Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/Base64Variant.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/Base64Variant.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/Base64Variant.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,593 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ +package com.fasterxml.jackson.core; + +import java.util.Arrays; + +import com.fasterxml.jackson.core.util.ByteArrayBuilder; + +/** + * Abstract base class used to define specific details of which + * variant of Base64 encoding/decoding is to be used. Although there is + * somewhat standard basic version (so-called "MIME Base64"), other variants + * exists, see Base64 Wikipedia entry for details. + * + * @author Tatu Saloranta + */ +public final class Base64Variant + implements java.io.Serializable +{ + private final static int INT_SPACE = 0x20; + + // We'll only serialize name + private static final long serialVersionUID = 1L; + + /** + * Placeholder used by "no padding" variant, to be used when a character + * value is needed. + */ + final static char PADDING_CHAR_NONE = '\0'; + + /** + * Marker used to denote ascii characters that do not correspond + * to a 6-bit value (in this variant), and is not used as a padding + * character. + */ + public final static int BASE64_VALUE_INVALID = -1; + + /** + * Marker used to denote ascii character (in decoding table) that + * is the padding character using this variant (if any). + */ + public final static int BASE64_VALUE_PADDING = -2; + + /* + /********************************************************** + /* Encoding/decoding tables + /********************************************************** + */ + + /** + * Decoding table used for base 64 decoding. + */ + private final transient int[] _asciiToBase64 = new int[128]; + + /** + * Encoding table used for base 64 decoding when output is done + * as characters. + */ + private final transient char[] _base64ToAsciiC = new char[64]; + + /** + * Alternative encoding table used for base 64 decoding when output is done + * as ascii bytes. + */ + private final transient byte[] _base64ToAsciiB = new byte[64]; + + /* + /********************************************************** + /* Other configuration + /********************************************************** + */ + + /** + * Symbolic name of variant; used for diagnostics/debugging. + *

+ * Note that this is the only non-transient field; used when reading + * back from serialized state + */ + protected final String _name; + + /** + * Whether this variant uses padding or not. + */ + protected final transient boolean _usesPadding; + + /** + * Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not). + */ + protected final transient char _paddingChar; + + /** + * Maximum number of encoded base64 characters to output during encoding + * before adding a linefeed, if line length is to be limited + * ({@link java.lang.Integer#MAX_VALUE} if not limited). + *

+ * Note: for some output modes (when writing attributes) linefeeds may + * need to be avoided, and this value ignored. + */ + protected final transient int _maxLineLength; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public Base64Variant(String name, String base64Alphabet, boolean usesPadding, char paddingChar, int maxLineLength) + { + _name = name; + _usesPadding = usesPadding; + _paddingChar = paddingChar; + _maxLineLength = maxLineLength; + + // Ok and then we need to create codec tables. + + // First the main encoding table: + int alphaLen = base64Alphabet.length(); + if (alphaLen != 64) { + throw new IllegalArgumentException("Base64Alphabet length must be exactly 64 (was "+alphaLen+")"); + } + + // And then secondary encoding table and decoding table: + base64Alphabet.getChars(0, alphaLen, _base64ToAsciiC, 0); + Arrays.fill(_asciiToBase64, BASE64_VALUE_INVALID); + for (int i = 0; i < alphaLen; ++i) { + char alpha = _base64ToAsciiC[i]; + _base64ToAsciiB[i] = (byte) alpha; + _asciiToBase64[alpha] = i; + } + + // Plus if we use padding, add that in too + if (usesPadding) { + _asciiToBase64[(int) paddingChar] = BASE64_VALUE_PADDING; + } + } + + /** + * "Copy constructor" that can be used when the base alphabet is identical + * to one used by another variant except for the maximum line length + * (and obviously, name). + */ + public Base64Variant(Base64Variant base, String name, int maxLineLength) + { + this(base, name, base._usesPadding, base._paddingChar, maxLineLength); + } + + /** + * "Copy constructor" that can be used when the base alphabet is identical + * to one used by another variant, but other details (padding, maximum + * line length) differ + */ + public Base64Variant(Base64Variant base, String name, boolean usesPadding, char paddingChar, int maxLineLength) + { + _name = name; + byte[] srcB = base._base64ToAsciiB; + System.arraycopy(srcB, 0, this._base64ToAsciiB, 0, srcB.length); + char[] srcC = base._base64ToAsciiC; + System.arraycopy(srcC, 0, this._base64ToAsciiC, 0, srcC.length); + int[] srcV = base._asciiToBase64; + System.arraycopy(srcV, 0, this._asciiToBase64, 0, srcV.length); + + _usesPadding = usesPadding; + _paddingChar = paddingChar; + _maxLineLength = maxLineLength; + } + + /* + /********************************************************** + /* Serializable overrides + /********************************************************** + */ + + /** + * Method used to "demote" deserialized instances back to + * canonical ones + */ + protected Object readResolve() { + return Base64Variants.valueOf(_name); + } + + /* + /********************************************************** + /* Public accessors + /********************************************************** + */ + + public String getName() { return _name; } + + public boolean usesPadding() { return _usesPadding; } + public boolean usesPaddingChar(char c) { return c == _paddingChar; } + public boolean usesPaddingChar(int ch) { return ch == (int) _paddingChar; } + public char getPaddingChar() { return _paddingChar; } + public byte getPaddingByte() { return (byte)_paddingChar; } + + public int getMaxLineLength() { return _maxLineLength; } + + /* + /********************************************************** + /* Decoding support + /********************************************************** + */ + + /** + * @return 6-bit decoded value, if valid character; + */ + public int decodeBase64Char(char c) + { + int ch = (int) c; + return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; + } + + public int decodeBase64Char(int ch) + { + return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; + } + + public int decodeBase64Byte(byte b) + { + int ch = (int) b; + return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; + } + + /* + /********************************************************** + /* Encoding support + /********************************************************** + */ + + public char encodeBase64BitsAsChar(int value) + { + /* Let's assume caller has done necessary checks; this + * method must be fast and inlinable + */ + return _base64ToAsciiC[value]; + } + + /** + * Method that encodes given right-aligned (LSB) 24-bit value + * into 4 base64 characters, stored in given result buffer. + */ + public int encodeBase64Chunk(int b24, char[] buffer, int ptr) + { + buffer[ptr++] = _base64ToAsciiC[(b24 >> 18) & 0x3F]; + buffer[ptr++] = _base64ToAsciiC[(b24 >> 12) & 0x3F]; + buffer[ptr++] = _base64ToAsciiC[(b24 >> 6) & 0x3F]; + buffer[ptr++] = _base64ToAsciiC[b24 & 0x3F]; + return ptr; + } + + public void encodeBase64Chunk(StringBuilder sb, int b24) + { + sb.append(_base64ToAsciiC[(b24 >> 18) & 0x3F]); + sb.append(_base64ToAsciiC[(b24 >> 12) & 0x3F]); + sb.append(_base64ToAsciiC[(b24 >> 6) & 0x3F]); + sb.append(_base64ToAsciiC[b24 & 0x3F]); + } + + /** + * Method that outputs partial chunk (which only encodes one + * or two bytes of data). Data given is still aligned same as if + * it as full data; that is, missing data is at the "right end" + * (LSB) of int. + * + * @param outputBytes Number of encoded bytes included (either 1 or 2) + */ + public int encodeBase64Partial(int bits, int outputBytes, char[] buffer, int outPtr) + { + buffer[outPtr++] = _base64ToAsciiC[(bits >> 18) & 0x3F]; + buffer[outPtr++] = _base64ToAsciiC[(bits >> 12) & 0x3F]; + if (_usesPadding) { + buffer[outPtr++] = (outputBytes == 2) ? + _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar; + buffer[outPtr++] = _paddingChar; + } else { + if (outputBytes == 2) { + buffer[outPtr++] = _base64ToAsciiC[(bits >> 6) & 0x3F]; + } + } + return outPtr; + } + + public void encodeBase64Partial(StringBuilder sb, int bits, int outputBytes) + { + sb.append(_base64ToAsciiC[(bits >> 18) & 0x3F]); + sb.append(_base64ToAsciiC[(bits >> 12) & 0x3F]); + if (_usesPadding) { + sb.append((outputBytes == 2) ? + _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar); + sb.append(_paddingChar); + } else { + if (outputBytes == 2) { + sb.append(_base64ToAsciiC[(bits >> 6) & 0x3F]); + } + } + } + + public byte encodeBase64BitsAsByte(int value) + { + // As with above, assuming it is 6-bit value + return _base64ToAsciiB[value]; + } + + /** + * Method that encodes given right-aligned (LSB) 24-bit value + * into 4 base64 bytes (ascii), stored in given result buffer. + */ + public int encodeBase64Chunk(int b24, byte[] buffer, int ptr) + { + buffer[ptr++] = _base64ToAsciiB[(b24 >> 18) & 0x3F]; + buffer[ptr++] = _base64ToAsciiB[(b24 >> 12) & 0x3F]; + buffer[ptr++] = _base64ToAsciiB[(b24 >> 6) & 0x3F]; + buffer[ptr++] = _base64ToAsciiB[b24 & 0x3F]; + return ptr; + } + + /** + * Method that outputs partial chunk (which only encodes one + * or two bytes of data). Data given is still aligned same as if + * it as full data; that is, missing data is at the "right end" + * (LSB) of int. + * + * @param outputBytes Number of encoded bytes included (either 1 or 2) + */ + public int encodeBase64Partial(int bits, int outputBytes, byte[] buffer, int outPtr) + { + buffer[outPtr++] = _base64ToAsciiB[(bits >> 18) & 0x3F]; + buffer[outPtr++] = _base64ToAsciiB[(bits >> 12) & 0x3F]; + if (_usesPadding) { + byte pb = (byte) _paddingChar; + buffer[outPtr++] = (outputBytes == 2) ? + _base64ToAsciiB[(bits >> 6) & 0x3F] : pb; + buffer[outPtr++] = pb; + } else { + if (outputBytes == 2) { + buffer[outPtr++] = _base64ToAsciiB[(bits >> 6) & 0x3F]; + } + } + return outPtr; + } + + /* + /********************************************************** + /* Convenience 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. + * + * @param input Byte array to encode + * @param addQuotes Whether to surround resulting value in double quotes or not + */ + public String encode(byte[] input, boolean addQuotes) + { + int inputEnd = input.length; + StringBuilder sb; + { + // let's approximate... 33% overhead, ~= 3/8 (0.375) + int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3); + sb = new StringBuilder(outputLen); + } + if (addQuotes) { + sb.append('"'); + } + + int chunksBeforeLF = getMaxLineLength() >> 2; + + // Ok, first we loop through all full triplets of data: + int inputPtr = 0; + int safeInputEnd = inputEnd-3; // to get only full triplets + + while (inputPtr <= safeInputEnd) { + // First, mash 3 bytes into lsb of 32-bit int + int b24 = ((int) input[inputPtr++]) << 8; + b24 |= ((int) input[inputPtr++]) & 0xFF; + b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); + encodeBase64Chunk(sb, b24); + if (--chunksBeforeLF <= 0) { + // note: must quote in JSON value, so not really useful... + sb.append('\\'); + sb.append('n'); + chunksBeforeLF = getMaxLineLength() >> 2; + } + } + + // And then we may have 1 or 2 leftover bytes to encode + int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 + if (inputLeft > 0) { // yes, but do we have room for output? + int b24 = ((int) input[inputPtr++]) << 16; + if (inputLeft == 2) { + b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; + } + encodeBase64Partial(sb, b24, inputLeft); + } + + if (addQuotes) { + sb.append('"'); + } + return sb.toString(); + } + + /** + * Convenience method for decoding contents of a Base64-encoded String, + * using this variant's settings. + * + * @param input + * + * @since 2.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.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 (ptr < len) { + // first, we'll skip preceding white space, if any + char ch; + do { + ch = str.charAt(ptr++); + if (ptr >= len) { + break main_loop; + } + } while (ch <= INT_SPACE); + int bits = 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("Unexpected end-of-String in base64 content"); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/Base64Variants.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/Base64Variants.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/Base64Variants.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,111 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ +package com.fasterxml.jackson.core; + +/** + * Container for commonly used Base64 variants: + *

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

+ * See wikipedia Base64 entry for details. + *

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

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

+ * Since there is little commonality between schemas for different data formats, + * this interface does not define much meaningful functionality for accessing + * schema details; rather, specific parser and generator implementations need + * to cast to schema implementations they use. This marker interface is mostly + * used for tagging "some kind of schema" -- instead of passing opaque + * {@link java.lang.Object} -- for documentation purposes. + */ +public interface FormatSchema +{ + /** + * Method that can be used to get an identifier that can be used for diagnostics + * purposes, to indicate what kind of data format this schema is used for: typically + * it is a short name of format itself, but it can also contain additional information + * in cases where data format supports multiple types of schemas. + */ + String getSchemaType(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonEncoding.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonEncoding.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonEncoding.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,57 @@ +/* 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) + ; + + protected final String _javaName; + + protected final boolean _bigEndian; + + protected 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; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1479 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ +package com.fasterxml.jackson.core; + +import java.io.*; +import java.lang.ref.SoftReference; +import java.net.URL; + +import com.fasterxml.jackson.core.format.InputAccessor; +import com.fasterxml.jackson.core.format.MatchStrength; +import com.fasterxml.jackson.core.io.*; +import com.fasterxml.jackson.core.json.*; +import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; +import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; +import com.fasterxml.jackson.core.util.BufferRecycler; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; + +/** + * The main factory class of Jackson package, used to configure and + * construct reader (aka parser, {@link JsonParser}) + * and writer (aka generator, {@link JsonGenerator}) + * instances. + *

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

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

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

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

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

+ * This setting is enabled by default. + * + * @since 2.4 + */ + FAIL_ON_SYMBOL_HASH_OVERFLOW(true), + + /** + * Feature that determines whether we will use {@link BufferRecycler} with + * {@link ThreadLocal} and {@link SoftReference}, for efficient reuse of + * underlying input/output buffers. + * This usually makes sense on normal J2SE/J2EE server-side processing; + * but may not make sense on platforms where {@link SoftReference} handling + * is broken (like Android), or if there are retention issues due to + * {@link ThreadLocal} (see + * Issue #189 + * for a possible case) + * + * @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(); + + private final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; + + /* + /********************************************************** + /* Buffer, symbol table management + /********************************************************** + */ + + /** + * This ThreadLocal contains a {@link java.lang.ref.SoftReference} + * to a {@link BufferRecycler} used to provide a low-cost + * buffer recycling between reader and writer instances. + */ + final protected static ThreadLocal> _recyclerRef + = new ThreadLocal>(); + + /** + * Each factory comes equipped with a shared root symbol table. + * It should not be linked back to the original blueprint, to + * avoid contents from leaking between factories. + */ + protected final transient CharsToNameCanonicalizer _rootCharSymbols = CharsToNameCanonicalizer.createRoot(); + + /** + * Alternative to the basic symbol table, some stream-based + * parsers use different name canonicalization method. + *

+ * TODO: should clean up this; looks messy having 2 alternatives + * with not very clear differences. + * + * @since 2.6.0 + */ + protected final transient ByteQuadsCanonicalizer _byteSymbolCanonicalizer = ByteQuadsCanonicalizer.createRoot(); + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Object that implements conversion functionality between + * Java objects and JSON content. For base JsonFactory implementation + * usually not set by default, but can be explicitly set. + * Sub-classes (like @link org.codehaus.jackson.map.MappingJsonFactory} + * usually provide an implementation. + */ + protected ObjectCodec _objectCodec; + + /** + * Currently enabled factory features. + */ + protected int _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS; + + /** + * Currently enabled parser features. + */ + protected int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS; + + /** + * Currently enabled generator features. + */ + protected int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; + + /** + * Definition of custom character escapes to use for generators created + * by this factory, if any. If null, standard data format specific + * escapes are used. + */ + protected CharacterEscapes _characterEscapes; + + /** + * Optional helper object that may decorate input sources, to do + * additional processing on input during parsing. + */ + protected InputDecorator _inputDecorator; + + /** + * Optional helper object that may decorate output object, to do + * additional processing on output during content generation. + */ + protected OutputDecorator _outputDecorator; + + /** + * Separator used between root-level values, if any; null indicates + * "do not add separator". + * Default separator is a single space character. + * + * @since 2.1 + */ + protected SerializableString _rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + /** + * Default constructor used to create factory instances. + * Creation of a factory instance is a light-weight operation, + * but it is still a good idea to reuse limited number of + * factory instances (and quite often just a single instance): + * factories are used as context for storing some reused + * processing objects (such as symbol tables parsers use) + * and this reuse only works within context of a single + * factory instance. + */ + public JsonFactory() { this(null); } + + public JsonFactory(ObjectCodec oc) { _objectCodec = oc; } + + /** + * Constructor used when copy()ing a factory instance. + * + * @since 2.2.1 + */ + protected JsonFactory(JsonFactory src, ObjectCodec codec) + { + _objectCodec = null; + _factoryFeatures = src._factoryFeatures; + _parserFeatures = src._parserFeatures; + _generatorFeatures = src._generatorFeatures; + _characterEscapes = src._characterEscapes; + _inputDecorator = src._inputDecorator; + _outputDecorator = src._outputDecorator; + _rootValueSeparator = src._rootValueSeparator; + + /* 27-Apr-2013, tatu: How about symbol table; should we try to + * reuse shared symbol tables? Could be more efficient that way; + * although can slightly add to concurrency overhead. + */ + } + + /** + * Method for constructing a new {@link JsonFactory} that has + * the same settings as this instance, but is otherwise + * independent (i.e. nothing is actually shared, symbol tables + * are separate). + * Note that {@link ObjectCodec} reference is not copied but is + * set to null; caller typically needs to set it after calling + * this method. Reason for this is that the codec is used for + * callbacks, and assumption is that there is strict 1-to-1 + * mapping between codec, factory. Caller has to, then, explicitly + * set codec after making the copy. + * + * @since 2.1 + */ + public JsonFactory copy() + { + _checkInvalidCopy(JsonFactory.class); + // as per above, do clear ObjectCodec + return new JsonFactory(this, null); + } + + /** + * @since 2.1 + * @param exp + */ + protected void _checkInvalidCopy(Class exp) + { + if (getClass() != exp) { + throw new IllegalStateException("Failed copy(): "+getClass().getName() + +" (version: "+version()+") does not override copy(); it has to"); + } + } + + /* + /********************************************************** + /* Serializable overrides + /********************************************************** + */ + + /** + * Method that we need to override to actually make restoration go + * through constructors etc. + * Also: must be overridden by sub-classes as well. + */ + protected Object readResolve() { + return new JsonFactory(this, _objectCodec); + } + + /* + /********************************************************** + /* Capability introspection + /********************************************************** + */ + + /** + * Introspection method that higher-level functionality may call + * to see whether underlying data format requires a stable ordering + * of object properties or not. + * This is usually used for determining + * whether to force a stable ordering (like alphabetic ordering by name) + * if no ordering if explicitly specified. + *

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

+ * Default implementation returns false as JSON does not + * support native access: all binary content must use Base64 encoding. + * Most binary formats (like Smile and Avro) support native binary content. + * + * @since 2.3 + */ + 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; } + + /** + * 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 Class getFormatReadFeatureType() { + return null; + } + + /** + * 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 Class getFormatWriteFeatureType() { + return null; + } + /* + /********************************************************** + /* Format detection functionality + /********************************************************** + */ + + /** + * Method that can be used to quickly check whether given schema + * is something that parsers and/or generators constructed by this + * factory could use. Note that this means possible use, at the level + * of data format (i.e. schema is for same data format as parsers and + * generators this factory constructs); individual schema instances + * may have further usage restrictions. + * + * @since 2.1 + */ + public boolean canUseSchema(FormatSchema schema) { + String ourFormat = getFormatName(); + return (ourFormat != null) && ourFormat.equals(schema.getSchemaType()); + } + + /** + * Method that returns short textual id identifying format + * this factory supports. + *

+ * Note: sub-classes should override this method; default + * implementation will return null for all sub-classes + */ + public String getFormatName() + { + /* Somewhat nasty check: since we can't make this abstract + * (due to backwards compatibility concerns), need to prevent + * format name "leakage" + */ + if (getClass() == JsonFactory.class) { + return FORMAT_NAME_JSON; + } + return null; + } + + /** + * 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) + */ + public final JsonFactory configure(JsonFactory.Feature f, boolean state) { + return state ? enable(f) : disable(f); + } + + /** + * Method for enabling specified parser feature + * (check {@link JsonFactory.Feature} for list of features) + */ + public JsonFactory enable(JsonFactory.Feature f) { + _factoryFeatures |= f.getMask(); + return this; + } + + /** + * Method for disabling specified parser features + * (check {@link JsonFactory.Feature} for list of features) + */ + public JsonFactory disable(JsonFactory.Feature f) { + _factoryFeatures &= ~f.getMask(); + return this; + } + + /** + * Checked whether specified parser feature is enabled. + */ + public final boolean isEnabled(JsonFactory.Feature f) { + return (_factoryFeatures & f.getMask()) != 0; + } + + /* + /********************************************************** + /* Configuration, parser configuration + /********************************************************** + */ + + /** + * Method for enabling or disabling specified parser feature + * (check {@link JsonParser.Feature} for list of features) + */ + public final JsonFactory configure(JsonParser.Feature f, boolean state) { + return state ? enable(f) : disable(f); + } + + /** + * Method for enabling specified parser feature + * (check {@link JsonParser.Feature} for list of features) + */ + public JsonFactory enable(JsonParser.Feature f) { + _parserFeatures |= f.getMask(); + return this; + } + + /** + * Method for disabling specified parser features + * (check {@link JsonParser.Feature} for list of features) + */ + public JsonFactory disable(JsonParser.Feature f) { + _parserFeatures &= ~f.getMask(); + return this; + } + + /** + * Checked whether specified parser feature is enabled. + */ + public final boolean isEnabled(JsonParser.Feature f) { + return (_parserFeatures & f.getMask()) != 0; + } + + /** + * Method for getting currently configured input decorator (if any; + * there is no default decorator). + */ + public InputDecorator getInputDecorator() { + return _inputDecorator; + } + + /** + * Method for overriding currently configured input decorator + */ + public JsonFactory setInputDecorator(InputDecorator d) { + _inputDecorator = d; + return this; + } + + /* + /********************************************************** + /* Configuration, generator settings + /********************************************************** + */ + + /** + * Method for enabling or disabling specified generator feature + * (check {@link JsonGenerator.Feature} for list of features) + */ + public final JsonFactory configure(JsonGenerator.Feature f, boolean state) { + return state ? enable(f) : disable(f); + } + + + /** + * Method for enabling specified generator features + * (check {@link JsonGenerator.Feature} for list of features) + */ + public JsonFactory enable(JsonGenerator.Feature f) { + _generatorFeatures |= f.getMask(); + return this; + } + + /** + * Method for disabling specified generator feature + * (check {@link JsonGenerator.Feature} for list of features) + */ + public JsonFactory disable(JsonGenerator.Feature f) { + _generatorFeatures &= ~f.getMask(); + return this; + } + + /** + * Check whether specified generator feature is enabled. + */ + public final boolean isEnabled(JsonGenerator.Feature f) { + return (_generatorFeatures & f.getMask()) != 0; + } + + /** + * Method for accessing custom escapes factory uses for {@link JsonGenerator}s + * it creates. + */ + public CharacterEscapes getCharacterEscapes() { return _characterEscapes; } + + /** + * Method for defining custom escapes factory uses for {@link JsonGenerator}s + * it creates. + */ + public JsonFactory setCharacterEscapes(CharacterEscapes esc) { + _characterEscapes = esc; + return this; + } + + /** + * Method for getting currently configured output decorator (if any; + * there is no default decorator). + */ + public OutputDecorator getOutputDecorator() { + return _outputDecorator; + } + + /** + * Method for overriding currently configured output decorator + */ + public JsonFactory setOutputDecorator(OutputDecorator d) { + _outputDecorator = d; + return this; + } + + /** + * Method that allows overriding String used for separating root-level + * JSON values (default is single space character) + * + * @param sep Separator to use, if any; null means that no separator is + * automatically added + * + * @since 2.1 + */ + public JsonFactory setRootValueSeparator(String sep) { + _rootValueSeparator = (sep == null) ? null : new SerializedString(sep); + return this; + } + + /** + * @since 2.1 + */ + public String getRootValueSeparator() { + return (_rootValueSeparator == null) ? null : _rootValueSeparator.getValue(); + } + + /* + /********************************************************** + /* Configuration, other + /********************************************************** + */ + + /** + * Method for associating a {@link ObjectCodec} (typically + * a com.fasterxml.jackson.databind.ObjectMapper) + * with this factory (and more importantly, parsers and generators + * it constructs). This is needed to use data-binding methods + * of {@link JsonParser} and {@link JsonGenerator} instances. + */ + public JsonFactory setCodec(ObjectCodec oc) { + _objectCodec = oc; + return this; + } + + public ObjectCodec getCodec() { return _objectCodec; } + + /* + /********************************************************** + /* Parser factories (new ones, as per [Issue-25]) + /********************************************************** + */ + + /** + * Method for constructing JSON parser instance to parse + * contents of specified file. + * + *

+ * Encoding is auto-detected from contents according to JSON + * specification recommended mechanism. 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 + */ + 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 + */ + 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.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 + * + * @since 2.1 + */ + 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.JsonParser.Feature#AUTO_CLOSE_SOURCE} + * is enabled. + * + * @param r Reader to use for reading JSON content to parse + * + * @since 2.1 + */ + public JsonParser createParser(Reader r) throws IOException, JsonParseException { + // false -> we do NOT own Reader (did not create it) + IOContext ctxt = _createContext(r, false); + return _createParser(_decorate(r, ctxt), ctxt); + } + + /** + * Method for constructing parser for parsing + * the contents of given byte array. + * + * @since 2.1 + */ + public JsonParser createParser(byte[] data) throws IOException, JsonParseException { + IOContext ctxt = _createContext(data, true); + if (_inputDecorator != null) { + InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); + if (in != null) { + return _createParser(in, ctxt); + } + } + return _createParser(data, 0, data.length, ctxt); + } + + /** + * Method for constructing parser for parsing + * the contents of given byte array. + * + * @param data Buffer that contains data to parse + * @param offset Offset of the first data byte within buffer + * @param len Length of contents to parse within buffer + * + * @since 2.1 + */ + public JsonParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException { + IOContext ctxt = _createContext(data, true); + // [JACKSON-512]: allow wrapping with InputDecorator + if (_inputDecorator != null) { + InputStream in = _inputDecorator.decorate(ctxt, data, offset, len); + if (in != null) { + return _createParser(in, ctxt); + } + } + return _createParser(data, offset, len, ctxt); + } + + /** + * Method for constructing parser for parsing + * contents of given String. + * + * @since 2.1 + */ + public JsonParser createParser(String content) throws IOException, JsonParseException { + 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 + */ + 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 + */ + 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); + } + + /* + /********************************************************** + /* Parser factories (old ones, pre-2.2) + /********************************************************** + */ + + /** + * 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); + } + + /* + /********************************************************** + /* Generator factories, new (as per [Issue-25] + /********************************************************** + */ + + /** + * Method for constructing JSON generator for writing JSON content + * using specified output stream. + * Encoding to use must be specified, and needs to be one of available + * types (as per JSON specification). + *

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

+ * Note: there are formats that use fixed encoding (like most binary data formats) + * and that ignore passed in encoding. + * + * @param out OutputStream to use for writing JSON content + * @param enc Character encoding to use + * + * @since 2.1 + */ + public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) + throws IOException + { + // false -> we won't manage the stream unless explicitly directed to + IOContext ctxt = _createContext(out, false); + ctxt.setEncoding(enc); + if (enc == JsonEncoding.UTF8) { + 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 + */ + 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 + */ + 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 + */ + 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); + } + + /* + /********************************************************** + /* Generator factories, old (pre-2.2) + /********************************************************** + */ + + /** + * Method for constructing JSON generator for writing JSON content + * using specified output stream. + * Encoding to use must be specified, and needs to be one of available + * types (as per JSON specification). + *

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

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

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

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

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

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

+ * This method is specifically designed to remain + * compatible between minor versions so that sub-classes can count + * on it being called as expected. That is, it is part of official + * interface from sub-class perspective, although not a public + * method available to users of factory implementations. + */ + protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException + { + return new ByteSourceJsonBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures, + _objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures); + } + + /* + /********************************************************** + /* Factory methods used by factory for creating generator instances, + /* overridable by sub-classes + /********************************************************** + */ + + /** + * Overridable factory method that actually instantiates generator for + * given {@link Writer} and context object. + *

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

+ * This method is specifically designed to remain + * compatible between minor versions so that sub-classes can count + * on it being called as expected. That is, it is part of official + * interface from sub-class perspective, although not a public + * method available to users of factory implementations. + */ + protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { + UTF8JsonGenerator gen = new UTF8JsonGenerator(ctxt, + _generatorFeatures, _objectCodec, out); + if (_characterEscapes != null) { + gen.setCharacterEscapes(_characterEscapes); + } + SerializableString rootSep = _rootValueSeparator; + if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) { + gen.setRootValueSeparator(rootSep); + } + return gen; + } + + 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.4 + */ + protected final OutputStream _decorate(OutputStream out, IOContext ctxt) throws IOException { + if (_outputDecorator != null) { + OutputStream out2 = _outputDecorator.decorate(ctxt, out); + if (out2 != null) { + return out2; + } + } + return out; + } + + /** + * @since 2.4 + */ + protected final Writer _decorate(Writer out, IOContext ctxt) throws IOException { + if (_outputDecorator != null) { + Writer out2 = _outputDecorator.decorate(ctxt, out); + if (out2 != null) { + return out2; + } + } + return out; + } + + /* + /********************************************************** + /* Internal factory methods, other + /********************************************************** + */ + + /** + * Method used by factory to create buffer recycler instances + * for parsers and generators. + *

+ * Note: only public to give access for ObjectMapper + */ + public BufferRecycler _getBufferRecycler() + { + BufferRecycler br; + + /* 23-Apr-2015, tatu: Let's allow disabling of buffer recycling + * scheme, for cases where it is considered harmful (possibly + * on Android, for example) + */ + if (isEnabled(Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)) { + SoftReference ref = _recyclerRef.get(); + br = (ref == null) ? null : ref.get(); + + if (br == null) { + br = new BufferRecycler(); + _recyclerRef.set(new SoftReference(br)); + } + } else { + br = new BufferRecycler(); + } + return br; + } + + /** + * Overridable factory method that actually instantiates desired + * context object. + */ + protected IOContext _createContext(Object srcRef, boolean resourceManaged) { + return new IOContext(_getBufferRecycler(), srcRef, resourceManaged); + } + + /** + * Helper methods used for constructing an optimal stream for + * parsers to use, when input is to be read from an URL. + * This helps when reading file content via URL. + */ + protected InputStream _optimizedStreamFromURL(URL url) throws IOException { + if ("file".equals(url.getProtocol())) { + /* Can not do this if the path refers + * to a network drive on windows. This fixes the problem; + * might not be needed on all platforms (NFS?), but should not + * matter a lot: performance penalty of extra wrapping is more + * relevant when accessing local file system. + */ + String host = url.getHost(); + if (host == null || host.length() == 0) { + // [Issue#48]: Let's try to avoid probs with URL encoded stuff + String path = url.getPath(); + if (path.indexOf('%') < 0) { + return new FileInputStream(url.getPath()); + + } + // otherwise, let's fall through and let URL decoder do its magic + } + } + return url.openStream(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonGenerationException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonGenerationException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonGenerationException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,73 @@ +/* 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; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonGenerator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonGenerator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonGenerator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1754 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ +package com.fasterxml.jackson.core; + +import static com.fasterxml.jackson.core.JsonTokenId.*; + +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.util.VersionUtil; + +/** + * Base class that defines public API for writing JSON content. + * Instances are created using factory methods of + * a {@link JsonFactory} instance. + * + * @author Tatu Saloranta + */ +public abstract class JsonGenerator + implements Closeable, Flushable, Versioned +{ + /** + * Enumeration that defines all togglable features for generators. + */ + public enum Feature { + // // Low-level I/O / content features + + /** + * Feature that determines whether generator will automatically + * close underlying output target that is NOT owned by the + * generator. + * If disabled, calling application has to separately + * close the underlying {@link OutputStream} and {@link Writer} + * instances used to create the generator. If enabled, generator + * will handle closing, as long as generator itself gets closed: + * this happens when end-of-input is encountered, or generator + * is closed by a call to {@link JsonGenerator#close}. + *

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

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

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

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

+ * Feature is enabled by default. + */ + QUOTE_NON_NUMERIC_NUMBERS(true), + + /** + * Feature that forces all Java numbers to be written as JSON strings. + * Default state is 'false', meaning that Java numbers are to + * be serialized using basic numeric serialization (as JSON + * numbers, integral or floating point). If enabled, all such + * numeric values are instead written out as JSON Strings. + *

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

+ * Feature is disabled by default. + */ + WRITE_NUMBERS_AS_STRINGS(false), + + /** + * Feature that determines whether {@link java.math.BigDecimal} entries are + * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent + * values to be written using scientific notation. + *

+ * 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), + + /** + * 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. + */ + ESCAPE_NON_ASCII(false), + +// 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. + * + * @since Xxx + */ +// ESCAPE_UTF8_SURROGATES(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); + + /** + * 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 + */ + @Deprecated + public abstract JsonGenerator setFeatureMask(int values); + + /** + * Bulk set method for (re)setting states of features specified by mask. + * Functionally equivalent to + * + * int oldState = getFeatureMask(); + * int newState = (oldState & ~mask) | (values & mask); + * setFeatureMask(newState); + * + * but preferred as this lets caller more efficiently specify actual changes made. + * + * @param values Bit mask of set/clear state for features to change + * @param mask Bit mask of features to change + * + * @since 2.6 + */ + public JsonGenerator overrideStdFeatures(int values, int mask) { + int oldState = getFeatureMask(); + int newState = (oldState & ~mask) | (values & mask); + return setFeatureMask(newState); + } + + /** + * Bulk access method for getting state of all {@link FormatFeature}s, format-specific + * on/off configuration settings. + * + * @return Bit mask that defines current states of all standard {@link FormatFeature}s. + * + * @since 2.6 + */ + public int getFormatFeatures() { + return 0; + } + + /** + * Bulk set method for (re)setting states of {@link FormatFeature}s, + * by specifying values (set / clear) along with a mask, to determine + * which features to change, if any. + *

+ * Default implementation will simply throw an exception to indicate that + * the generator implementation does not support any {@link FormatFeature}s. + * + * @param values Bit mask of set/clear state for features to change + * @param mask Bit mask of features to change + * + * @since 2.6 + */ + public JsonGenerator overrideFormatFeatures(int values, int mask) { + throw new IllegalArgumentException("No FormatFeatures defined for generator of type "+getClass().getName()); + /* + int oldState = getFeatureMask(); + int newState = (oldState & ~mask) | (values & mask); + return setFeatureMask(newState); + */ + } + + /* + /********************************************************** + /* 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 -1 to indicate no additional escaping is performed. + */ + public int getHighestEscapedChar() { return 0; } + + /** + * Method for accessing custom escapes factory uses for {@link JsonGenerator}s + * it creates. + */ + public CharacterEscapes getCharacterEscapes() { return null; } + + /** + * Method for defining custom escapes factory uses for {@link JsonGenerator}s + * it creates. + *

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

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

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

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

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

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

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

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

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

+ * Marker can be written if the innermost structured type + * is Object, and the last written event was either a + * complete value, or START-OBJECT marker (see JSON specification + * for more details). + */ + public abstract void writeEndObject() throws IOException; + + /** + * 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; + + /* + /********************************************************** + /* 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. + */ + public abstract void writeString(char[] text, int offset, int len) throws IOException; + + /** + * Method similar to {@link #writeString(String)}, but that takes + * {@link SerializableString} which can make this potentially + * more efficient to call as generator may be able to reuse + * quoted and/or encoded representation. + *

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

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

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

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

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

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

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

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

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

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

+ * Note: because of lack of type safety, some generator + * implementations may not be able to implement this + * method. For example, if a binary JSON format is used, + * it may require type information for encoding; similarly + * for generator-wrappers around Java objects or JSON nodes. + * If implementation does not implement this method, + * it needs to throw {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException If underlying data format does not + * support numbers serialized textually AND if generator is not allowed + * to just output a String instead (Schema-based formats may require actual + * number, for example) + */ + public abstract void writeNumber(String encodedValue) throws IOException; + + /** + * 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; + + /* + /********************************************************** + /* 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); + } + + /* + /********************************************************** + /* Public API, write methods, serializing Java objects + /********************************************************** + */ + + /** + * Method for writing given Java object (POJO) as Json. + * Exactly how the object gets written depends on object + * in question (ad on codec, its configuration); for most + * beans it will result in JSON Object, but for others JSON + * Array, or String or numeric value (and for nulls, JSON + * null literal. + * NOTE: generator must have its object codec + * set to non-null value; for generators created by a mapping + * factory this is the case, for others not. + */ + public abstract void writeObject(Object pojo) throws IOException; + + /** + * Method for writing given JSON tree (expressed as a tree + * where given JsonNode is the root) using this generator. + * This will generally just call + * {@link #writeObject} with given node, but is added + * for convenience and to make code more explicit in cases + * where it deals specifically with trees. + */ + public abstract void writeTree(TreeNode rootNode) throws IOException; + + /* + /********************************************************** + /* Public API, convenience field write methods + /********************************************************** + */ + + /** + * Convenience method for outputting a field entry ("member") + * that has a String value. Equivalent to: + *

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

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

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

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

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

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

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

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

+ * Calling this method will not advance the given + * parser, although it may cause parser to internally process + * more data (if it lazy loads contents of value events, for example) + */ + public void copyCurrentEvent(JsonParser p) throws IOException + { + JsonToken t = p.getCurrentToken(); + // sanity check; what to do? + if (t == null) { + _reportError("No current event to copy"); + } + switch (t.id()) { + case ID_NOT_AVAILABLE: + _reportError("No current event to copy"); + 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: + _throwInternal(); + } + } + + /** + * 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.getCurrentToken(); + if (t == null) { + _reportError("No current event to copy"); + } + // Let's handle field-name separately first + int id = t.id(); + if (id == ID_FIELD_NAME) { + writeFieldName(p.getCurrentName()); + t = p.nextToken(); + id = t.id(); + // fall-through to copy the associated value + } + switch (id) { + case ID_START_OBJECT: + writeStartObject(); + while (p.nextToken() != JsonToken.END_OBJECT) { + copyCurrentStructure(p); + } + writeEndObject(); + break; + case ID_START_ARRAY: + writeStartArray(); + while (p.nextToken() != JsonToken.END_ARRAY) { + copyCurrentStructure(p); + } + writeEndArray(); + break; + default: + copyCurrentEvent(p); + } + } + + /* + /********************************************************** + /* 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()); + } + + /** + * 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()+")"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonLocation.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonLocation.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonLocation.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,139 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +/** + * Object that encapsulates Location information used for reporting + * parsing (or potentially generation) errors, as well as current location + * within input streams. + */ +public class JsonLocation + implements java.io.Serializable // as per [JACKSON-168] +{ + private static final long serialVersionUID = 1L; + + /** + * Shared immutable "N/A location" that can be returned to indicate + * that no location information is available + */ + public final static JsonLocation NA = new JsonLocation("N/A", -1L, -1L, -1, -1); + + final long _totalBytes; + final long _totalChars; + + final int _lineNr; + final int _columnNr; + + /** + * Displayable description for input source: file path, URL. + *

+ * NOTE: transient since 2.2 so that Location itself is Serializable. + */ + final transient Object _sourceRef; + + public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr) + { + /* Unfortunately, none of legal encodings are straight single-byte + * encodings. Could determine offset for UTF-16/UTF-32, but the + * most important one is UTF-8... + * so for now, we'll just not report any real byte count + */ + this(srcRef, -1L, totalChars, lineNr, colNr); + } + + public JsonLocation(Object sourceRef, long totalBytes, long totalChars, + int lineNr, int columnNr) + { + _sourceRef = sourceRef; + _totalBytes = totalBytes; + _totalChars = totalChars; + _lineNr = lineNr; + _columnNr = columnNr; + } + + /** + * Reference to the original resource being read, if one available. + * For example, when a parser has been constructed by passing + * a {@link java.io.File} instance, this method would return + * that File. Will return null if no such reference is available, + * for example when {@link java.io.InputStream} was used to + * construct the parser instance. + */ + public Object getSourceRef() { return _sourceRef; } + + /** + * @return Line number of the location (1-based) + */ + public int getLineNr() { return _lineNr; } + + /** + * @return Column number of the location (1-based) + */ + public int getColumnNr() { return _columnNr; } + + /** + * @return Character offset within underlying stream, reader or writer, + * if available; -1 if not. + */ + public long getCharOffset() { return _totalChars; } + + /** + * @return Byte offset within underlying stream, reader or writer, + * if available; -1 if not. + */ + public long getByteOffset() + { + return _totalBytes; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("[Source: "); + if (_sourceRef == null) { + sb.append("UNKNOWN"); + } else { + sb.append(_sourceRef.toString()); + } + sb.append("; line: "); + sb.append(_lineNr); + sb.append(", column: "); + sb.append(_columnNr); + sb.append(']'); + return sb.toString(); + } + + @Override + public int hashCode() + { + int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode(); + hash ^= _lineNr; + hash += _columnNr; + hash ^= (int) _totalChars; + hash += (int) _totalBytes; + return hash; + } + + @Override + public boolean equals(Object other) + { + if (other == this) return true; + if (other == null) return false; + if (!(other instanceof JsonLocation)) return false; + JsonLocation otherLoc = (JsonLocation) other; + + if (_sourceRef == null) { + if (otherLoc._sourceRef != null) return false; + } else if (!_sourceRef.equals(otherLoc._sourceRef)) return false; + + return (_lineNr == otherLoc._lineNr) + && (_columnNr == otherLoc._columnNr) + && (_totalChars == otherLoc._totalChars) + && (getByteOffset() == otherLoc.getByteOffset()) + ; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonParseException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonParseException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonParseException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,80 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +/** + * Exception type for parsing problems, used when non-well-formed content + * (content that does not conform to JSON syntax as per specification) + * is encountered. + */ +public class JsonParseException extends JsonProcessingException { + private static final long serialVersionUID = 2L; // 2.7 + + // since 2.7.4 + protected transient JsonParser _processor; + + @Deprecated // since 2.7 + public JsonParseException(String msg, JsonLocation loc) { + super(msg, loc); + } + + @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(msg, (p == null) ? null : p.getCurrentLocation()); + _processor = p; + } + + /** + * @since 2.7 + */ + public JsonParseException(JsonParser p, String msg, Throwable root) { + super(msg, (p == null) ? null : p.getCurrentLocation(), root); + _processor = p; + } + + /** + * @since 2.7 + */ + public JsonParseException(JsonParser p, String msg, JsonLocation loc) { + super(msg, loc); + _processor = p; + } + + /** + * @since 2.7 + */ + public JsonParseException(JsonParser p, String msg, JsonLocation loc, Throwable root) { + super(msg, loc, root); + _processor = p; + } + + /** + * Fluent method that may be used to assign originating {@link JsonParser}, + * to be accessed using {@link #getProcessor()}. + * + * @since 2.7 + */ + public JsonParseException withParser(JsonParser p) { + _processor = p; + return this; + } + + @Override + public JsonParser getProcessor() { + return _processor; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonParser.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonParser.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonParser.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1598 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +import java.io.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Iterator; + +import com.fasterxml.jackson.core.type.TypeReference; + +/** + * Base class that defines public API for reading JSON content. + * Instances are created using factory methods of + * a {@link JsonFactory} instance. + * + * @author Tatu Saloranta + */ +public abstract class JsonParser + implements Closeable, Versioned +{ + private final static int MIN_BYTE_I = (int) Byte.MIN_VALUE; + // as per [JACKSON-804], allow range up to and including 255 + private final static int MAX_BYTE_I = (int) 255; + + private final static int MIN_SHORT_I = (int) Short.MIN_VALUE; + private final static int MAX_SHORT_I = (int) Short.MAX_VALUE; + + /** + * Enumeration of possible "native" (optimal) types that can be + * used for numbers. + */ + public enum NumberType { + INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL + }; + + /** + * Enumeration that defines all on/off features for parsers. + */ + public enum Feature { + + // // // Low-level I/O handling features: + + /** + * Feature that determines whether parser will automatically + * close underlying input source that is NOT owned by the + * parser. If disabled, calling application has to separately + * close the underlying {@link InputStream} and {@link Reader} + * instances used to create the parser. If enabled, parser + * will handle closing, as long as parser itself gets closed: + * this happens when end-of-input is encountered, or parser + * is closed by a call to {@link JsonParser#close}. + *

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

+ * Since JSON specification does not mention comments as legal + * construct, + * this is a non-standard feature; however, in the wild + * this is extensively used. As such, feature is + * disabled by default for parsers and must be + * explicitly enabled. + */ + ALLOW_COMMENTS(false), + + /** + * Feature that determines whether parser will allow use + * of 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), + + /** + * Feature that determines whether parser will allow use + * of unquoted field names (which is allowed by Javascript, + * but not by JSON specification). + *

+ * Since JSON specification requires use of double quotes for + * field names, + * this is a non-standard feature, and as such disabled by default. + */ + ALLOW_UNQUOTED_FIELD_NAMES(false), + + /** + * Feature that determines whether parser will allow use + * of single quotes (apostrophe, character '\'') for + * quoting Strings (names and String values). If so, + * this is in addition to other 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. + */ + ALLOW_SINGLE_QUOTES(false), + + /** + * Feature that determines whether parser will allow + * JSON Strings to contain unquoted control characters + * (ASCII characters with value less than 32, including + * tab and line feed characters) or not. + * If feature is set false, an exception is thrown if such a + * character is encountered. + *

+ * Since JSON specification requires quoting for all control characters, + * this is a non-standard feature, and as such disabled by default. + */ + ALLOW_UNQUOTED_CONTROL_CHARS(false), + + /** + * Feature that can be enabled to accept quoting of all character + * using backslash 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. + */ + ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false), + + /** + * Feature that determines whether parser will allow + * JSON integral numbers to start with additional (ignorable) + * zeroes (like: 000001). If enabled, no exception is thrown, and extra + * nulls are silently ignored (and not included in textual representation + * exposed via {@link JsonParser#getText}). + *

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

+ *

+ * Since JSON specification does not allow use of such values, + * this is a non-standard feature, and as such disabled by default. + */ + ALLOW_NON_NUMERIC_NUMBERS(false), + + /** + * 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) + ; + + /** + * 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; + + /* + /********************************************************** + /* Construction, configuration, initialization + /********************************************************** + */ + + protected JsonParser() { } + protected JsonParser(int features) { _features = features; } + + /** + * Accessor for {@link ObjectCodec} associated with this + * parser, if any. Codec is used by {@link #readValueAs(Class)} + * method (and its variants). + */ + public abstract ObjectCodec getCodec(); + + /** + * Setter that allows defining {@link ObjectCodec} associated with this + * parser, if any. Codec is used by {@link #readValueAs(Class)} + * method (and its variants). + */ + public abstract void setCodec(ObjectCodec c); + + /** + * Method that can be used to get access to object that is used + * to access input being parsed; this is usually either + * {@link InputStream} or {@link Reader}, depending on what + * parser was constructed with. + * Note that returned value may be null in some cases; including + * case where parser implementation does not want to exposed raw + * source to caller. + * In cases where input has been decorated, object returned here + * is the decorated version; this allows some level of interaction + * between users of parser and decorator object. + *

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

+ * Note that "current value" is NOT populated (or used) by Streaming parser; + * it is only used by higher-level data-binding functionality. + * The reason it is included here is that it can be stored and accessed hierarchically, + * and gets passed through data-binding. + * + * @since 2.5 + */ + public Object getCurrentValue() { + JsonStreamContext ctxt = getParsingContext(); + return (ctxt == null) ? null : ctxt.getCurrentValue(); + } + + /** + * Helper method, usually equivalent to: + * + * getParsingContext().setCurrentValue(v); + * + * + * @since 2.5 + */ + public void setCurrentValue(Object v) { + JsonStreamContext ctxt = getParsingContext(); + if (ctxt != null) { + ctxt.setCurrentValue(v); + } + } + + /* + /********************************************************** + /* Format support + /********************************************************** + */ + + /** + * Method to call to make this parser use specified schema. Method must + * be called before trying to parse any content, right after parser instance + * has been created. + * Note that not all parsers support schemas; and those that do usually only + * accept specific types of schemas: ones defined for data format parser can read. + *

+ * If parser does not support specified schema, {@link UnsupportedOperationException} + * is thrown. + * + * @param schema Schema to use + * + * @throws UnsupportedOperationException if parser does not support schema + */ + public void setSchema(FormatSchema schema) { + throw new UnsupportedOperationException("Parser of type "+getClass().getName()+" does not support schema of type '" + +schema.getSchemaType()+"'"); + } + + /** + * Method for accessing Schema that this parser uses, if any. + * Default implementation returns null. + * + * @since 2.1 + */ + public FormatSchema getSchema() { return null; } + + /** + * Method that can be used to verify that given schema can be used with + * this parser (using {@link #setSchema}). + * + * @param schema Schema to check + * + * @return True if this parser can use given schema; false if not + */ + public boolean canUseSchema(FormatSchema schema) { return false; } + + /* + /********************************************************** + /* Capability introspection + /********************************************************** + */ + + /** + * Method that can be called to determine if a custom + * {@link ObjectCodec} is needed for binding data parsed + * using {@link JsonParser} constructed by this factory + * (which typically also implies the same for serialization + * with {@link JsonGenerator}). + * + * @return True if custom codec is needed with parsers and + * generators created by this factory; false if a general + * {@link ObjectCodec} is enough + * + * @since 2.1 + */ + public boolean requiresCustomCodec() { return false;} + + /* + /********************************************************** + /* Versioned + /********************************************************** + */ + + /** + * Accessor for getting version of the core package, given a parser instance. + * Left for sub-classes to implement. + */ + @Override + public abstract Version version(); + + /* + /********************************************************** + /* Closeable implementation + /********************************************************** + */ + + /** + * Closes the parser so that no further iteration or data access + * can be made; will also close the underlying input source + * if parser either owns the input source, or feature + * {@link Feature#AUTO_CLOSE_SOURCE} is enabled. + * Whether parser owns the input source depends on factory + * method that was used to construct instance (so check + * {@link com.fasterxml.jackson.core.JsonFactory} for details, + * but the general + * idea is that if caller passes in closable resource (such + * as {@link InputStream} or {@link Reader}) parser does NOT + * own the source; but if it passes a reference (such as + * {@link java.io.File} or {@link java.net.URL} and creates + * stream or reader it does own them. + */ + @Override + public abstract void close() throws IOException; + + /* + /********************************************************** + /* Buffer handling + /********************************************************** + */ + + /** + * Method that can be called to push back any content that + * has been read but not consumed by the parser. This is usually + * done after reading all content of interest using parser. + * Content is released by writing it to given stream if possible; + * if underlying input is byte-based it can released, if not (char-based) + * it can not. + * + * @return -1 if the underlying content source is not byte based + * (that is, input can not be sent to {@link OutputStream}; + * otherwise number of bytes released (0 if there was nothing to release) + * + * @throws IOException if write to stream threw exception + */ + public int releaseBuffered(OutputStream out) throws IOException { + return -1; + } + + /** + * Method that can be called to push back any content that + * has been read but not consumed by the parser. + * This is usually + * done after reading all content of interest using parser. + * Content is released by writing it to given writer if possible; + * if underlying input is char-based it can released, if not (byte-based) + * it can not. + * + * @return -1 if the underlying content source is not char-based + * (that is, input can not be sent to {@link Writer}; + * otherwise number of chars released (0 if there was nothing to release) + * + * @throws IOException if write using Writer threw exception + */ + public int releaseBuffered(Writer w) throws IOException { return -1; } + + /* + /*************************************************** + /* Public API, configuration + /*************************************************** + */ + + /** + * Method for enabling specified parser feature + * (check {@link Feature} for list of features) + */ + public JsonParser enable(Feature f) { + _features |= f.getMask(); + return this; + } + + /** + * Method for disabling specified feature + * (check {@link Feature} for list of features) + */ + public JsonParser disable(Feature f) { + _features &= ~f.getMask(); + return this; + } + + /** + * Method for enabling or disabling specified feature + * (check {@link Feature} for list of features) + */ + public JsonParser configure(Feature f, boolean state) { + if (state) enable(f); else disable(f); + return this; + } + + /** + * Method for checking whether specified {@link Feature} is enabled. + */ + public boolean isEnabled(Feature f) { return f.enabledIn(_features); } + + /** + * Bulk access method for getting state of all standard {@link Feature}s. + * + * @return Bit mask that defines current states of all standard {@link Feature}s. + * + * @since 2.3 + */ + public int getFeatureMask() { return _features; } + + /** + * Bulk set method for (re)setting states of all standard {@link Feature}s + * + * @return This parser object, to allow chaining of calls + * + * @since 2.3 + * + * @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead + */ + @Deprecated + public JsonParser setFeatureMask(int mask) { + _features = mask; + return this; + } + + /** + * Bulk set method for (re)setting states of features specified by mask. + * Functionally equivalent to + * + * int oldState = getFeatureMask(); + * int newState = (oldState & ~mask) | (values & mask); + * setFeatureMask(newState); + * + * but preferred as this lets caller more efficiently specify actual changes made. + * + * @param values Bit mask of set/clear state for features to change + * @param mask Bit mask of features to change + * + * @since 2.6 + */ + public JsonParser overrideStdFeatures(int values, int mask) { + int newState = (_features & ~mask) | (values & mask); + return setFeatureMask(newState); + } + + /** + * Bulk access method for getting state of all {@link FormatFeature}s, format-specific + * on/off configuration settings. + * + * @return Bit mask that defines current states of all standard {@link FormatFeature}s. + * + * @since 2.6 + */ + public int getFormatFeatures() { + return 0; + } + + /** + * Bulk set method for (re)setting states of {@link FormatFeature}s, + * by specifying values (set / clear) along with a mask, to determine + * which features to change, if any. + *

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

+     *  return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
+     *
+ * but may be faster for parser to verify, and can therefore be used if caller + * expects to get such a property name from input next. + * + * @param str Property name to compare next token to (if next token is + * JsonToken.FIELD_NAME) + */ + public boolean nextFieldName(SerializableString str) throws IOException, JsonParseException { + return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName()); + } + + /** + * Method that fetches next token (as if calling {@link #nextToken}) and + * 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, JsonParseException { + 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, JsonParseException { + return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null; + } + + /** + * Method that fetches next token (as if calling {@link #nextToken}) and + * if it is {@link JsonToken#VALUE_NUMBER_INT} returns 32-bit int value; + * otherwise returns specified default value + * It is functionally equivalent to: + *
+     *  return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
+     *
+ * but may be faster for parser to process, and can therefore be used if caller + * expects to get a String value next from input. + */ + public int nextIntValue(int defaultValue) throws IOException, JsonParseException { + return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue; + } + + /** + * Method that fetches next token (as if calling {@link #nextToken}) and + * if it is {@link JsonToken#VALUE_NUMBER_INT} returns 64-bit long value; + * otherwise returns specified default value + * It is functionally equivalent to: + *
+     *  return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
+     *
+ * but may be faster for parser to process, and can therefore be used if caller + * expects to get a String value next from input. + */ + public long nextLongValue(long defaultValue) throws IOException, JsonParseException { + return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue; + } + + /** + * Method that fetches next token (as if calling {@link #nextToken}) and + * if it is {@link JsonToken#VALUE_TRUE} or {@link JsonToken#VALUE_FALSE} + * returns matching Boolean value; otherwise return null. + * It is functionally equivalent to: + *
+     *  JsonToken t = nextToken();
+     *  if (t == JsonToken.VALUE_TRUE) return Boolean.TRUE;
+     *  if (t == JsonToken.VALUE_FALSE) return Boolean.FALSE;
+     *  return null;
+     *
+ * but may be faster for parser to process, and can therefore be used if caller + * expects to get a String value next from input. + */ + public Boolean nextBooleanValue() throws IOException, JsonParseException { + 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, JsonParseException; + + /** + * Method that can be called to determine whether this parser + * is closed or not. If it is closed, no new tokens can be + * retrieved by calling {@link #nextToken} (and the underlying + * stream may be closed). Closing may be due to an explicit + * call to {@link #close} or because parser has encountered + * end of input. + */ + public abstract boolean isClosed(); + + /* + /********************************************************** + /* Public API, token accessors + /********************************************************** + */ + + /** + * Accessor to find which token parser currently points to, if any; + * null will be returned if none. + * If return value is non-null, data associated with the token + * is available via other accessor methods. + * + * @return Type of the token this parser currently points to, + * if any: null before any tokens have been read, and + * after end-of-input has been encountered, as well as + * if the current token has been explicitly cleared. + */ + public abstract JsonToken getCurrentToken(); + + /** + * Method similar to {@link #getCurrentToken()} but that returns an + * int instead of {@link JsonToken} (enum value). + *

+ * Use of int directly is typically more efficient on switch statements, + * so this method may be useful when building low-overhead codecs. + * Note, however, that effect may not be big enough to matter: make sure + * to profile performance before deciding to use this method. + * + * @since 2.3 + * + * @return int matching one of constants from {@link JsonTokenId}. + */ + 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 getCurrentTokenId() == id + * + * but may be more efficiently implemented. + *

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

+ * Note that no traversal or conversion is performed; so in some + * cases calling method like {@link #isExpectedStartArrayToken()} + * is necessary instead. + * + * @since 2.6 + */ + public abstract boolean hasToken(JsonToken t); + + /** + * 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; + + /** + * Method that can be used to access current parsing context reader + * is in. There are 3 different types: root, array and object contexts, + * with slightly different available information. Contexts are + * hierarchically nested, and can be used for example for figuring + * out part of the input document that correspond to specific + * array or object (for highlighting purposes, or error reporting). + * Contexts can also be used for simple xpath-like matching of + * input, if so desired. + */ + public abstract JsonStreamContext getParsingContext(); + + /** + * Method that return the starting location of the current + * token; that is, position of the first character from input + * that starts the current token. + */ + public abstract JsonLocation getTokenLocation(); + + /** + * Method that returns location of the last processed character; + * usually for error reporting purposes. + */ + public abstract JsonLocation getCurrentLocation(); + + /** + * Specialized accessor that can be used to verify that the current + * token indicates start array (usually meaning that current token + * is {@link JsonToken#START_ARRAY}) when start array is expected. + * For some specialized parsers this can return true for other cases + * as well; this is usually done to emulate arrays 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: + *

+     *   getCurrentToken() == JsonToken.START_ARRAY
+     *
+ * but may be overridden by custom parser implementations. + * + * @return True if the current token can be considered as a + * start-array marker (such {@link JsonToken#START_ARRAY}); + * false if not. + */ + public boolean isExpectedStartArrayToken() { return getCurrentToken() == JsonToken.START_ARRAY; } + + /** + * Similar to {@link #isExpectedStartArrayToken()}, but checks whether stream + * currently points to {@link JsonToken#START_OBJECT}. + * + * @since 2.5 + */ + public boolean isExpectedStartObjectToken() { return getCurrentToken() == JsonToken.START_OBJECT; } + + /* + /********************************************************** + /* Public API, token state overrides + /********************************************************** + */ + + /** + * Method called to "consume" the current token by effectively + * removing it so that {@link #hasCurrentToken} returns false, and + * {@link #getCurrentToken} null). + * Cleared token value can still be accessed by calling + * {@link #getLastClearedToken} (if absolutely needed), but + * usually isn't. + *

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

+ * Note that use of this method should only be done as sort of last + * resort, as it is a work-around for regular operation. + * + * @param name Name to use as the current name; may be null. + */ + public abstract void overrideCurrentName(String name); + + /* + /********************************************************** + /* Public API, access to token information, text + /********************************************************** + */ + + /** + * Method 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 similar to {@link #getText}, but that will return + * underlying (unmodifiable) character array that contains + * textual value, instead of constructing a String object + * to contain this information. + * Note, however, that: + *

+ *

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

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

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

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

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

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

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

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

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

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

+ * Note: only some specialized parser implementations support + * embedding of objects (usually ones that are facades on top + * of non-streaming sources, such as object trees). + */ + public abstract Object getEmbeddedObject() throws IOException; + + /* + /********************************************************** + /* 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 an int (including structured type + * markers like start/end Object/Array) + * default value of 0 will be returned; no exceptions are thrown. + */ + public long getValueAsLong() throws IOException { + return getValueAsLong(0); + } + + /** + * Method that will try to convert value of current token to a + * long. + * Numbers are coerced using default Java rules; booleans convert to 0 (false) + * and 1 (true), and Strings are parsed using default Java language integer + * parsing rules. + *

+ * If representation can not be converted to an int (including structured type + * markers like start/end Object/Array) + * specified 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 integer + * parsing rules. + *

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

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

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

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

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

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

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

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

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

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

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

+ * Instances are fully immutable and can be shared, cached. + * + * @author Tatu Saloranta + * + * @since 2.3 + */ +public class JsonPointer +{ + /** + * 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); } + + /* 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; } + public boolean mayMatchProperty() { return _matchingPropertyName != null; } + 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; + } + + public JsonPointer append(JsonPointer tail) { + if (this == EMPTY) { + return tail; + } + if (tail == EMPTY) { + return this; + } + 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); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonProcessingException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonProcessingException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonProcessingException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,128 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +/** + * Intermediate base class for all problems encountered when + * processing (parsing, generating) JSON content + * that are not pure I/O problems. + * Regular {@link java.io.IOException}s will be passed through as is. + * Sub-class of {@link java.io.IOException} for convenience. + */ +public class JsonProcessingException extends java.io.IOException +{ + final static long serialVersionUID = 123; // Stupid eclipse... + + protected JsonLocation _location; + + protected JsonProcessingException(String msg, JsonLocation loc, Throwable rootCause) { + /* Argh. IOException(Throwable,String) is only available starting + * with JDK 1.6... + */ + super(msg); + if (rootCause != null) { + initCause(rootCause); + } + _location = loc; + } + + protected JsonProcessingException(String msg) { + super(msg); + } + + protected JsonProcessingException(String msg, JsonLocation loc) { + this(msg, loc, null); + } + + protected JsonProcessingException(String msg, Throwable rootCause) { + this(msg, null, rootCause); + } + + protected JsonProcessingException(Throwable rootCause) { + this(null, null, rootCause); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + public JsonLocation getLocation() { return _location; } + + /** + * 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(); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonStreamContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonStreamContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonStreamContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,133 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +/** + * Shared base class for streaming processing contexts used during + * reading and writing of Json content using Streaming API. + * This context is also exposed to applications: + * context object can be used by applications to get an idea of + * relative position of the parser/generator within json content + * being processed. This allows for some contextual processing: for + * example, output within Array context can differ from that of + * Object context. + */ +public abstract class JsonStreamContext +{ + // // // Type constants used internally + + protected final static int TYPE_ROOT = 0; + protected final static int TYPE_ARRAY = 1; + protected final static int TYPE_OBJECT = 2; + + protected int _type; + + /** + * Index of the currently processed entry. Starts with -1 to signal + * that no entries have been started, and gets advanced each + * time a new entry is started, either by encountering an expected + * separator, or with new values if no separators are expected + * (the case for root context). + */ + protected int _index; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected JsonStreamContext() { } + + /* + /********************************************************** + /* Public API, accessors + /********************************************************** + */ + + /** + * Accessor for finding parent context of this context; will + * return null for root context. + */ + public abstract JsonStreamContext getParent(); + + /** + * Method that returns true if this context is an Array context; + * that is, content is being read from or written to a Json Array. + */ + public final boolean inArray() { return _type == TYPE_ARRAY; } + + /** + * Method that returns true if this context is a Root context; + * that is, content is being read from or written to without + * enclosing array or object structure. + */ + public final boolean inRoot() { return _type == TYPE_ROOT; } + + /** + * Method that returns true if this context is an Object context; + * that is, content is being read from or written to a Json Object. + */ + public final boolean inObject() { return _type == TYPE_OBJECT; } + + /** + * Method for accessing simple type description of current context; + * either ROOT (for root-level values), OBJECT (for field names and + * values of JSON Objects) or ARRAY (for values of JSON Arrays) + */ + public final String getTypeDesc() { + switch (_type) { + case TYPE_ROOT: return "ROOT"; + case TYPE_ARRAY: return "ARRAY"; + case TYPE_OBJECT: return "OBJECT"; + } + return "?"; + } + + /** + * @return Number of entries that are complete and started. + */ + public final int getEntryCount() { return _index + 1; } + + /** + * @return Index of the currently processed entry, if any + */ + public final int getCurrentIndex() { return (_index < 0) ? 0 : _index; } + + /** + * Method for accessing name associated with the current location. + * Non-null for FIELD_NAME and value events that directly + * follow field names; null for root level and array values. + */ + public abstract String getCurrentName(); + + /** + * Method for accessing currently active value being used by data-binding + * (as the source of streaming data to write, or destination of data being + * read), at this level in hierarchy. + *

+ * Note that "current value" is NOT populated (or used) by Streaming parser or generator; + * it is only used by higher-level data-binding functionality. + * The reason it is included here is that it can be stored and accessed hierarchically, + * and gets passed through data-binding. + * + * @return Currently active value, if one has been assigned. + * + * @since 2.5 + */ + public Object getCurrentValue() { + return null; + } + + /** + * Method to call to pass value to be returned via {@link #getCurrentValue}; typically + * called indirectly through {@link JsonParser#setCurrentValue} + * or {@link JsonGenerator#setCurrentValue}). + * + * @since 2.5 + */ + public void setCurrentValue(Object v) { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonToken.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonToken.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonToken.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,201 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +/** + * Enumeration for basic token types used for returning results + * of parsing JSON content. + */ +public enum JsonToken +{ + /* Some notes on implementation: + * + * - Entries are to be ordered such that start/end array/object + * markers come first, then field name marker (if any), and + * finally scalar value tokens. This is assumed by some + * typing checks. + */ + + /** + * NOT_AVAILABLE can be returned if {@link JsonParser} + * implementation can not currently return the requested + * token (usually next one), or even if any will be + * available, but that may be able to determine this in + * future. This is the case with non-blocking parsers -- + * they can not block to wait for more data to parse and + * must return something. + */ + NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE), + + /** + * START_OBJECT is returned when encountering '{' + * which signals starting of an Object value. + */ + START_OBJECT("{", JsonTokenId.ID_START_OBJECT), + + /** + * END_OBJECT is returned when encountering '}' + * which signals ending of an Object value + */ + END_OBJECT("}", JsonTokenId.ID_END_OBJECT), + + /** + * START_ARRAY is returned when encountering '[' + * which signals starting of an Array value + */ + START_ARRAY("[", JsonTokenId.ID_START_ARRAY), + + /** + * END_ARRAY is returned when encountering ']' + * which signals ending of an Array value + */ + END_ARRAY("]", JsonTokenId.ID_END_ARRAY), + + /** + * FIELD_NAME is returned when a String token is encountered + * as a field name (same lexical value, different function) + */ + FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME), + + /** + * Placeholder token returned when the input source has a concept + * of embedded Object that are not accessible as usual structure + * (of starting with {@link #START_OBJECT}, having values, ending with + * {@link #END_OBJECT}), but as "raw" objects. + *

+ * Note: this token is never returned by regular JSON readers, but + * only by readers that expose other kinds of source (like + * JsonNode-based JSON trees, Maps, Lists and such). + */ + VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT), + + /** + * VALUE_STRING is returned when a String token is encountered + * in value context (array element, field value, or root-level + * stand-alone value) + */ + VALUE_STRING(null, JsonTokenId.ID_STRING), + + /** + * VALUE_NUMBER_INT is returned when an integer numeric token is + * encountered in value context: that is, a number that does + * not have floating point or exponent marker in it (consists + * only of an optional sign, followed by one or more digits) + */ + VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT), + + /** + * VALUE_NUMBER_INT is returned when a numeric token other + * that is not an integer is encountered: that is, a number that does + * have floating point or exponent marker in it, in addition + * to one or more digits. + */ + VALUE_NUMBER_FLOAT(null, 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; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonTokenId.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonTokenId.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/JsonTokenId.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,87 @@ +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; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/ObjectCodec.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/ObjectCodec.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/ObjectCodec.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,184 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +import java.io.IOException; +import java.util.Iterator; + +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.core.type.TypeReference; + +/** + * Abstract class that defines the interface that {@link JsonParser} and + * {@link JsonGenerator} use to serialize and deserialize regular + * Java objects (POJOs aka Beans). + *

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

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

+ * Note: since Jackson 2.1, stateful implementations MUST implement + * {@link com.fasterxml.jackson.core.util.Instantiatable} interface, + * to allow for constructing per-generation instances and avoid + * state corruption (see [JACKSON-851] for details). + * Stateless implementations need not do this; but those are less common. + */ +public interface PrettyPrinter +{ + /* + /********************************************************** + /* First methods that act both as events, and expect + /* output for correct functioning (i.e something gets + /* output even when not pretty-printing) + /********************************************************** + */ + + // // // Root-level handling: + + /** + * Method called after a root-level value has been completely + * output, and before another value is to be output. + *

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

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

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

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

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

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

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

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

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

+ * Default handling does not output anything, but pretty-printer + * is free to add any white space decoration. + */ + void beforeObjectEntries(JsonGenerator gen) + throws IOException, JsonGenerationException; +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/SerializableString.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/SerializableString.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/SerializableString.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,156 @@ +/* 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 equvalent to: + *

+     *   getValue().length();
+     *
+ */ + int charLength(); + + /* + /********************************************************** + /* Accessors for byte sequences + /********************************************************** + */ + + /** + * Returns JSON quoted form of the String, as character array. + * Result can be embedded as-is in textual JSON as property name or JSON String. + */ + char[] asQuotedChars(); + + /** + * Returns UTF-8 encoded version of unquoted String. + * Functionally equivalent to (but more efficient than): + *
+     * getValue().getBytes("UTF-8");
+     *
+ */ + byte[] asUnquotedUTF8(); + + /** + * Returns UTF-8 encoded version of JSON-quoted String. + * Functionally equivalent to (but more efficient than): + *
+     * new String(asQuotedChars()).getBytes("UTF-8");
+     *
+ */ + byte[] asQuotedUTF8(); + + /* + /********************************************************** + /* Helper methods for appending byte/char sequences + /********************************************************** + */ + + /** + * Method that will append quoted UTF-8 bytes of this String into given + * buffer, if there is enough room; if not, returns -1. + * Functionally equivalent to: + *
+     *  byte[] bytes = str.asQuotedUTF8();
+     *  System.arraycopy(bytes, 0, buffer, offset, bytes.length);
+     *  return bytes.length;
+     *
+ * + * @return Number of bytes appended, if successful, otherwise -1 + */ + int appendQuotedUTF8(byte[] buffer, int offset); + + /** + * Method that will append quoted characters of this String into given + * buffer. Functionally equivalent to: + *
+     *  char[] ch = str.asQuotedChars();
+     *  System.arraycopy(ch, 0, buffer, offset, ch.length);
+     *  return ch.length;
+     *
+ * + * @return Number of characters appended, if successful, otherwise -1 + */ + int appendQuoted(char[] buffer, int offset); + + /** + * Method that will append unquoted ('raw') UTF-8 bytes of this String into given + * buffer. Functionally equivalent to: + *
+     *  byte[] bytes = str.asUnquotedUTF8();
+     *  System.arraycopy(bytes, 0, buffer, offset, bytes.length);
+     *  return bytes.length;
+     *
+ * + * @return Number of bytes appended, if successful, otherwise -1 + */ + int appendUnquotedUTF8(byte[] buffer, int offset); + + + /** + * Method that will append unquoted characters of this String into given + * buffer. Functionally equivalent to: + *
+     *  char[] ch = str.getValue().toCharArray();
+     *  System.arraycopy(bytes, 0, buffer, offset, ch.length);
+     *  return ch.length;
+     *
+ * + * @return Number of characters appended, if successful, otherwise -1 + */ + int appendUnquoted(char[] buffer, int offset); + + /* + /********************************************************** + /* Helper methods for writing out byte sequences + /********************************************************** + */ + + /** + * @return Number of bytes written + */ + int writeQuotedUTF8(OutputStream out) throws IOException; + + /** + * @return Number of bytes written + */ + int writeUnquotedUTF8(OutputStream out) throws IOException; + + /** + * @return Number of bytes put, if successful, otherwise -1 + */ + int putQuotedUTF8(ByteBuffer buffer) throws IOException; + + /** + * @return Number of bytes put, if successful, otherwise -1 + */ + int putUnquotedUTF8(ByteBuffer out) throws IOException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/TreeCodec.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/TreeCodec.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/TreeCodec.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,18 @@ +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 T readTree(JsonParser p) throws IOException, JsonProcessingException; + public abstract void writeTree(JsonGenerator g, TreeNode tree) throws IOException, JsonProcessingException; + public abstract TreeNode createArrayNode(); + public abstract TreeNode createObjectNode(); + public abstract JsonParser treeAsTokens(TreeNode node); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/TreeNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/TreeNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/TreeNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,288 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +import java.util.Iterator; + +/** + * Marker interface used to denote JSON Tree nodes, as far as + * the core package knows them (which is very little): mostly + * needed to allow {@link ObjectCodec} to have some level + * of interoperability. + * Most functionality is within JsonNode + * base class in mapper package. + *

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

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

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

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

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

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

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

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

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

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

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

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

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

+ * Note that optional group and artifact id properties are new with Jackson 2.0: + * if provided, they should align with Maven artifact information. + */ +public class Version + implements Comparable, java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + private final static Version UNKNOWN_VERSION = new Version(0, 0, 0, null, null, null); + + protected final int _majorVersion; + + protected final int _minorVersion; + + protected final int _patchLevel; + + protected final String _groupId; + + protected final String _artifactId; + + /** + * Additional information for snapshot versions; null for non-snapshot + * (release) versions. + */ + protected final String _snapshotInfo; + + /** + * @deprecated Use variant that takes group and artifact ids + * + * @since 2.1 + */ + @Deprecated + public Version(int major, int minor, int patchLevel, String snapshotInfo) + { + this(major, minor, patchLevel, snapshotInfo, null, null); + } + + public Version(int major, int minor, int patchLevel, String snapshotInfo, + String groupId, String artifactId) + { + _majorVersion = major; + _minorVersion = minor; + _patchLevel = patchLevel; + _snapshotInfo = snapshotInfo; + _groupId = (groupId == null) ? "" : groupId; + _artifactId = (artifactId == null) ? "" : artifactId; + } + + /** + * Method returns canonical "not known" version, which is used as version + * in cases where actual version information is not known (instead of null). + */ + public static Version unknownVersion() { return UNKNOWN_VERSION; } + + /** + * @since 2.7 to replace misspelled {@link #isUknownVersion()} + */ + public boolean isUnknownVersion() { return (this == UNKNOWN_VERSION); } + + public boolean isSnapshot() { return (_snapshotInfo != null && _snapshotInfo.length() > 0); } + + /** + * @deprecated Since 2.7 use correctly spelled method {@link #isUnknownVersion()} + */ + @Deprecated + public boolean isUknownVersion() { return isUnknownVersion(); } + + public int getMajorVersion() { return _majorVersion; } + public int getMinorVersion() { return _minorVersion; } + public int getPatchLevel() { return _patchLevel; } + + public String getGroupId() { return _groupId; } + public String getArtifactId() { return _artifactId; } + + public String toFullString() { + return _groupId + '/' + _artifactId + '/' + toString(); + } + + @Override public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(_majorVersion).append('.'); + sb.append(_minorVersion).append('.'); + sb.append(_patchLevel); + if (isSnapshot()) { + sb.append('-').append(_snapshotInfo); + } + return sb.toString(); + } + + @Override public int hashCode() { + return _artifactId.hashCode() ^ _groupId.hashCode() + _majorVersion - _minorVersion + _patchLevel; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + Version other = (Version) o; + return (other._majorVersion == _majorVersion) + && (other._minorVersion == _minorVersion) + && (other._patchLevel == _patchLevel) + && other._artifactId.equals(_artifactId) + && other._groupId.equals(_groupId) + ; + } + + @Override + public int compareTo(Version other) + { + if (other == this) return 0; + + int diff = _groupId.compareTo(other._groupId); + if (diff == 0) { + diff = _artifactId.compareTo(other._artifactId); + if (diff == 0) { + diff = _majorVersion - other._majorVersion; + if (diff == 0) { + diff = _minorVersion - other._minorVersion; + if (diff == 0) { + diff = _patchLevel - other._patchLevel; + } + } + } + } + return diff; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/Versioned.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/Versioned.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/Versioned.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,23 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core; + +/** + * Interface that those Jackson components that are explicitly versioned will implement. + * Intention is to allow both plug-in components (custom extensions) and applications and + * frameworks that use Jackson to detect exact version of Jackson in use. + * This may be useful for example for ensuring that proper Jackson version is deployed + * (beyond mechanisms that deployment system may have), as well as for possible + * workarounds. + */ +public interface Versioned { + /** + * Method called to detect version of the component that implements this interface; + * returned version should never be null, but may return specific "not available" + * instance (see {@link Version} for details). + */ + Version version(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/GeneratorBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/GeneratorBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/GeneratorBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,436 @@ +package com.fasterxml.jackson.core.base; + +import java.io.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.json.DupDetector; +import com.fasterxml.jackson.core.json.JsonWriteContext; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.core.util.VersionUtil; + +/** + * This base class implements part of API that a JSON generator exposes + * to applications, adds shared internal methods that sub-classes + * can use and adds some abstract methods sub-classes must implement. + */ +public abstract class GeneratorBase extends JsonGenerator +{ + 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 + */ + 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 String WRITE_BINARY = "write a binary value"; + protected final String WRITE_BOOLEAN = "write a boolean value"; + protected final String WRITE_NULL = "write a null"; + protected final String WRITE_NUMBER = "write a number"; + protected final String WRITE_RAW = "write a raw (unencoded) value"; + protected final String WRITE_STRING = "write a string"; + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected ObjectCodec _objectCodec; + + /** + * Bit flag composed of bits that indicate which + * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s + * are enabled. + */ + protected int _features; + + /** + * Flag set to indicate that implicit conversion from number + * to JSON String is needed (as per + * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_NUMBERS_AS_STRINGS}). + */ + protected boolean _cfgNumbersAsStrings; + + /* + /********************************************************** + /* State + /********************************************************** + */ + + /** + * Object that keeps track of the current contextual state + * of the generator. + */ + protected JsonWriteContext _writeContext; + + /** + * Flag that indicates whether generator is closed or not. Gets + * set when it is closed by an explicit call + * ({@link #close}). + */ + protected boolean _closed; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected GeneratorBase(int features, ObjectCodec codec) { + super(); + _features = features; + _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 + */ + 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 VersionUtil.versionFor(getClass()); } + + @Override + public Object getCurrentValue() { + return _writeContext.getCurrentValue(); + } + + @Override + public void setCurrentValue(Object v) { + _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) { } + + @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; + } + + @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 + */ + 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: co-variant return type. + */ + @Override public JsonWriteContext 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 + + /* + /********************************************************** + /* 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 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(); + } + + /* + /********************************************************** + /* 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; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/ParserBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/ParserBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/ParserBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1166 @@ +package com.fasterxml.jackson.core.base; + +import java.io.*; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.IOContext; +import com.fasterxml.jackson.core.io.NumberInput; +import com.fasterxml.jackson.core.json.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; + + /* + /********************************************************** + /* Constants and fields of former 'JsonNumericParserBase' + /********************************************************** + */ + + final protected static int NR_UNKNOWN = 0; + + // First, integer types + + final protected static int NR_INT = 0x0001; + final protected static int NR_LONG = 0x0002; + final protected static int NR_BIGINT = 0x0004; + + // And then floating point types + + final protected static int NR_DOUBLE = 0x008; + final protected static int NR_BIGDECIMAL = 0x0010; + + // Also, we need some numeric constants + + final static BigInteger BI_MIN_INT = BigInteger.valueOf(Integer.MIN_VALUE); + final static BigInteger BI_MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE); + + final static BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); + final static BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + + final static BigDecimal BD_MIN_LONG = new BigDecimal(BI_MIN_LONG); + final static BigDecimal BD_MAX_LONG = new BigDecimal(BI_MAX_LONG); + + final static BigDecimal BD_MIN_INT = new BigDecimal(BI_MIN_INT); + final static BigDecimal BD_MAX_INT = new BigDecimal(BI_MAX_INT); + + final static long MIN_INT_L = (long) Integer.MIN_VALUE; + final static long MAX_INT_L = (long) Integer.MAX_VALUE; + + // These are not very accurate, but have to do... (for bounds checks) + + final static double MIN_LONG_D = (double) Long.MIN_VALUE; + final static double MAX_LONG_D = (double) Long.MAX_VALUE; + + final static double MIN_INT_D = (double) Integer.MIN_VALUE; + final static double MAX_INT_D = (double) Integer.MAX_VALUE; + + // Digits, numeric + final protected static int INT_0 = '0'; + final protected static int INT_9 = '9'; + + final protected static int INT_MINUS = '-'; + final protected static int INT_PLUS = '+'; + + final protected static char CHAR_NULL = '\0'; + + // Numeric value holders: multiple fields used for + // for efficiency + + /** + * Bitfield that indicates which numeric representations + * have been calculated for the current type + */ + protected int _numTypesValid = NR_UNKNOWN; + + // First primitives + + protected int _numberInt; + + protected long _numberLong; + + protected double _numberDouble; + + // And then object types + + protected BigInteger _numberBigInt; + + protected BigDecimal _numberBigDecimal; + + // And then other information about value itself + + /** + * Flag that indicates whether numeric value has a negative + * value. That is, whether its textual representation starts + * with minus character. + */ + protected boolean _numberNegative; + + /** + * Length of integer part of the number, in characters + */ + protected int _intLength; + + /** + * Length of the fractional part (not including decimal + * point or exponent), in characters. + * Not used for pure integer values. + */ + protected int _fractLength; + + /** + * Length of the exponent part of the number, if any, not + * including 'e' marker or sign, just digits. + * Not used for pure integer values. + */ + protected int _expLength; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected ParserBase(IOContext ctxt, int features) { + super(features); + _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) { + _closed = true; + try { + _closeInput(); + } finally { + // as per [JACKSON-324], do in finally block + // Also, internal buffer(s) can now be released as well + _releaseBuffers(); + } + } + } + + @Override public boolean isClosed() { return _closed; } + @Override public JsonReadContext getParsingContext() { return _parsingContext; } + + /** + * Method that return the starting location of the current + * token; that is, position of the first character from input + * that starts the current token. + */ + @Override + public JsonLocation getTokenLocation() { + return new JsonLocation(_ioContext.getSourceReference(), + -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(_ioContext.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; + } + + // No embedded objects with base impl... + @Override public Object getEmbeddedObject() throws IOException { return null; } + + @SuppressWarnings("resource") + @Override // since 2.7 + public byte[] getBinaryValue(Base64Variant variant) throws IOException + { + 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); + } + + /* + /********************************************************** + /* Low-level reading, other + /********************************************************** + */ + + protected final void loadMoreGuaranteed() throws IOException { + if (!loadMore()) { _reportInvalidEOF(); } + } + + /* + /********************************************************** + /* Abstract methods needed from sub-classes + /********************************************************** + */ + + protected abstract boolean loadMore() throws IOException; + protected abstract void _finishString() throws IOException; + protected abstract void _closeInput() throws IOException; + + /* + /********************************************************** + /* Low-level reading, other + /********************************************************** + */ + + /** + * Method called to release internal buffers owned by the base + * reader. This may be called along with {@link #_closeInput} (for + * example, when explicitly closing this reader instance), or + * separately (if need be). + */ + protected void _releaseBuffers() throws IOException { + _textBuffer.releaseBuffers(); + char[] buf = _nameCopyBuffer; + if (buf != null) { + _nameCopyBuffer = null; + _ioContext.releaseNameCopyBuffer(buf); + } + } + + /** + * Method called when an EOF is encountered between tokens. + * If so, it may be a legitimate EOF, but only iff there + * is no open non-root context. + */ + @Override + protected void _handleEOF() throws JsonParseException { + if (!_parsingContext.inRoot()) { + _reportInvalidEOF(": expected close marker for "+_parsingContext.getTypeDesc()+" (from "+_parsingContext.getStartLocation(_ioContext.getSourceReference())+")"); + } + } + + /** + * @since 2.4 + */ + protected final int _eofAsNextChar() throws JsonParseException { + _handleEOF(); + return -1; + } + + /* + /********************************************************** + /* Internal/package methods: Error reporting + /********************************************************** + */ + + protected void _reportMismatchedEndMarker(int actCh, char expCh) throws JsonParseException { + String startDesc = ""+_parsingContext.getStartLocation(_ioContext.getSourceReference()); + _reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+_parsingContext.getTypeDesc()+" starting at "+startDesc+")"); + } + + /* + /********************************************************** + /* Internal/package methods: shared/reusable builders + /********************************************************** + */ + + public ByteArrayBuilder _getByteArrayBuilder() + { + if (_byteArrayBuilder == null) { + _byteArrayBuilder = new ByteArrayBuilder(); + } else { + _byteArrayBuilder.reset(); + } + return _byteArrayBuilder; + } + + /* + /********************************************************** + /* Methods from former JsonNumericParserBase + /********************************************************** + */ + + // // // Life-cycle of number-parsing + + protected final JsonToken reset(boolean negative, int intLen, int fractLen, int expLen) + { + if (fractLen < 1 && expLen < 1) { // integer + return resetInt(negative, intLen); + } + return resetFloat(negative, intLen, fractLen, expLen); + } + + protected final JsonToken resetInt(boolean negative, int intLen) + { + _numberNegative = negative; + _intLength = intLen; + _fractLength = 0; + _expLength = 0; + _numTypesValid = NR_UNKNOWN; // to force parsing + return JsonToken.VALUE_NUMBER_INT; + } + + protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen, int expLen) + { + _numberNegative = negative; + _intLength = intLen; + _fractLength = fractLen; + _expLength = expLen; + _numTypesValid = NR_UNKNOWN; // to force parsing + return JsonToken.VALUE_NUMBER_FLOAT; + } + + protected final JsonToken resetAsNaN(String valueStr, double value) + { + _textBuffer.resetWithString(valueStr); + _numberDouble = value; + _numTypesValid = NR_DOUBLE; + return JsonToken.VALUE_NUMBER_FLOAT; + } + + /* + /********************************************************** + /* Numeric accessors of public API + /********************************************************** + */ + + @Override + public Number getNumberValue() throws IOException + { + 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) { + char[] buf = _textBuffer.getTextBuffer(); + int offset = _textBuffer.getTextOffset(); + int len = _intLength; + if (_numberNegative) { + ++offset; + } + if (len <= 9) { // definitely fits in int + int i = NumberInput.parseInt(buf, offset, len); + _numberInt = _numberNegative ? -i : i; + _numTypesValid = NR_INT; + return; + } + if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls + long l = NumberInput.parseLong(buf, offset, len); + if (_numberNegative) { + l = -l; + } + // [JACKSON-230] Could still fit in int, need to check + if (len == 10) { + if (_numberNegative) { + if (l >= MIN_INT_L) { + _numberInt = (int) l; + _numTypesValid = NR_INT; + return; + } + } else { + if (l <= MAX_INT_L) { + _numberInt = (int) l; + _numTypesValid = NR_INT; + return; + } + } + } + _numberLong = l; + _numTypesValid = NR_LONG; + return; + } + _parseSlowInt(expType, buf, offset, len); + return; + } + if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { + _parseSlowFloat(expType); + return; + } + _reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors"); + } + + /** + * @since 2.6 + */ + protected int _parseIntValue() throws IOException + { + // Inlined variant of: _parseNumericValue(NR_INT) + + if (_currToken == JsonToken.VALUE_NUMBER_INT) { + char[] buf = _textBuffer.getTextBuffer(); + int offset = _textBuffer.getTextOffset(); + int len = _intLength; + if (_numberNegative) { + ++offset; + } + if (len <= 9) { + int i = NumberInput.parseInt(buf, offset, len); + if (_numberNegative) { + i = -i; + } + _numberInt = i; + _numTypesValid = NR_INT; + return i; + } + } + _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 '"+_textBuffer.contentsAsString()+"'", nex); + } + } + + private void _parseSlowInt(int expType, char[] buf, int offset, int len) throws IOException + { + String numStr = _textBuffer.contentsAsString(); + try { + // [JACKSON-230] Some long cases still... + if (NumberInput.inLongRange(buf, offset, len, _numberNegative)) { + // Probably faster to construct a String, call parse, than to use BigInteger + _numberLong = Long.parseLong(numStr); + _numTypesValid = NR_LONG; + } else { + // nope, need the heavy guns... (rare case) + _numberBigInt = new BigInteger(numStr); + _numTypesValid = NR_BIGINT; + } + } catch (NumberFormatException nex) { + // Can this ever occur? Due to overflow, maybe? + _wrapError("Malformed numeric value '"+numStr+"'", nex); + } + } + + /* + /********************************************************** + /* Numeric conversions + /********************************************************** + */ + + protected void convertNumberToInt() throws IOException + { + // First, converting from long ought to be easy + if ((_numTypesValid & NR_LONG) != 0) { + // Let's verify it's lossless conversion by simple roundtrip + int result = (int) _numberLong; + if (((long) result) != _numberLong) { + _reportError("Numeric value ("+getText()+") out of range of int"); + } + _numberInt = result; + } else if ((_numTypesValid & NR_BIGINT) != 0) { + if (BI_MIN_INT.compareTo(_numberBigInt) > 0 + || BI_MAX_INT.compareTo(_numberBigInt) < 0) { + reportOverflowInt(); + } + _numberInt = _numberBigInt.intValue(); + } else if ((_numTypesValid & NR_DOUBLE) != 0) { + // Need to check boundaries + if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D) { + reportOverflowInt(); + } + _numberInt = (int) _numberDouble; + } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { + if (BD_MIN_INT.compareTo(_numberBigDecimal) > 0 + || BD_MAX_INT.compareTo(_numberBigDecimal) < 0) { + reportOverflowInt(); + } + _numberInt = _numberBigDecimal.intValue(); + } else { + _throwInternal(); + } + _numTypesValid |= NR_INT; + } + + protected void convertNumberToLong() throws IOException + { + 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 + * would 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; + } + + /* + /********************************************************** + /* Number handling exceptions + /********************************************************** + */ + + protected void reportUnexpectedNumberChar(int ch, String comment) throws JsonParseException { + String msg = "Unexpected character ("+_getCharDesc(ch)+") in numeric value"; + if (comment != null) { + msg += ": "+comment; + } + _reportError(msg); + } + + protected void reportInvalidNumber(String msg) throws JsonParseException { + _reportError("Invalid numeric value: "+msg); + } + + protected void reportOverflowInt() throws IOException { + _reportError("Numeric value ("+getText()+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")"); + } + + protected void reportOverflowLong() throws IOException { + _reportError("Numeric value ("+getText()+") out of range of long ("+Long.MIN_VALUE+" - "+Long.MAX_VALUE+")"); + } + + /* + /********************************************************** + /* 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) { + throw reportInvalidBase64Char(b64variant, unescaped, index); + } + return bits; + } + + protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index) throws IOException + { + // 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars + if (ch != '\\') { + throw reportInvalidBase64Char(b64variant, ch, index); + } + char unescaped = _decodeEscaped(); + // if white space, skip if first triplet; otherwise errors + if (unescaped <= INT_SPACE) { + if (index == 0) { // whitespace only allowed to be skipped between triplets + return -1; + } + } + // otherwise try to find actual triplet value + int bits = b64variant.decodeBase64Char(unescaped); + if (bits < 0) { + throw reportInvalidBase64Char(b64variant, unescaped, index); + } + return bits; + } + + protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex) throws IllegalArgumentException { + return reportInvalidBase64Char(b64variant, ch, bindex, null); + } + + /** + * @param bindex Relative index within base64 character unit; between 0 + * and 3 (as unit has exactly 4 characters) + */ + protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex, String msg) throws IllegalArgumentException { + String base; + if (ch <= INT_SPACE) { + base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units"; + } else if (b64variant.usesPaddingChar(ch)) { + base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; + } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { + // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level) + base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; + } else { + base = "Illegal character '"+((char)ch)+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; + } + if (msg != null) { + base = base + ": " + msg; + } + return new IllegalArgumentException(base); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/ParserMinimalBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/ParserMinimalBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/ParserMinimalBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,551 @@ +package com.fasterxml.jackson.core.base; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.core.io.NumberInput; +import com.fasterxml.jackson.core.util.ByteArrayBuilder; +import com.fasterxml.jackson.core.util.VersionUtil; + +import static com.fasterxml.jackson.core.JsonTokenId.*; + +/** + * Intermediate base class used by all Jackson {@link JsonParser} + * implementations, but does not add any additional fields that depend + * on particular method of obtaining input. + *

+ * Note that 'minimal' here mostly refers to minimal number of fields + * (size) and functionality that is specific to certain types + * of parser implementations; but not necessarily to number of methods. + */ +public abstract class ParserMinimalBase extends JsonParser +{ + // Control chars: + protected final static int INT_TAB = '\t'; + protected final static int INT_LF = '\n'; + protected final static int INT_CR = '\r'; + protected final static int INT_SPACE = 0x0020; + + // Markup + protected final static int INT_LBRACKET = '['; + protected final static int INT_RBRACKET = ']'; + protected final static int INT_LCURLY = '{'; + protected final static int INT_RCURLY = '}'; + protected final static int INT_QUOTE = '"'; + protected final static int INT_BACKSLASH = '\\'; + protected final static int INT_SLASH = '/'; + protected final static int INT_COLON = ':'; + protected final static int INT_COMMA = ','; + protected final static int INT_HASH = '#'; + + // fp numbers + protected final static int INT_PERIOD = '.'; + protected final static int INT_e = 'e'; + protected final static int INT_E = 'E'; + + /* + /********************************************************** + /* Minimal generally useful state + /********************************************************** + */ + + /** + * Last token retrieved via {@link #nextToken}, if any. + * Null before the first call to nextToken(), + * as well as if token has been explicitly cleared + */ + protected JsonToken _currToken; + + /** + * Last cleared token, if any: that is, value that was in + * effect when {@link #clearCurrentToken} was called. + */ + protected JsonToken _lastClearedToken; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected ParserMinimalBase() { } + protected ParserMinimalBase(int features) { super(features); } + + // NOTE: had base impl in 2.3 and before; but shouldn't + // public abstract Version version(); + + /* + /********************************************************** + /* Configuration overrides if any + /********************************************************** + */ + + // from base class: + + //public void enableFeature(Feature f) + //public void disableFeature(Feature f) + //public void setFeature(Feature f, boolean state) + //public boolean isFeatureEnabled(Feature f) + + /* + /********************************************************** + /* JsonParser impl + /********************************************************** + */ + + @Override public abstract JsonToken nextToken() throws IOException; + @Override public JsonToken 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; + } + } + } + } + + /** + * 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 = this.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 = this.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 = this.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 + { + // just call helper method introduced in 2.2.3 + 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 _reportUnexpectedChar(int ch, String comment) throws JsonParseException + { + if (ch < 0) { // sanity check + _reportInvalidEOF(); + } + String msg = "Unexpected character ("+_getCharDesc(ch)+")"; + if (comment != null) { + msg += ": "+comment; + } + _reportError(msg); + } + + protected void _reportInvalidEOF() throws JsonParseException { + _reportInvalidEOF(" in "+_currToken); + } + + protected void _reportInvalidEOF(String msg) throws JsonParseException { + _reportError("Unexpected end-of-input"+msg); + } + + protected void _reportInvalidEOFInValue() throws JsonParseException { + _reportInvalidEOF(" in a value"); + } + + protected void _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); + } + + /** + * Method called to report a problem with unquoted control character. + * Note: starting with version 1.4, it is possible to suppress + * exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}. + */ + protected void _throwUnquotedSpace(int i, String ctxtDesc) throws JsonParseException { + // JACKSON-208; possible to allow unquoted control chars: + if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i > INT_SPACE) { + char c = (char) i; + String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc; + _reportError(msg); + } + } + + protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException { + // as per [JACKSON-300] + if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) { + return ch; + } + // and [JACKSON-548] + if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { + return ch; + } + _reportError("Unrecognized character escape "+_getCharDesc(ch)); + return ch; + } + + /* + /********************************************************** + /* Error reporting, generic + /********************************************************** + */ + + protected final static String _getCharDesc(int ch) + { + char c = (char) ch; + if (Character.isISOControl(c)) { + return "(CTRL-CHAR, code "+ch+")"; + } + if (ch > 255) { + return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")"; + } + return "'"+c+"' (code "+ch+")"; + } + + protected final void _reportError(String msg) throws JsonParseException { + throw _constructError(msg); + } + + protected final void _wrapError(String msg, Throwable t) throws JsonParseException { + throw _constructError(msg, t); + } + + protected final void _throwInternal() { + VersionUtil.throwInternal(); + } + + protected final JsonParseException _constructError(String msg, Throwable t) { + return new JsonParseException(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); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/base/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,9 @@ +/** + * 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; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,868 @@ +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 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.writeRawUTF8String(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; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,896 @@ +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 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 final 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 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 + { + //Check for _allowMultipleMatches - false and atleast 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 not scalar and ended successfully, then return null + if((_currToken.isStructEnd() && _headContext.isStartHandled()) ){ + return (_currToken = null); + } + //else if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL matched once + // then return null + else 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 + return (_currToken = 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 (_includePath) { + return (_currToken = t); + } + } + 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))) { + 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? + return (_currToken = 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 (_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))) { + 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; + } + // Hmmh. Do we need both checks, or should above suffice? + if (gotEnd || (_headContext == buffRoot)) { + return null; + } + } + 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) { + return _nextBuffered(buffRoot); + } + } + 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))) { + 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; + } + } + } + + @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; + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/JsonPointerBasedFilter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/JsonPointerBasedFilter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/JsonPointerBasedFilter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,69 @@ +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+"]"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/TokenFilter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/TokenFilter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/TokenFilter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,363 @@ +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: + *

+ *

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

+ *

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

+ *

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

+ *

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

+ *

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

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

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

+ * NOTE: value itself not passed since it may come on multiple forms + * and is unlikely to be of much use in determining inclusion + * criteria. + */ + public boolean includeRawValue() { + return _includeScalar(); + } + + /** + * Call made to verify whether leaf-level + * embedded (Opaque) value + * should be included in output or not. + */ + public boolean includeEmbeddedValue(Object ob) { + return _includeScalar(); + } + + /* + /********************************************************** + /* Overrides + /********************************************************** + */ + + @Override + public String toString() { + if (this == INCLUDE_ALL) { + return "TokenFilter.INCLUDE_ALL"; + } + return super.toString(); + } + + /* + /********************************************************** + /* Other methods + /********************************************************** + */ + + /** + * Overridable default implementation delegated to all scalar value + * inclusion check methods. + * The default implementation simply includes all leaf values. + */ + protected boolean _includeScalar() { + return true; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/TokenFilterContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/TokenFilterContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/filter/TokenFilterContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,353 @@ +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; } + + 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(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/DataFormatDetector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/DataFormatDetector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/DataFormatDetector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,202 @@ +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 detectors) { + this(detectors.toArray(new JsonFactory[detectors.size()])); + } + + /** + * Method that will return a detector instance that uses given + * optimal match level (match that is considered sufficient to return, without + * trying to find stronger matches with other formats). + */ + public DataFormatDetector withOptimalMatch(MatchStrength optMatch) { + if (optMatch == _optimalMatch) { + return this; + } + return new DataFormatDetector(_detectors, optMatch, _minimalMatch, _maxInputLookahead); + } + /** + * Method that will return a detector instance that uses given + * minimal match level; match that may be returned unless a stronger match + * is found with other format detectors. + */ + public DataFormatDetector withMinimalMatch(MatchStrength minMatch) { + if (minMatch == _minimalMatch) { + return this; + } + return new DataFormatDetector(_detectors, _optimalMatch, minMatch, _maxInputLookahead); + } + + /** + * Method that will return a detector instance that allows detectors to + * read up to specified number of bytes when determining format match strength. + */ + public DataFormatDetector withMaxInputLookahead(int lookaheadBytes) { + if (lookaheadBytes == _maxInputLookahead) { + return this; + } + return new DataFormatDetector(_detectors, _optimalMatch, _minimalMatch, lookaheadBytes); + } + + private DataFormatDetector(JsonFactory[] detectors, + MatchStrength optMatch, MatchStrength minMatch, int maxInputLookahead) { + _detectors = detectors; + _optimalMatch = optMatch; + _minimalMatch = minMatch; + _maxInputLookahead = maxInputLookahead; + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + /** + * Method to call to find format that content (accessible via given + * {@link InputStream}) given has, as per configuration of this detector + * instance. + * + * @return Matcher object which contains result; never null, even in cases + * where no match (with specified minimal match strength) is found. + */ + public DataFormatMatcher findFormat(InputStream in) throws IOException { + return _findFormat(new InputAccessor.Std(in, new byte[_maxInputLookahead])); + } + + /** + * Method to call to find format that given content (full document) + * has, as per configuration of this detector instance. + * + * @return Matcher object which contains result; never null, even in cases + * where no match (with specified minimal match strength) is found. + */ + public DataFormatMatcher findFormat(byte[] fullInputData) throws IOException { + return _findFormat(new InputAccessor.Std(fullInputData)); + } + + /** + * Method to call to find format that given content (full document) + * has, as per configuration of this detector instance. + * + * @return Matcher object which contains result; never null, even in cases + * where no match (with specified minimal match strength) is found. + * + * @since 2.1 + */ + public DataFormatMatcher findFormat(byte[] fullInputData, int offset, int len) throws IOException { + return _findFormat(new InputAccessor.Std(fullInputData, offset, len)); + } + + /* + /********************************************************** + /* Overrides + /********************************************************** + */ + + @Override public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + final int len = _detectors.length; + if (len > 0) { + sb.append(_detectors[0].getFormatName()); + for (int i = 1; i < len; ++i) { + sb.append(", "); + sb.append(_detectors[i].getFormatName()); + } + } + sb.append(']'); + return sb.toString(); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + private DataFormatMatcher _findFormat(InputAccessor.Std acc) throws IOException { + JsonFactory bestMatch = null; + MatchStrength bestMatchStrength = null; + for (JsonFactory f : _detectors) { + acc.reset(); + MatchStrength strength = f.hasFormat(acc); + // if not better than what we have so far (including minimal level limit), skip + if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) { + continue; + } + // also, needs to better match than before + if (bestMatch != null) { + if (bestMatchStrength.ordinal() >= strength.ordinal()) { + continue; + } + } + // finally: if it's good enough match, we are done + bestMatch = f; + bestMatchStrength = strength; + if (strength.ordinal() >= _optimalMatch.ordinal()) { + break; + } + } + return acc.createMatcher(bestMatch, bestMatchStrength); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/DataFormatMatcher.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/DataFormatMatcher.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/DataFormatMatcher.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,124 @@ +package com.fasterxml.jackson.core.format; + +import java.io.*; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.io.MergedStream; + +/** + * Result object constructed by {@link DataFormatDetector} when requested + * to detect format of given input data. + */ +public class DataFormatMatcher +{ + protected final InputStream _originalStream; + + /** + * Content read during format matching process + */ + protected final byte[] _bufferedData; + + /** + * Pointer to the first byte in buffer available for reading + */ + protected final int _bufferedStart; + + /** + * Number of bytes available in buffer. + */ + protected final int _bufferedLength; + + /** + * Factory that produced sufficient match (if any) + */ + protected final JsonFactory _match; + + /** + * Strength of match with {@link #_match} + */ + protected final MatchStrength _matchStrength; + + protected DataFormatMatcher(InputStream in, byte[] buffered, + int bufferedStart, int bufferedLength, + JsonFactory match, MatchStrength strength) + { + _originalStream = in; + _bufferedData = buffered; + _bufferedStart = bufferedStart; + _bufferedLength = bufferedLength; + _match = match; + _matchStrength = strength; + } + + /* + /********************************************************** + /* Public API, simple accessors + /********************************************************** + */ + + /** + * Accessor to use to see if any formats matched well enough with + * the input data. + */ + public boolean hasMatch() { return _match != null; } + + /** + * Method for accessing strength of the match, if any; if no match, + * will return {@link MatchStrength#INCONCLUSIVE}. + */ + public MatchStrength getMatchStrength() { + return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength; + } + + /** + * Accessor for {@link JsonFactory} that represents format that data matched. + */ + public JsonFactory getMatch() { return _match; } + + /** + * Accessor for getting brief textual name of matched format if any (null + * if none). Equivalent to: + *

+     *   return hasMatch() ? getMatch().getFormatName() : null;
+     *
+ */ + public String getMatchedFormatName() { + return _match.getFormatName(); + } + + /* + /********************************************************** + /* Public API, factory methods + /********************************************************** + */ + + /** + * Convenience method for trying to construct a {@link JsonParser} for + * parsing content which is assumed to be in detected data format. + * If no match was found, returns null. + */ + public JsonParser createParserWithMatch() throws IOException { + if (_match == null) { + return null; + } + if (_originalStream == null) { + return _match.createParser(_bufferedData, _bufferedStart, _bufferedLength); + } + return _match.createParser(getDataStream()); + } + + /** + * Method to use for accessing input for which format detection has been done. + * This must be used instead of using stream passed to detector + * unless given stream itself can do buffering. + * Stream will return all content that was read during matching process, as well + * as remaining contents of the underlying stream. + */ + public InputStream getDataStream() { + if (_originalStream == null) { + return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength); + } + return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/InputAccessor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/InputAccessor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/InputAccessor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,151 @@ +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); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/MatchStrength.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/MatchStrength.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/MatchStrength.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,62 @@ +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. + *

+ * 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 + ; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/format/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,6 @@ +/** + * 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; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/CharTypes.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/CharTypes.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/CharTypes.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,259 @@ +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 128 Unicode characters (7-bit ASCII) + * range. For actual hex digits, contains corresponding value; + * for others -1. + */ + private final static int[] sHexValues = new int[128]; + static { + Arrays.fill(sHexValues, -1); + for (int i = 0; i < 10; ++i) { + sHexValues['0' + i] = i; + } + for (int i = 0; i < 6; ++i) { + sHexValues['a' + i] = 10 + i; + sHexValues['A' + i] = 10 + i; + } + } + + public static int[] getInputCodeLatin1() { return sInputCodes; } + public static int[] getInputCodeUtf8() { return sInputCodesUTF8; } + + public static int[] getInputCodeLatin1JsNames() { return sInputCodesJsNames; } + public static int[] getInputCodeUtf8JsNames() { return sInputCodesUtf8JsNames; } + + public static int[] getInputCodeComment() { return sInputCodesComment; } + 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; } + + public static int charToHex(int ch) + { + return (ch > 127) ? -1 : sHexValues[ch]; + } + + public static void appendQuoted(StringBuilder sb, String content) + { + final int[] escCodes = sOutputEscapes128; + int escLen = escCodes.length; + for (int i = 0, len = content.length(); i < len; ++i) { + char c = content.charAt(i); + if (c >= escLen || escCodes[c] == 0) { + sb.append(c); + continue; + } + sb.append('\\'); + int escCode = escCodes[c]; + if (escCode < 0) { // generic quoting (hex value) + // 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(); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/CharacterEscapes.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/CharacterEscapes.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/CharacterEscapes.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,71 @@ +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 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); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/IOContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/IOContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/IOContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,281 @@ +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. + *

+ * 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)); + } + + 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) { + if ((toRelease != src) && (toRelease.length <= src.length)) { throw wrongBuf(); } + } + + protected final void _verifyRelease(char[] toRelease, char[] src) { + if ((toRelease != src) && (toRelease.length <= src.length)) { throw wrongBuf(); } + } + + private IllegalArgumentException wrongBuf() { return new IllegalArgumentException("Trying to release buffer not owned by the context"); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/InputDecorator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/InputDecorator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/InputDecorator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,68 @@ +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 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; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/JsonStringEncoder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/JsonStringEncoder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/JsonStringEncoder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,392 @@ +package com.fasterxml.jackson.core.io; + +import java.lang.ref.SoftReference; + +import com.fasterxml.jackson.core.util.BufferRecycler; +import com.fasterxml.jackson.core.util.ByteArrayBuilder; +import com.fasterxml.jackson.core.util.TextBuffer; + +/** + * Helper class used for efficient encoding of JSON String values (including + * JSON field names) into Strings or UTF-8 byte arrays. + *

+ * Note that methods in here are somewhat optimized, but not ridiculously so. + * Reason is that conversion method results are expected to be cached so that + * these methods will not be hot spots during normal operation. + */ +public final class JsonStringEncoder +{ + private final static char[] 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 INT_BACKSLASH = '\\'; +// private final static int INT_U = 'u'; +// private final static int INT_0 = '0'; + + /** + * This ThreadLocal contains a {@link java.lang.ref.SoftReference} + * to a {@link BufferRecycler} used to provide a low-cost + * buffer recycling between reader and writer instances. + */ + final protected static ThreadLocal> _threadEncoder + = new ThreadLocal>(); + + /** + * Lazily constructed text buffer used to produce JSON encoded Strings + * as characters (without UTF-8 encoding) + */ + protected TextBuffer _text; + + /** + * Lazily-constructed builder used for UTF-8 encoding of text values + * (quoted and unquoted) + */ + protected ByteArrayBuilder _bytes; + + /** + * Temporary buffer used for composing quote/escape sequences + */ + protected final char[] _qbuf; + + /* + /********************************************************** + /* Construction, instance access + /********************************************************** + */ + + public JsonStringEncoder() { + _qbuf = new char[6]; + _qbuf[0] = '\\'; + _qbuf[2] = '0'; + _qbuf[3] = '0'; + } + + /** + * Factory method for getting an instance; this is either recycled per-thread instance, + * or a newly constructed one. + */ + public static JsonStringEncoder getInstance() { + SoftReference ref = _threadEncoder.get(); + JsonStringEncoder enc = (ref == null) ? null : ref.get(); + + if (enc == null) { + enc = new JsonStringEncoder(); + _threadEncoder.set(new SoftReference(enc)); + } + return enc; + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + /** + * Method that will quote text contents using JSON standard quoting, + * and return results as a character array + */ + public char[] quoteAsString(String input) + { + TextBuffer textBuffer = _text; + if (textBuffer == null) { + // no allocator; can add if we must, shouldn't need to + _text = textBuffer = new TextBuffer(null); + } + char[] outputBuffer = textBuffer.emptyAndGetCurrentSegment(); + final int[] escCodes = CharTypes.get7BitOutputEscapes(); + final int escCodeCount = escCodes.length; + int inPtr = 0; + final int inputLen = input.length(); + int outPtr = 0; + + outer: + while (inPtr < inputLen) { + tight_loop: + while (true) { + char c = input.charAt(inPtr); + if (c < escCodeCount && escCodes[c] != 0) { + break tight_loop; + } + if (outPtr >= outputBuffer.length) { + outputBuffer = textBuffer.finishCurrentSegment(); + outPtr = 0; + } + outputBuffer[outPtr++] = c; + if (++inPtr >= inputLen) { + break outer; + } + } + // something to escape; 2 or 6-char variant? + char d = input.charAt(inPtr++); + int escCode = escCodes[d]; + int length = (escCode < 0) + ? _appendNumeric(d, _qbuf) + : _appendNamed(escCode, _qbuf); + ; + if ((outPtr + length) > outputBuffer.length) { + int first = outputBuffer.length - outPtr; + if (first > 0) { + System.arraycopy(_qbuf, 0, outputBuffer, outPtr, first); + } + 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; + } + } + textBuffer.setCurrentLength(outPtr); + return textBuffer.contentsAsArray(); + } + + /** + * Will quote given JSON String value using standard quoting, encode + * results as UTF-8, and return result as a byte array. + */ + @SuppressWarnings("resource") + public byte[] quoteAsUTF8(String text) + { + ByteArrayBuilder bb = _bytes; + if (bb == null) { + // no allocator; can add if we must, shouldn't need to + _bytes = bb = new ByteArrayBuilder(null); + } + int inputPtr = 0; + int inputEnd = text.length(); + int outputPtr = 0; + byte[] outputBuffer = bb.resetAndGetFirstSegment(); + + 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) { + outputBuffer = bb.finishCurrentSegment(); + outputPtr = 0; + } + outputBuffer[outputPtr++] = (byte) ch; + if (++inputPtr >= inputEnd) { + break main; + } + } + 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; + } + return _bytes.completeAndCoalesce(outputPtr); + } + + /** + * Will encode given String as UTF-8 (without any quoting), return + * resulting byte array. + */ + @SuppressWarnings("resource") + public byte[] encodeAsUTF8(String text) + { + ByteArrayBuilder byteBuilder = _bytes; + if (byteBuilder == null) { + // no allocator; can add if we must, shouldn't need to + _bytes = byteBuilder = new ByteArrayBuilder(null); + } + int inputPtr = 0; + int inputEnd = text.length(); + int outputPtr = 0; + byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment(); + int outputEnd = outputBuffer.length; + + main_loop: + while (inputPtr < inputEnd) { + int c = text.charAt(inputPtr++); + + // first tight loop for ascii + while (c <= 0x7F) { + if (outputPtr >= outputEnd) { + outputBuffer = byteBuilder.finishCurrentSegment(); + outputEnd = outputBuffer.length; + outputPtr = 0; + } + outputBuffer[outputPtr++] = (byte) c; + if (inputPtr >= inputEnd) { + break main_loop; + } + c = text.charAt(inputPtr++); + } + + // then multi-byte... + if (outputPtr >= outputEnd) { + outputBuffer = byteBuilder.finishCurrentSegment(); + outputEnd = outputBuffer.length; + outputPtr = 0; + } + if (c < 0x800) { // 2-byte + outputBuffer[outputPtr++] = (byte) (0xc0 | (c >> 6)); + } else { // 3 or 4 bytes + // Surrogates? + if (c < SURR1_FIRST || c > SURR2_LAST) { // nope + outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12)); + if (outputPtr >= outputEnd) { + outputBuffer = byteBuilder.finishCurrentSegment(); + outputEnd = outputBuffer.length; + outputPtr = 0; + } + outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + } else { // yes, surrogate pair + if (c > SURR1_LAST) { // must be from first range + _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 = byteBuilder.finishCurrentSegment(); + outputEnd = outputBuffer.length; + outputPtr = 0; + } + outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); + if (outputPtr >= outputEnd) { + outputBuffer = byteBuilder.finishCurrentSegment(); + outputEnd = outputBuffer.length; + outputPtr = 0; + } + outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + } + } + if (outputPtr >= outputEnd) { + outputBuffer = byteBuilder.finishCurrentSegment(); + outputEnd = outputBuffer.length; + outputPtr = 0; + } + outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + } + return _bytes.completeAndCoalesce(outputPtr); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + 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)); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/MergedStream.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/MergedStream.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/MergedStream.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,121 @@ +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); + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/NumberInput.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/NumberInput.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/NumberInput.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,307 @@ +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] - '0'; + + if (len > 4) { + num = (num * 10) + (ch[++off] - '0'); + num = (num * 10) + (ch[++off] - '0'); + num = (num * 10) + (ch[++off] - '0'); + num = (num * 10) + (ch[++off] - '0'); + len -= 4; + if (len > 4) { + num = (num * 10) + (ch[++off] - '0'); + num = (num * 10) + (ch[++off] - '0'); + num = (num * 10) + (ch[++off] - '0'); + num = (num * 10) + (ch[++off] - '0'); + return num; + } + } + if (len > 1) { + num = (num * 10) + (ch[++off] - '0'); + if (len > 2) { + num = (num * 10) + (ch[++off] - '0'); + if (len > 3) { + num = (num * 10) + (ch[++off] - '0'); + } + } + } + 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"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/NumberOutput.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/NumberOutput.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/NumberOutput.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,402 @@ +package com.fasterxml.jackson.core.io; + +public final class NumberOutput +{ + private final static char NC = (char) 0; + + private static int MILLION = 1000000; + private static int BILLION = 1000000000; + private static long TEN_BILLION_L = 10000000000L; + private static long THOUSAND_L = 1000L; + + private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE; + private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE; + + final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE); + + private final static char[] LEAD_3 = new char[4000]; + private final static char[] FULL_3 = new char[4000]; + static { + /* Let's fill it with NULLs for ignorable leading digits, + * and digit chars for others + */ + int ix = 0; + for (int i1 = 0; i1 < 10; ++i1) { + char f1 = (char) ('0' + i1); + char l1 = (i1 == 0) ? NC : f1; + for (int i2 = 0; i2 < 10; ++i2) { + char f2 = (char) ('0' + i2); + char l2 = (i1 == 0 && i2 == 0) ? NC : f2; + for (int i3 = 0; i3 < 10; ++i3) { + // Last is never to be empty + char f3 = (char) ('0' + i3); + LEAD_3[ix] = l1; + LEAD_3[ix+1] = l2; + LEAD_3[ix+2] = f3; + FULL_3[ix] = f1; + FULL_3[ix+1] = f2; + FULL_3[ix+2] = f3; + ix += 4; + } + } + } + } + + private final static byte[] FULL_TRIPLETS_B = new byte[4000]; + static { + for (int i = 0; i < 4000; ++i) { + FULL_TRIPLETS_B[i] = (byte) FULL_3[i]; + } + } + + private final static String[] sSmallIntStrs = new String[] { + "0","1","2","3","4","5","6","7","8","9","10" + }; + 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 outputLong((long) v, b, off); + } + b[off++] = '-'; + v = -v; + } + + if (v < MILLION) { // at most 2 triplets... + if (v < 1000) { + if (v < 10) { + b[off++] = (char) ('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; + } + + // ok, all 3 triplets included + /* Let's first hand possible billions separately before + * handling 3 triplets. This is possible since we know we + * can have at most '2' as billion count. + */ + boolean hasBillions = (v >= BILLION); + if (hasBillions) { + v -= BILLION; + if (v >= BILLION) { + v -= BILLION; + b[off++] = '2'; + } else { + b[off++] = '1'; + } + } + int newValue = v / 1000; + int ones = (v - (newValue * 1000)); // == value % 1000 + v = newValue; + newValue /= 1000; + int thousands = (v - (newValue * 1000)); + + // value now has millions, which have 1, 2 or 3 digits + if (hasBillions) { + off = full3(newValue, b, off); + } else { + off = leading3(newValue, b, off); + } + off = full3(thousands, b, off); + off = full3(ones, b, off); + return off; + } + + public static int outputInt(int v, byte[] b, int off) + { + if (v < 0) { + if (v == Integer.MIN_VALUE) { + return outputLong((long) v, 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; + } + boolean hasB = (v >= BILLION); + if (hasB) { + v -= BILLION; + if (v >= BILLION) { + v -= BILLION; + b[off++] = '2'; + } else { + b[off++] = '1'; + } + } + int newValue = v / 1000; + int ones = (v - (newValue * 1000)); // == value % 1000 + v = newValue; + newValue /= 1000; + int thousands = (v - (newValue * 1000)); + + if (hasB) { + off = full3(newValue, b, off); + } else { + off = leading3(newValue, b, off); + } + off = full3(thousands, b, off); + off = full3(ones, b, off); + return off; + } + + /** + * @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) { + /* MIN_INT is actually printed as long, just because its + * negation is not an int but long + */ + if (v > MIN_INT_AS_LONG) { + return outputInt((int) v, b, off); + } + if (v == Long.MIN_VALUE) { + // Special case: no matching positive value within range + int len = SMALLEST_LONG.length(); + SMALLEST_LONG.getChars(0, len, b, off); + return (off + len); + } + b[off++] = '-'; + v = -v; + } else { + if (v <= MAX_INT_AS_LONG) { + return outputInt((int) v, b, off); + } + } + + /* Ok: real long print. Need to first figure out length + * in characters, and then print in from end to beginning + */ + int origOffset = off; + off += calcLongStrLength(v); + int ptr = off; + + // First, with long arithmetics: + while (v > MAX_INT_AS_LONG) { // full triplet + ptr -= 3; + long newValue = v / THOUSAND_L; + int triplet = (int) (v - newValue * THOUSAND_L); + full3(triplet, b, ptr); + v = newValue; + } + // Then with int arithmetics: + int ivalue = (int) v; + while (ivalue >= 1000) { // still full triplet + ptr -= 3; + int newValue = ivalue / 1000; + int triplet = ivalue - (newValue * 1000); + full3(triplet, b, ptr); + ivalue = newValue; + } + // And finally, if anything remains, partial triplet + leading3(ivalue, b, origOffset); + + return off; + } + + 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) { + // Special case: no matching positive value within range + int len = SMALLEST_LONG.length(); + for (int i = 0; i < len; ++i) { + b[off++] = (byte) SMALLEST_LONG.charAt(i); + } + return off; + } + b[off++] = '-'; + v = -v; + } else { + if (v <= MAX_INT_AS_LONG) { + return outputInt((int) v, b, off); + } + } + int origOff = off; + off += calcLongStrLength(v); + int ptr = off; + + // First, with long arithmetics: + while (v > MAX_INT_AS_LONG) { // full triplet + ptr -= 3; + long newV = v / THOUSAND_L; + int t = (int) (v - newV * THOUSAND_L); + full3(t, b, ptr); + v = newV; + } + // Then with int arithmetics: + int ivalue = (int) v; + while (ivalue >= 1000) { // still full triplet + ptr -= 3; + int newV = ivalue / 1000; + int t = ivalue - (newV * 1000); + full3(t, b, ptr); + ivalue = newV; + } + leading3(ivalue, b, origOff); + return off; + } + + /* + /********************************************************** + /* Secondary convenience serialization methods + /********************************************************** + */ + + /* !!! 05-Aug-2008, tatus: Any ways to further optimize + * these? (or need: only called by diagnostics methods?) + */ + + public static String toString(int 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); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + private static int leading3(int t, char[] b, int off) + { + int digitOffset = (t << 2); + char c = LEAD_3[digitOffset++]; + if (c != NC) { + b[off++] = c; + } + c = LEAD_3[digitOffset++]; + if (c != NC) { + b[off++] = c; + } + // Last is required to be non-empty + b[off++] = LEAD_3[digitOffset]; + return off; + } + + private static int leading3(int t, byte[] b, int off) + { + int digitOffset = (t << 2); + char c = LEAD_3[digitOffset++]; + if (c != NC) { + b[off++] = (byte) c; + } + c = LEAD_3[digitOffset++]; + if (c != NC) { + b[off++] = (byte) c; + } + // Last is required to be non-empty + b[off++] = (byte) LEAD_3[digitOffset]; + return off; + } + + private static int full3(int t, char[] b, int off) + { + int digitOffset = (t << 2); + b[off++] = FULL_3[digitOffset++]; + b[off++] = FULL_3[digitOffset++]; + b[off++] = FULL_3[digitOffset]; + return off; + } + + private static int full3(int t, byte[] b, int off) + { + int digitOffset = (t << 2); + b[off++] = FULL_TRIPLETS_B[digitOffset++]; + b[off++] = FULL_TRIPLETS_B[digitOffset++]; + b[off++] = FULL_TRIPLETS_B[digitOffset]; + return off; + } + + /** + *

+ * Pre-conditions: c is positive, and larger than + * Integer.MAX_VALUE (about 2 billions). + */ + private static int calcLongStrLength(long v) + { + int len = 10; + long cmp = TEN_BILLION_L; + + // 19 is longest, need to worry about overflow + while (v >= cmp) { + if (len == 19) { + break; + } + ++len; + cmp = (cmp << 3) + (cmp << 1); // 10x + } + return len; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/OutputDecorator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/OutputDecorator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/OutputDecorator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,38 @@ +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; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/SegmentedStringWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/SegmentedStringWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/SegmentedStringWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,87 @@ +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 + * JsonGenerator. + */ +public final class SegmentedStringWriter extends Writer +{ + final protected TextBuffer _buffer; + + public SegmentedStringWriter(BufferRecycler br) { + super(); + _buffer = new TextBuffer(br); + } + + /* + /********************************************************** + /* java.io.Writer implementation + /********************************************************** + */ + + @Override + public Writer append(char c) { + write(c); + return this; + } + + @Override + public Writer append(CharSequence csq) { + String str = csq.toString(); + _buffer.append(str, 0, str.length()); + return this; + } + + @Override + public Writer append(CharSequence csq, int start, int end) { + String str = csq.subSequence(start, end).toString(); + _buffer.append(str, 0, str.length()); + return this; + } + + @Override public void close() { } // NOP + @Override public void flush() { } // NOP + + @Override + public void write(char[] cbuf) { _buffer.append(cbuf, 0, cbuf.length); } + + @Override + public void write(char[] cbuf, int off, int len) { _buffer.append(cbuf, off, len); } + + @Override + public void write(int c) { _buffer.append((char) c); } + + @Override + public void write(String str) { _buffer.append(str, 0, str.length()); } + + @Override + public void write(String str, int off, int len) { _buffer.append(str, off, len); } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Main access method that will construct a String that contains + * all the contents, release all internal buffers we may have, + * and return result String. + * Note that the method is not idempotent -- if called second time, + * will just return an empty String. + */ + public String getAndClear() { + String result = _buffer.contentsAsString(); + _buffer.releaseBuffers(); + return result; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/SerializedString.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/SerializedString.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/SerializedString.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,269 @@ +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. + *

+ * Class is final for performance reasons and since this is not designed to + * be extensible or customizable (customizations would occur in calling code) + */ +public class SerializedString + implements SerializableString, java.io.Serializable +{ + protected final String _value; + + /* 13-Dec-2010, tatu: Whether use volatile or not is actually an important + * decision for multi-core use cases. Cost of volatility can be non-trivial + * for heavy use cases, and serialized-string instances are accessed often. + * Given that all code paths with common Jackson usage patterns go through + * a few memory barriers (mostly with cache/reuse pool access) it seems safe + * enough to omit volatiles here, given how simple lazy initialization is. + * This can be compared to how {@link String#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(); } + + @Override + public final char[] asQuotedChars() { + char[] result = _quotedChars; + if (result == null) { + result = JsonStringEncoder.getInstance().quoteAsString(_value); + _quotedChars = result; + } + return result; + } + + /** + * Accessor for accessing value that has been quoted using JSON + * quoting rules, and encoded using UTF-8 encoding. + */ + @Override + public final byte[] asUnquotedUTF8() { + byte[] result = _unquotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); + _unquotedUTF8Ref = result; + } + return result; + } + + /** + * Accessor for accessing value as is (without JSON quoting) + * encoded using UTF-8 encoding. + */ + @Override + public final byte[] asQuotedUTF8() { + byte[] result = _quotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); + _quotedUTF8Ref = result; + } + return result; + } + + /* + /********************************************************** + /* Additional 2.0 methods for appending/writing contents + /********************************************************** + */ + + @Override + public int appendQuotedUTF8(byte[] buffer, int offset) { + byte[] result = _quotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); + _quotedUTF8Ref = result; + } + final int length = result.length; + if ((offset + length) > buffer.length) { + return -1; + } + System.arraycopy(result, 0, buffer, offset, length); + return length; + } + + @Override + public int appendQuoted(char[] buffer, int offset) { + char[] result = _quotedChars; + if (result == null) { + result = JsonStringEncoder.getInstance().quoteAsString(_value); + _quotedChars = result; + } + final int length = result.length; + if ((offset + length) > buffer.length) { + return -1; + } + System.arraycopy(result, 0, buffer, offset, length); + return length; + } + + @Override + public int appendUnquotedUTF8(byte[] buffer, int offset) { + byte[] result = _unquotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); + _unquotedUTF8Ref = result; + } + final int length = result.length; + if ((offset + length) > buffer.length) { + return -1; + } + System.arraycopy(result, 0, buffer, offset, length); + return length; + } + + @Override + public int appendUnquoted(char[] buffer, int offset) { + String str = _value; + final int length = str.length(); + if ((offset + length) > buffer.length) { + return -1; + } + str.getChars(0, length, buffer, offset); + return length; + } + + @Override + public int writeQuotedUTF8(OutputStream out) throws IOException { + byte[] result = _quotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); + _quotedUTF8Ref = result; + } + final int length = result.length; + out.write(result, 0, length); + return length; + } + + @Override + public int writeUnquotedUTF8(OutputStream out) throws IOException { + byte[] result = _unquotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); + _unquotedUTF8Ref = result; + } + final int length = result.length; + out.write(result, 0, length); + return length; + } + + @Override + public int putQuotedUTF8(ByteBuffer buffer) { + byte[] result = _quotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); + _quotedUTF8Ref = result; + } + final int length = result.length; + if (length > buffer.remaining()) { + return -1; + } + buffer.put(result, 0, length); + return length; + } + + @Override + public int putUnquotedUTF8(ByteBuffer buffer) { + byte[] result = _unquotedUTF8Ref; + if (result == null) { + result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); + _unquotedUTF8Ref = result; + } + final int length = result.length; + if (length > buffer.remaining()) { + return -1; + } + buffer.put(result, 0, length); + return length; + } + + + /* + /********************************************************** + /* Standard method overrides + /********************************************************** + */ + + @Override + public final String toString() { return _value; } + + @Override + public final int hashCode() { return _value.hashCode(); } + + @Override + public final boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + SerializedString other = (SerializedString) o; + return _value.equals(other._value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/UTF32Reader.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/UTF32Reader.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/UTF32Reader.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,268 @@ +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); + } + + len += start; + int outPtr = 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? + return -1; + } + } + } + + main_loop: + while (outPtr < len) { + int ptr = _ptr; + int ch; + + if (_bigEndian) { + ch = (_buffer[ptr] << 24) | ((_buffer[ptr+1] & 0xFF) << 16) + | ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF); + } else { + ch = (_buffer[ptr] & 0xFF) | ((_buffer[ptr+1] & 0xFF) << 8) + | ((_buffer[ptr+2] & 0xFF) << 16) | (_buffer[ptr+3] << 24); + } + _ptr += 4; + + // Does it need to be split to surrogates? + // (also, we can and need to verify illegal chars) + if (ch > 0xFFFF) { // need to split into surrogates? + if (ch > LAST_VALID_UNICODE_CHAR) { + reportInvalid(ch, outPtr-start, + "(above "+Integer.toHexString(LAST_VALID_UNICODE_CHAR)+") "); + } + ch -= 0x10000; // to normalize it starting with 0x0 + cbuf[outPtr++] = (char) (0xD800 + (ch >> 10)); + // hmmh. can this ever be 0? (not legal, at least?) + ch = (0xDC00 | (ch & 0x03FF)); + // Room for second part? + if (outPtr >= len) { // nope + _surrogate = (char) ch; + break main_loop; + } + } + cbuf[outPtr++] = (char) ch; + if (_ptr >= _length) { + break main_loop; + } + } + + len = outPtr - start; + _charCount += len; + return len; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + private void reportUnexpectedEOF(int gotBytes, int needed) throws IOException { + int bytePos = _byteCount + gotBytes, 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"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/UTF8Writer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/UTF8Writer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/io/UTF8Writer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,387 @@ +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"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,499 @@ +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 +{ + final static byte UTF8_BOM_1 = (byte) 0xEF; + final static byte UTF8_BOM_2 = (byte) 0xBB; + final static byte UTF8_BOM_3 = (byte) 0xBF; + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected final IOContext _context; + + protected final InputStream _in; + + /* + /********************************************************** + /* Input buffering + /********************************************************** + */ + + protected final byte[] _inputBuffer; + + private int _inputPtr; + + private int _inputEnd; + + /** + * Flag that indicates whether buffer above is to be recycled + * after being used or not. + */ + private final boolean _bufferRecyclable; + + /* + /********************************************************** + /* Input location + /********************************************************** + */ + + /** + * Current number of input units (bytes or chars) that were processed in + * previous blocks, + * before contents of current input buffer. + *

+ * Note: includes possible BOMs, if those were part of the input. + */ + protected int _inputProcessed; + + /* + /********************************************************** + /* Data gathered + /********************************************************** + */ + + protected boolean _bigEndian = true; + + protected int _bytesPerChar; // 0 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; + } + + /* + /********************************************************** + /* 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 + { + JsonEncoding enc = detectEncoding(); + + if (enc == JsonEncoding.UTF8) { + /* and without canonicalization, byte-based approach is not performance; just use std UTF-8 reader + * (which is ok for larger input; not so hot for smaller; but this is not a common case) + */ + if (JsonFactory.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(factoryFeatures)) { + ByteQuadsCanonicalizer can = rootByteSymbols.makeChild(factoryFeatures); + return new UTF8StreamJsonParser(_context, parserFeatures, _in, codec, can, + _inputBuffer, _inputPtr, _inputEnd, _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(); + ch = (int) b & 0xFF; + } + } + + /* + /********************************************************** + /* Internal methods, parsing + /********************************************************** + */ + + /** + * @return True if a BOM was succesfully found, and encoding + * thereby recognized. + */ + private boolean handleBOM(int quad) throws IOException + { + /* Handling of (usually) optional BOM (required for + * multi-byte formats); first 32-bit charsets: + */ + switch (quad) { + case 0x0000FEFF: + _bigEndian = true; + _inputPtr += 4; + _bytesPerChar = 4; + return true; + case 0xFFFE0000: // UCS-4, LE? + _inputPtr += 4; + _bytesPerChar = 4; + _bigEndian = false; + return true; + case 0x0000FFFE: // UCS-4, in-order... + reportWeirdUCS4("2143"); // throws exception + case 0xFEFF0000: // UCS-4, in-order... + reportWeirdUCS4("3412"); // throws exception + } + // Ok, if not, how about 16-bit encoding BOMs? + int msw = quad >>> 16; + if (msw == 0xFEFF) { // UTF-16, BE + _inputPtr += 2; + _bytesPerChar = 2; + _bigEndian = true; + return true; + } + if (msw == 0xFFFE) { // UTF-16, LE + _inputPtr += 2; + _bytesPerChar = 2; + _bigEndian = false; + return true; + } + // And if not, then UTF-8 BOM? + if ((quad >>> 8) == 0xEFBBBF) { // UTF-8 + _inputPtr += 3; + _bytesPerChar = 1; + _bigEndian = true; // doesn't really matter + return true; + } + return false; + } + + private boolean checkUTF32(int quad) throws IOException + { + /* Handling of (usually) optional BOM (required for + * multi-byte formats); first 32-bit charsets: + */ + if ((quad >> 8) == 0) { // 0x000000?? -> UTF32-BE + _bigEndian = true; + } else if ((quad & 0x00FFFFFF) == 0) { // 0x??000000 -> UTF32-LE + _bigEndian = false; + } else if ((quad & ~0x00FF0000) == 0) { // 0x00??0000 -> UTF32-in-order + reportWeirdUCS4("3412"); + } else if ((quad & ~0x0000FF00) == 0) { // 0x0000??00 -> UTF32-in-order + reportWeirdUCS4("2143"); + } else { + // Can not be valid UTF-32 encoded JSON... + return false; + } + // Not BOM (just regular content), nothing to skip past: + //_inputPtr += 4; + _bytesPerChar = 4; + return true; + } + + private boolean checkUTF16(int i16) + { + if ((i16 & 0xFF00) == 0) { // UTF-16BE + _bigEndian = true; + } else if ((i16 & 0x00FF) == 0) { // UTF-16LE + _bigEndian = false; + } else { // nope, not UTF-16 + return false; + } + // Not BOM (just regular content), nothing to skip past: + //_inputPtr += 2; + _bytesPerChar = 2; + return true; + } + + /* + /********************************************************** + /* Internal methods, problem reporting + /********************************************************** + */ + + private void reportWeirdUCS4(String type) throws IOException { + throw new CharConversionException("Unsupported UCS-4 endianness ("+type+") detected"); + } + + /* + /********************************************************** + /* Internal methods, raw input access + /********************************************************** + */ + + protected boolean ensureLoaded(int minimum) throws IOException { + /* Let's assume here buffer has enough room -- this will always + * be true for the limited used this method gets + */ + int gotten = (_inputEnd - _inputPtr); + while (gotten < minimum) { + int count; + + if (_in == null) { // block source + count = -1; + } else { + count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); + } + if (count < 1) { + return false; + } + _inputEnd += count; + gotten += count; + } + return true; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/DupDetector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/DupDetector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/DupDetector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,97 @@ +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 _seen; + + private DupDetector(Object src) { + _source = src; + } + + public static DupDetector rootDetector(JsonParser p) { + return new DupDetector(p); + } + + public static DupDetector rootDetector(JsonGenerator g) { + return new DupDetector(g); + } + + public DupDetector child() { + return new DupDetector(_source); + } + + public void reset() { + _firstName = null; + _secondName = null; + _seen = null; + } + + public JsonLocation findLocation() { + // ugly but: + if (_source instanceof JsonParser) { + return ((JsonParser)_source).getCurrentLocation(); + } + // do generators have a way to provide Location? Apparently not... + return null; + } + + /** + * @since 2.7 + */ + public Object getSource() { + return _source; + } + + public boolean isDup(String name) throws JsonParseException + { + if (_firstName == null) { + _firstName = name; + return false; + } + if (name.equals(_firstName)) { + return true; + } + if (_secondName == null) { + _secondName = name; + return false; + } + if (name.equals(_secondName)) { + return true; + } + if (_seen == null) { + _seen = new HashSet(16); // 16 is default, seems reasonable + _seen.add(_firstName); + _seen.add(_secondName); + } + return !_seen.add(name); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,205 @@ +package com.fasterxml.jackson.core.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.base.GeneratorBase; +import com.fasterxml.jackson.core.io.CharTypes; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.io.IOContext; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.core.util.VersionUtil; + +/** + * Intermediate base class shared by JSON-backed generators + * like {@link UTF8JsonGenerator} and {@link WriterBasedJsonGenerator}. + * + * @since 2.1 + */ +public abstract class JsonGeneratorImpl extends GeneratorBase +{ + /* + /********************************************************** + /* Constants + /********************************************************** + */ + + /** + * This is the default set of escape codes, over 7-bit ASCII range + * (first 128 character codes), used for single-byte UTF-8 characters. + */ + protected final static int[] sOutputEscapes = CharTypes.get7BitOutputEscapes(); + + /* + /********************************************************** + /* Configuration, basic I/O + /********************************************************** + */ + + final protected IOContext _ioContext; + + /* + /********************************************************** + /* Configuration, output escaping + /********************************************************** + */ + + /** + * Currently active set of output escape code definitions (whether + * and how to escape or not) for 7-bit ASCII range (first 128 + * character codes). Defined separately to make potentially + * customizable + */ + protected int[] _outputEscapes = sOutputEscapes; + + /** + * Value between 128 (0x80) and 65535 (0xFFFF) that indicates highest + * Unicode code point that will not need escaping; or 0 to indicate + * that all characters can be represented without escaping. + * Typically used to force escaping of some portion of character set; + * for example to always escape non-ASCII characters (if value was 127). + *

+ * NOTE: not all sub-classes make use of this setting. + */ + protected int _maximumNonEscapedChar; + + /** + * Definition of custom character escapes to use for generators created + * by this factory, if any. If null, standard data format specific + * escapes are used. + */ + protected CharacterEscapes _characterEscapes; + + /* + /********************************************************** + /* Configuration, other + /********************************************************** + */ + + /** + * Separator to use, if any, between root-level values. + * + * @since 2.1 + */ + protected SerializableString _rootValueSeparator + = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; + + /** + * Flag that is set if quoting is not to be added around + * JSON Object property names. + * + * @since 2.7 + */ + protected boolean _cfgUnqNames; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + 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); + } + + /* + /********************************************************** + /* Overridden configuration methods + /********************************************************** + */ + + @Override + public JsonGenerator enable(Feature f) { + super.enable(f); + if (f == Feature.QUOTE_FIELD_NAMES) { + _cfgUnqNames = false; + } + return this; + } + + @Override + public JsonGenerator disable(Feature f) { + super.disable(f); + if (f == Feature.QUOTE_FIELD_NAMES) { + _cfgUnqNames = true; + } + return this; + } + + @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; + } + + /* + /********************************************************** + /* Versioned + /********************************************************** + */ + + @Override + public Version version() { + return VersionUtil.versionFor(getClass()); + } + + /* + /********************************************************** + /* Partial API + /********************************************************** + */ + + // // Overrides just to make things final, to possibly help with inlining + + @Override + public final void writeStringField(String fieldName, String value) throws IOException + { + writeFieldName(fieldName); + writeString(value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonReadContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonReadContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonReadContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,245 @@ +package com.fasterxml.jackson.core.json; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharTypes; + +/** + * Extension of {@link JsonStreamContext}, which implements + * core methods needed, and also exposes + * more complete API to parser implementation classes. + */ +public final class JsonReadContext extends JsonStreamContext +{ + // // // Configuration + + /** + * 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 jp) { + _dups = DupDetector.rootDetector(jp); + } + */ + + 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 implementation + /********************************************************** + */ + + @Override public String getCurrentName() { return _currentName; } + @Override public JsonReadContext getParent() { return _parent; } + + /** + * 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; + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * @return Location pointing to the point where the context + * start marker was found + */ + public JsonLocation getStartLocation(Object srcRef) { + // We don't keep track of offsets at this level (only reader does) + long totalChars = -1L; + return new JsonLocation(srcRef, totalChars, _lineNr, _columnNr); + } + + 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 JsonGenerator) ? ((JsonParser) src) : null), + "Duplicate field '"+name+"'"); + } + } + + /* + /********************************************************** + /* Overridden standard methods + /********************************************************** + */ + + /** + * Overridden to provide developer readable "JsonPath" representation + * of the context. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + switch (_type) { + case TYPE_ROOT: + sb.append("/"); + break; + case TYPE_ARRAY: + sb.append('['); + sb.append(getCurrentIndex()); + sb.append(']'); + break; + case TYPE_OBJECT: + sb.append('{'); + if (_currentName != null) { + sb.append('"'); + CharTypes.appendQuoted(sb, _currentName); + sb.append('"'); + } else { + sb.append('?'); + } + sb.append('}'); + break; + } + return sb.toString(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonWriteContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonWriteContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/JsonWriteContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,241 @@ +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 parsed; only + * used for OBJECT contexts + */ + protected String _currentName; + + /** + * @since 2.5 + */ + protected Object _currentValue; + + /** + * Marker used to indicate that we just received a name, and + * now expect a value + */ + protected boolean _gotName; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups) { + super(); + _type = type; + _parent = parent; + _dups = dups; + _index = -1; + } + + protected JsonWriteContext reset(int type) { + _type = type; + _index = -1; + _currentName = null; + _gotName = false; + _currentValue = null; + 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); + } + + 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); + } + + @Override public final JsonWriteContext getParent() { return _parent; } + @Override public final String getCurrentName() { return _currentName; } + + /** + * 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 (_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; + } + + // // // Internally used abstract methods + + protected void appendDesc(StringBuilder sb) { + if (_type == TYPE_OBJECT) { + sb.append('{'); + if (_currentName != null) { + sb.append('"'); + // !!! TODO: Name chars should be escaped? + sb.append(_currentName); + sb.append('"'); + } else { + sb.append('?'); + } + sb.append('}'); + } else if (_type == TYPE_ARRAY) { + sb.append('['); + sb.append(getCurrentIndex()); + sb.append(']'); + } else { + // nah, ROOT: + sb.append("/"); + } + } + + // // // Overridden standard methods + + /** + * Overridden to provide developer writeable "JsonPath" representation + * of the context. + */ + @Override public String toString() { + StringBuilder sb = new StringBuilder(64); + appendDesc(sb); + return sb.toString(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/PackageVersion.java.in =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/PackageVersion.java.in (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/PackageVersion.java.in (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,20 @@ +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; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,2756 @@ +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 +{ + // 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; } + + @Override + protected boolean loadMore() throws IOException + { + final int bufSize = _inputEnd; + + _currInputProcessed += bufSize; + _currInputRowStart -= bufSize; + + // 26-Nov-2015, tatu: Since name-offset requires it too, must offset + // this increase to avoid "moving" name-offset, resulting most likely + // in negative value, which is fine as combine value remains unchanged. + _nameStartOffset -= bufSize; + + if (_reader != null) { + int count = _reader.read(_inputBuffer, 0, _inputBuffer.length); + if (count > 0) { + _inputPtr = 0; + _inputEnd = count; + return true; + } + // End of input + _closeInput(); + // Should never return 0, so let's fail + if (count == 0) { + throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd); + } + } + return false; + } + + protected char getNextChar(String eofMsg) throws IOException { + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { _reportInvalidEOF(eofMsg); } + } + return _inputBuffer[_inputPtr++]; + } + + @Override + protected void _closeInput() throws IOException { + /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() + * on the underlying Reader, unless we "own" it, or auto-closing + * feature is enabled. + * One downside is that when using our optimized + * Reader (granted, we only do that for UTF-32...) this + * means that buffer recycling won't work correctly. + */ + if (_reader != null) { + if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) { + _reader.close(); + } + _reader = null; + } + } + + /** + * Method called to release internal buffers owned by the base + * reader. This may be called along with {@link #_closeInput} (for + * example, when explicitly closing this reader instance), or + * separately (if need be). + */ + @Override + protected void _releaseBuffers() throws IOException { + super._releaseBuffers(); + // 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); + } + } + } + + /* + /********************************************************** + /* 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); + } + + // // // 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_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 + char ch; + do { + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + } while (ch <= INT_SPACE); + int bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { // reached the end, fair and square? + if (ch == '"') { + break; + } + bits = _decodeBase64Escape(b64variant, ch, 0); + if (bits < 0) { // white space to skip + continue; + } + } + + // enough room? If not, flush + if (outputPtr > outputEnd) { + outputCount += outputPtr; + out.write(buffer, 0, outputPtr); + outputPtr = 0; + } + + int decodedData = bits; + + // then second base64 char; can't get padding yet, nor ws + + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + bits = _decodeBase64Escape(b64variant, ch, 1); + } + decodedData = (decodedData << 6) | bits; + + // third base64 char; can be padding, but not ws + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + bits = b64variant.decodeBase64Char(ch); + + // First branch: can get padding (-> 1 byte) + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + // as per [JACKSON-631], could also just be 'missing' padding + if (ch == '"' && !b64variant.usesPadding()) { + decodedData >>= 4; + buffer[outputPtr++] = (byte) decodedData; + break; + } + bits = _decodeBase64Escape(b64variant, ch, 2); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + // Ok, must get padding + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + if (!b64variant.usesPaddingChar(ch)) { + throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'"); + } + // Got 12 bits, only need 8, need to shift + decodedData >>= 4; + buffer[outputPtr++] = (byte) decodedData; + continue; + } + } + // Nope, 2 or 3 bytes + decodedData = (decodedData << 6) | bits; + // fourth and last base64 char; can be padding, but not ws + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + // as per [JACKSON-631], could also just be 'missing' padding + if (ch == '"' && !b64variant.usesPadding()) { + decodedData >>= 2; + buffer[outputPtr++] = (byte) (decodedData >> 8); + buffer[outputPtr++] = (byte) decodedData; + break; + } + bits = _decodeBase64Escape(b64variant, ch, 3); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + /* With padding we only get 2 bytes; but we have + * to shift it a bit so it is identical to triplet + * case with partial output. + * 3 chars gives 3x6 == 18 bits, of which 2 are + * dummies, need to discard: + */ + decodedData >>= 2; + buffer[outputPtr++] = (byte) (decodedData >> 8); + buffer[outputPtr++] = (byte) decodedData; + continue; + } + } + // otherwise, our triplet is now complete + decodedData = (decodedData << 6) | bits; + buffer[outputPtr++] = (byte) (decodedData >> 16); + buffer[outputPtr++] = (byte) (decodedData >> 8); + buffer[outputPtr++] = (byte) decodedData; + } + _tokenIncomplete = false; + if (outputPtr > 0) { + outputCount += outputPtr; + out.write(buffer, 0, outputPtr); + } + return outputCount; + } + + /* + /********************************************************** + /* Public API, traversal + /********************************************************** + */ + + /** + * @return Next token from the stream, if any found, or null + * to indicate end-of-input + */ + @Override + public 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 + /* 19-Feb-2009, tatu: 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) { + _updateLocation(); + if (!_parsingContext.inArray()) { + _reportMismatchedEndMarker(i, '}'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + return (_currToken = JsonToken.END_ARRAY); + } + if (i == INT_RCURLY) { + _updateLocation(); + if (!_parsingContext.inObject()) { + _reportMismatchedEndMarker(i, ']'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + return (_currToken = JsonToken.END_OBJECT); + } + + // Nope: do we then expect a comma? + if (_parsingContext.expectComma()) { + i = _skipComma(i); + } + + /* 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 ']': + case '}': + // Error: neither is 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); + } + + /* + /********************************************************** + /* 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; + + if (i == INT_RBRACKET) { + _updateLocation(); + if (!_parsingContext.inArray()) { + _reportMismatchedEndMarker(i, '}'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_ARRAY; + return false; + } + if (i == INT_RCURLY) { + _updateLocation(); + if (!_parsingContext.inObject()) { + _reportMismatchedEndMarker(i, ']'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_OBJECT; + return false; + } + if (_parsingContext.expectComma()) { + i = _skipComma(i); + } + + 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) { + _updateLocation(); + if (!_parsingContext.inArray()) { + _reportMismatchedEndMarker(i, '}'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_ARRAY; + return null; + } + if (i == INT_RCURLY) { + _updateLocation(); + if (!_parsingContext.inObject()) { + _reportMismatchedEndMarker(i, ']'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_OBJECT; + return null; + } + if (_parsingContext.expectComma()) { + i = _skipComma(i); + } + 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)); + } + 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"); + 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 + 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 (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { + reportInvalidNumber("Leading zeroes not allowed"); + } + // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) + ++_inputPtr; // Leading zero to be skipped + if (ch == INT_0) { + while (_inputPtr < _inputEnd || loadMore()) { + ch = _inputBuffer[_inputPtr]; + if (ch < '0' || ch > '9') { // followed by non-number; retain one zero + return '0'; + } + ++_inputPtr; // skip previous zero + if (ch != '0') { // followed by other number; return + break; + } + } + } + return ch; + } + + /** + * Method called if expected numeric value (due to leading sign) does not + * look like a number + */ + protected JsonToken _handleInvalidNumberStart(int ch, boolean negative) throws IOException + { + if (ch == 'I') { + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { _reportInvalidEOFInValue(); } + } + ch = _inputBuffer[_inputPtr++]; + if (ch == 'N') { + String match = negative ? "-INF" :"+INF"; + _matchToken(match, 3); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); + } + _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + } else if (ch == 'n') { + String match = negative ? "-Infinity" :"+Infinity"; + _matchToken(match, 3); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); + } + _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + } + } + reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); + return null; + } + + /** + * 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(": was expecting closing '"+((char) endChar)+"' for name"); + } + } + char c = _inputBuffer[_inputPtr++]; + int i = (int) c; + if (i <= INT_BACKSLASH) { + if (i == INT_BACKSLASH) { + /* Although chars outside of BMP are to be escaped as + * an UTF-16 surrogate pair, does that affect decoding? + * For now let's assume it does not. + */ + c = _decodeEscaped(); + } else if (i <= endChar) { + if (i == endChar) { + break; + } + if (i < INT_SPACE) { + _throwUnquotedSpace(i, "name"); + } + } + } + hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + 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 == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { + return _parseAposName(); + } + // [JACKSON-69]: allow unquoted names if feature enabled: + if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { + _reportUnexpectedChar(i, "was expecting double-quote to start field name"); + } + final int[] codes = CharTypes.getInputCodeLatin1JsNames(); + final int maxCode = codes.length; + + // Also: first char must be a valid name char, but NOT be number + boolean firstOk; + + if (i < maxCode) { // identifier, 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 '\'': + /* [JACKSON-173]: allow single quotes. Unlike with regular + * Strings, we'll eagerly parse contents; this so that there's + * no need to store information on quote char used. + * + * Also, no separation to fast/slow parsing; we'll just do + * one regular (~= slowish) parsing, to keep code simple + */ + if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { + return _handleApos(); + } + break; + case 'N': + _matchToken("NaN", 1); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN("NaN", Double.NaN); + } + _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + break; + case 'I': + _matchToken("Infinity", 1); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN("Infinity", Double.POSITIVE_INFINITY); + } + _reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + break; + case '+': // note: '-' is taken as number + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidEOFInValue(); + } + } + return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false); + } + // [Issue#77] Try to decode most likely token + if (Character.isJavaIdentifierStart(i)) { + _reportInvalidToken(""+((char) i), "('true', 'false' or 'null')"); + } + // but if it doesn't look like a token: + _reportUnexpectedChar(i, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); + 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"); + } + } + 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"); + } + } + 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"); + } + 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 (true) { + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + 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) { + if (i < INT_SPACE) { + _throwInvalidSpace(i); + } + _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); + } + } + } + } + + // 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.getTypeDesc()+" 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.getTypeDesc()+" 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 (!isEnabled(Feature.ALLOW_COMMENTS)) { + _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); + } + // First: check which comment (if either) it is: + if (_inputPtr >= _inputEnd && !loadMore()) { + _reportInvalidEOF(" in a comment"); + } + char c = _inputBuffer[_inputPtr++]; + if (c == '/') { + _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"); + } + + private boolean _skipYAMLComment() throws IOException + { + if (!isEnabled(Feature.ALLOW_YAML_COMMENTS)) { + 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"); + } + } + 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"); + } + } + 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(); + + do { + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidToken(matchStr.substring(0, i)); + } + } + if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) { + _reportInvalidToken(matchStr.substring(0, i)); + } + ++_inputPtr; + } while (++i < len); + + // but let's also ensure we either get EOF, or non-alphanum char... + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + return; + } + } + char c = _inputBuffer[_inputPtr]; + if (c < '0' || c == ']' || c == '}') { // expected/allowed chars + return; + } + // if Java letter, it's a problem tho + if (Character.isJavaIdentifierPart(c)) { + _reportInvalidToken(matchStr.substring(0, i)); + } + return; + } + + /* + /********************************************************** + /* Binary access + /********************************************************** + */ + + /** + * Efficient handling for incremental parsing of base64-encoded + * textual content. + */ + @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 == '"' && !b64variant.usesPadding()) { + decodedData >>= 4; + builder.append(decodedData); + return builder.toByteArray(); + } + bits = _decodeBase64Escape(b64variant, ch, 2); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + // Ok, must get more padding chars, then + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + if (!b64variant.usesPaddingChar(ch)) { + throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'"); + } + // Got 12 bits, only need 8, need to shift + decodedData >>= 4; + builder.append(decodedData); + continue; + } + // otherwise we got escaped other char, to be processed below + } + // Nope, 2 or 3 bytes + decodedData = (decodedData << 6) | bits; + // fourth and last base64 char; can be padding, but not ws + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++]; + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + // as per [JACKSON-631], could also just be 'missing' padding + if (ch == '"' && !b64variant.usesPadding()) { + decodedData >>= 2; + builder.appendTwoBytes(decodedData); + return builder.toByteArray(); + } + bits = _decodeBase64Escape(b64variant, ch, 3); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + // With padding we only get 2 bytes; but we have + // to shift it a bit so it is identical to triplet + // case with partial output. + // 3 chars gives 3x6 == 18 bits, of which 2 are + // dummies, need to discard: + decodedData >>= 2; + builder.appendTwoBytes(decodedData); + continue; + } + // otherwise we got escaped other char, to be processed below + } + // otherwise, our triplet is now complete + decodedData = (decodedData << 6) | bits; + builder.appendThreeBytes(decodedData); + } + } + + /* + /********************************************************** + /* Internal methods, location updating (refactored in 2.7) + /********************************************************** + */ + + @Override + public JsonLocation getTokenLocation() + { + final Object src = _ioContext.getSourceReference(); + if (_currToken == JsonToken.FIELD_NAME) { + long total = _currInputProcessed + (_nameStartOffset-1); + return new JsonLocation(src, + -1L, total, _nameStartRow, _nameStartCol); + } + return new JsonLocation(src, + -1L, _tokenInputTotal-1, _tokenInputRow, _tokenInputCol); + } + + @Override + public JsonLocation getCurrentLocation() { + int col = _inputPtr - _currInputRowStart + 1; // 1-based + return new JsonLocation(_ioContext.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, "'null', 'true', 'false' or NaN"); + } + + protected void _reportInvalidToken(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. + */ + while (true) { + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + break; + } + } + char c = _inputBuffer[_inputPtr]; + if (!Character.isJavaIdentifierPart(c)) { + break; + } + ++_inputPtr; + sb.append(c); + } + _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1985 @@ +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.*; + +public class UTF8JsonGenerator + extends JsonGeneratorImpl +{ + private final static byte BYTE_u = (byte) 'u'; + + private final static byte BYTE_0 = (byte) '0'; + + private final static byte BYTE_LBRACKET = (byte) '['; + private final static byte BYTE_RBRACKET = (byte) ']'; + private final static byte BYTE_LCURLY = (byte) '{'; + private final static byte BYTE_RCURLY = (byte) '}'; + + private final static byte BYTE_BACKSLASH = (byte) '\\'; + private final static byte BYTE_COMMA = (byte) ','; + private final static byte BYTE_COLON = (byte) ':'; + private final static byte BYTE_QUOTE = (byte) '"'; + + // 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' }; + + /* + /********************************************************** + /* Output buffering + /********************************************************** + */ + + /** + * Underlying output stream used for writing JSON content. + */ + final protected OutputStream _outputStream; + + /** + * Intermediate buffer in which contents are buffered before + * being written using {@link #_outputStream}. + */ + protected byte[] _outputBuffer; + + /** + * Pointer to the position right beyond the last character to output + * (end marker; may be past the buffer) + */ + protected int _outputTail; + + /** + * End marker of the output buffer; one past the last valid position + * within the buffer. + */ + protected final int _outputEnd; + + /** + * Maximum number of chars that we know will always fit + * in the output buffer after escaping + */ + protected final int _outputMaxContiguous; + + /** + * Intermediate buffer in which characters of a String are copied + * before being encoded. + */ + protected char[] _charBuffer; + + /** + * Length of _charBuffer + */ + protected final int _charBufferLength; + + /** + * 6 character temporary buffer allocated if needed, for constructing + * escape sequences + */ + protected byte[] _entityBuffer; + + /** + * Flag that indicates whether the output buffer is recycable (and + * needs to be returned to recycler once we are done) or not. + */ + protected boolean _bufferRecyclable; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, + OutputStream out) + { + super(ctxt, features, codec); + _outputStream = out; + _bufferRecyclable = true; + _outputBuffer = ctxt.allocWriteEncodingBuffer(); + _outputEnd = _outputBuffer.length; + + /* To be exact, each char can take up to 6 bytes when escaped (Unicode + * escape with backslash, 'u' and 4 hex digits); but to avoid fluctuation, + * we will actually round down to only do up to 1/8 number of chars + */ + _outputMaxContiguous = _outputEnd >> 3; + _charBuffer = ctxt.allocConcatBuffer(); + _charBufferLength = _charBuffer.length; + + // By default we use this feature to determine additional quoting + if (isEnabled(Feature.ESCAPE_NON_ASCII)) { + setHighestNonEscapedChar(127); + } + } + + public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, + OutputStream out, + byte[] outputBuffer, int outputOffset, boolean bufferRecyclable) + { + + super(ctxt, features, codec); + _outputStream = out; + _bufferRecyclable = bufferRecyclable; + _outputTail = outputOffset; + _outputBuffer = outputBuffer; + _outputEnd = _outputBuffer.length; + // up to 6 bytes per char (see above), rounded up to 1/8 + _outputMaxContiguous = (_outputEnd >> 3); + _charBuffer = ctxt.allocConcatBuffer(); + _charBufferLength = _charBuffer.length; + } + + /* + /********************************************************** + /* Overridden configuration methods + /********************************************************** + */ + + @Override + public Object getOutputTarget() { + return _outputStream; + } + + @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++] = BYTE_QUOTE; + // 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++] = BYTE_QUOTE; + } + + @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++] = BYTE_QUOTE; + int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); + if (len < 0) { // couldn't append, bit longer processing + _writeBytes(name.asQuotedUTF8()); + } else { + _outputTail += len; + } + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + 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 + public final void writeEndArray() throws IOException + { + if (!_writeContext.inArray()) { + _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc()); + } + if (_cfgPrettyPrinter != null) { + _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_RBRACKET; + } + _writeContext = _writeContext.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 + public final void writeEndObject() throws IOException + { + if (!_writeContext.inObject()) { + _reportError("Current context not an object but "+_writeContext.getTypeDesc()); + } + if (_cfgPrettyPrinter != null) { + _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_RCURLY; + } + _writeContext = _writeContext.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++] = BYTE_QUOTE; + 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++] = BYTE_QUOTE; + } + + 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++] = BYTE_QUOTE; + } + _writeBytes(name.asQuotedUTF8()); + if (addQuotes) { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + } + + /* + /********************************************************** + /* Output method implementations, textual + /********************************************************** + */ + + @Override + public void writeString(String text) throws IOException + { + _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++] = BYTE_QUOTE; + _writeStringSegment(text, 0, len); // we checked space already above + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @Override + public void writeString(char[] text, int offset, int len) throws IOException + { + _verifyValueWrite(WRITE_STRING); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + // One or multiple segments? + if (len <= _outputMaxContiguous) { + if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space + _flushBuffer(); + } + _writeStringSegment(text, offset, len); + } else { + _writeStringSegments(text, offset, len); + } + // And finally, closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @Override + public final void writeString(SerializableString text) throws IOException + { + _verifyValueWrite(WRITE_STRING); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + int len = text.appendQuotedUTF8(_outputBuffer, _outputTail); + if (len < 0) { + _writeBytes(text.asQuotedUTF8()); + } else { + _outputTail += len; + } + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @Override + public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException + { + _verifyValueWrite(WRITE_STRING); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeBytes(text, offset, length); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @Override + public void writeUTF8String(byte[] text, int offset, int len) throws IOException + { + _verifyValueWrite(WRITE_STRING); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + // One or multiple segments? + if (len <= _outputMaxContiguous) { + _writeUTF8Segment(text, offset, len); + } else { + _writeUTF8Segments(text, offset, len); + } + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + /* + /********************************************************** + /* Output method implementations, unprocessed ("raw") + /********************************************************** + */ + + @Override + public void writeRaw(String text) + throws IOException, JsonGenerationException + { + int start = 0; + int len = text.length(); + while (len > 0) { + char[] buf = _charBuffer; + final int blen = buf.length; + final int len2 = (len < blen) ? len : blen; + text.getChars(start, start+len2, buf, 0); + writeRaw(buf, 0, len2); + start += len2; + len -= len2; + } + } + + @Override + public void writeRaw(String text, int offset, int len) + throws IOException, JsonGenerationException + { + while (len > 0) { + char[] buf = _charBuffer; + final int blen = buf.length; + final int len2 = (len < blen) ? len : blen; + text.getChars(offset, offset+len2, buf, 0); + writeRaw(buf, 0, len2); + offset += len2; + len -= len2; + } + } + + @Override + public void writeRaw(SerializableString text) throws IOException, JsonGenerationException + { + byte[] raw = text.asUnquotedUTF8(); + if (raw.length > 0) { + _writeBytes(raw); + } + } + + // since 2.5 + @Override + public void writeRawValue(SerializableString text) throws IOException { + _verifyValueWrite(WRITE_RAW); + byte[] raw = text.asUnquotedUTF8(); + if (raw.length > 0) { + _writeBytes(raw); + } + } + + // @TODO: rewrite for speed... + @Override + public final void writeRaw(char[] cbuf, int offset, int len) + throws IOException, JsonGenerationException + { + // First: if we have 3 x charCount spaces, we know it'll fit just fine + { + int len3 = len+len+len; + if ((_outputTail + len3) > _outputEnd) { + // maybe we could flush? + if (_outputEnd < len3) { // wouldn't be enough... + _writeSegmentedRaw(cbuf, offset, len); + return; + } + // yes, flushing brings enough space + _flushBuffer(); + } + } + len += offset; // now marks the end + + // Note: here we know there is enough room, hence no output boundary checks + main_loop: + while (offset < len) { + inner_loop: + while (true) { + int ch = (int) cbuf[offset]; + if (ch > 0x7F) { + break inner_loop; + } + _outputBuffer[_outputTail++] = (byte) ch; + if (++offset >= len) { + break main_loop; + } + } + char ch = cbuf[offset++]; + if (ch < 0x800) { // 2-byte? + _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6)); + _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); + } else { + offset = _outputRawMultiByteChar(ch, cbuf, offset, len); + } + } + } + + @Override + public void writeRaw(char ch) + throws IOException, JsonGenerationException + { + if ((_outputTail + 3) >= _outputEnd) { + _flushBuffer(); + } + final byte[] bbuf = _outputBuffer; + if (ch <= 0x7F) { + bbuf[_outputTail++] = (byte) ch; + } else if (ch < 0x800) { // 2-byte? + bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6)); + bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); + } else { + /*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, JsonGenerationException + { + final int end = _outputEnd; + final byte[] bbuf = _outputBuffer; + + main_loop: + while (offset < len) { + inner_loop: + while (true) { + int ch = (int) cbuf[offset]; + if (ch >= 0x80) { + break inner_loop; + } + // !!! TODO: fast(er) writes (roll input, output checks in one) + if (_outputTail >= end) { + _flushBuffer(); + } + bbuf[_outputTail++] = (byte) ch; + if (++offset >= len) { + break main_loop; + } + } + if ((_outputTail + 3) >= _outputEnd) { + _flushBuffer(); + } + char ch = cbuf[offset++]; + if (ch < 0x800) { // 2-byte? + bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6)); + bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); + } else { + offset = _outputRawMultiByteChar(ch, cbuf, offset, len); + } + } + } + + /* + /********************************************************** + /* Output method implementations, base64-encoded binary + /********************************************************** + */ + + @Override + public void writeBinary(Base64Variant b64variant, + byte[] data, int offset, int len) + throws IOException, JsonGenerationException + { + _verifyValueWrite(WRITE_BINARY); + // Starting quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + _writeBinary(b64variant, data, offset, offset+len); + // and closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @Override + public int writeBinary(Base64Variant b64variant, + InputStream data, int dataLength) + throws IOException, JsonGenerationException + { + _verifyValueWrite(WRITE_BINARY); + // Starting quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + byte[] encodingBuffer = _ioContext.allocBase64Buffer(); + int bytes; + try { + if (dataLength < 0) { // length unknown + bytes = _writeBinary(b64variant, data, encodingBuffer); + } else { + int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength); + if (missing > 0) { + _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")"); + } + bytes = dataLength; + } + } finally { + _ioContext.releaseBase64Buffer(encodingBuffer); + } + // and closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + return bytes; + } + + /* + /********************************************************** + /* Output method implementations, primitive + /********************************************************** + */ + + @Override + public void writeNumber(short s) throws IOException + { + _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++] = BYTE_QUOTE; + _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @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++] = BYTE_QUOTE; + _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @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++] = BYTE_QUOTE; + _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @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()); + } + } + + + @Override + public void writeNumber(double d) throws IOException + { + if (_cfgNumbersAsStrings || + (((Double.isNaN(d) || Double.isInfinite(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)); + } + + @Override + public void writeNumber(float f) throws IOException + { + if (_cfgNumbersAsStrings || + // [JACKSON-139] + (((Float.isNaN(f) || Float.isInfinite(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) { + String raw = Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features) + ? value.toPlainString() : value.toString(); + _writeQuotedRaw(raw); + } else if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) { + writeRaw(value.toPlainString()); + } else { + writeRaw(value.toString()); + } + } + + @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++] = BYTE_QUOTE; + writeRaw(value); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = BYTE_QUOTE; + } + + @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 + { + int status = _writeContext.writeValue(); + if (status == JsonWriteContext.STATUS_EXPECT_NAME) { + _reportError("Can not "+typeMsg+", expecting field name"); + } + if (_cfgPrettyPrinter == null) { + byte b; + switch (status) { + case JsonWriteContext.STATUS_OK_AFTER_COMMA: + b = BYTE_COMMA; + break; + case JsonWriteContext.STATUS_OK_AFTER_COLON: + b = BYTE_COLON; + break; + case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator + if (_rootValueSeparator != null) { + byte[] raw = _rootValueSeparator.asUnquotedUTF8(); + if (raw.length > 0) { + _writeBytes(raw); + } + } + return; + case JsonWriteContext.STATUS_OK_AS_IS: + default: + return; + } + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail] = b; + ++_outputTail; + return; + } + // Otherwise, pretty printer knows what to do... + _verifyPrettyValueWrite(typeMsg, status); + } + + protected final void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException + { + // If we have a pretty printer, it knows what to do: + switch (status) { + case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array + _cfgPrettyPrinter.writeArrayValueSeparator(this); + break; + case JsonWriteContext.STATUS_OK_AFTER_COLON: + _cfgPrettyPrinter.writeObjectFieldValueSeparator(this); + break; + case JsonWriteContext.STATUS_OK_AFTER_SPACE: + _cfgPrettyPrinter.writeRootValueSeparator(this); + break; + case JsonWriteContext.STATUS_OK_AS_IS: + // First entry, but of which context? + if (_writeContext.inArray()) { + _cfgPrettyPrinter.beforeArrayValues(this); + } else if (_writeContext.inObject()) { + _cfgPrettyPrinter.beforeObjectEntries(this); + } + break; + default: + _throwInternal(); + break; + } + } + + /* + /********************************************************** + /* Low-level output handling + /********************************************************** + */ + + @Override + public void flush() throws IOException + { + _flushBuffer(); + if (_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++] = BYTE_QUOTE; + } + + 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++] = BYTE_QUOTE; + } + } + + /** + * 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) { + // [JACKSON-106] + if (_characterEscapes != null) { + _writeCustomStringSegment2(cbuf, offset, len); + // [JACKSON-102] + } 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 inputLen) + throws IOException + { + // Let's handle surrogates gracefully (as 4 byte output): + if (ch >= SURR1_FIRST) { + if (ch <= SURR2_LAST) { // yes, outside of BMP + // Do we have second part? + if (inputOffset >= inputLen || cbuf == null) { // nope... have to note down + _reportError("Split surrogate on writeRaw() input (last character)"); + } + _outputSurrogates(ch, cbuf[inputOffset]); + return (inputOffset+1); + } + } + final byte[] bbuf = _outputBuffer; + bbuf[_outputTail++] = (byte) (0xe0 | (ch >> 12)); + bbuf[_outputTail++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); + bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); + return inputOffset; + } + + protected final void _outputSurrogates(int surr1, int surr2) throws IOException + { + int c = _decodeSurrogate(surr1, surr2); + if ((_outputTail + 4) > _outputEnd) { + _flushBuffer(); + } + final byte[] bbuf = _outputBuffer; + bbuf[_outputTail++] = (byte) (0xf0 | (c >> 18)); + bbuf[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f)); + bbuf[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + bbuf[_outputTail++] = (byte) (0x80 | (c & 0x3f)); + } + + /** + * + * @param ch + * @param outputPtr Position within output buffer to append multi-byte in + * + * @return New output position after appending + * + * @throws IOException + */ + private 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); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,3684 @@ +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.InputStream} as the input source. + *

+ * Note: non-final since version 2.3. + */ +public class UTF8StreamJsonParser + extends ParserBase +{ + final static byte BYTE_LF = (byte) '\n'; + + // This is the main input-code lookup table, fetched eagerly + private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8(); + + // Latin1 encoding is not supported, but we do use 8-bit subset for + // pre-processing task, to simplify first pass, keep it fast. + protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1(); + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Codec used for data binding when (if) requested; typically full + * ObjectMapper, but that abstract is not part of core + * package. + */ + protected ObjectCodec _objectCodec; + + /** + * Symbol table that contains field names encountered so far + */ + final protected ByteQuadsCanonicalizer _symbols; + + /* + /********************************************************** + /* Parsing state + /********************************************************** + */ + + /** + * Temporary buffer used for name parsing. + */ + protected int[] _quadBuffer = new int[16]; + + /** + * Flag that indicates that the current token has not yet + * been fully processed, and needs to be finished for + * some access (or skipped to obtain the next token) + */ + protected boolean _tokenIncomplete; + + /** + * Temporary storage for partially parsed name bytes. + */ + private int _quad1; + + /** + * Value of {@link #_inputPtr} at the time when the first character of + * name token was read. Used for calculating token location when requested; + * combined with {@link #_currInputProcessed}, may be updated appropriately + * as needed. + * + * @since 2.7 + */ + protected int _nameStartOffset; + + /** + * @since 2.7 + */ + protected int _nameStartRow; + + /** + * @since 2.7 + */ + protected int _nameStartCol; + + /* + /********************************************************** + /* Input buffering (from former 'StreamBasedParserBase') + /********************************************************** + */ + + protected InputStream _inputStream; + + /* + /********************************************************** + /* Current input data + /********************************************************** + */ + + /** + * Current buffer from which data is read; generally data is read into + * buffer from input source, but in some cases pre-loaded buffer + * is handed to the parser. + */ + protected byte[] _inputBuffer; + + /** + * Flag that indicates whether the input buffer is recycable (and + * needs to be returned to recycler once we are done) or not. + *

+ * If it is not, it also means that parser can NOT modify underlying + * buffer. + */ + protected boolean _bufferRecyclable; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in, + ObjectCodec codec, ByteQuadsCanonicalizer sym, + byte[] inputBuffer, int start, int end, + boolean bufferRecyclable) + { + super(ctxt, features); + _inputStream = in; + _objectCodec = codec; + _symbols = sym; + _inputBuffer = inputBuffer; + _inputPtr = start; + _inputEnd = end; + _currInputRowStart = start; + // If we have offset, need to omit that from byte offset, so: + _currInputProcessed = -start; + _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 + /********************************************************** + */ + + @Override + protected final boolean loadMore() throws IOException + { + final int bufSize = _inputEnd; + + _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; + + 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; + return true; + } + // End of input + _closeInput(); + // Should never return 0, so let's fail + if (count == 0) { + throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes"); + } + } + return false; + } + + /** + * Helper method that will try to load at least specified number bytes in + * input buffer, possible moving existing data around if necessary + */ + protected final boolean _loadToHaveAtLeast(int minAvailable) throws IOException + { + // No input stream, no leading (either we are closed, or have non-stream input source) + if (_inputStream == null) { + return false; + } + // Need to move remaining data in front? + int amount = _inputEnd - _inputPtr; + if (amount > 0 && _inputPtr > 0) { + final int ptr = _inputPtr; + + _currInputProcessed += ptr; + _currInputRowStart -= ptr; + // 26-Nov-2015, tatu: Since name-offset requires it too, must offset + // (note: probably has little effect here but just in case) + _nameStartOffset -= ptr; + + System.arraycopy(_inputBuffer, ptr, _inputBuffer, 0, amount); + _inputEnd = amount; + } else { + _inputEnd = 0; + } + _inputPtr = 0; + while (_inputEnd < minAvailable) { + int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); + if (count < 1) { + // End of input + _closeInput(); + // Should never return 0, so let's fail + if (count == 0) { + throw new IOException("InputStream.read() returned 0 characters when trying to read "+amount+" bytes"); + } + return false; + } + _inputEnd += count; + } + return true; + } + + @Override + protected void _closeInput() throws IOException + { + /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() + * on the underlying InputStream, unless we "own" it, or auto-closing + * feature is enabled. + */ + if (_inputStream != null) { + if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) { + _inputStream.close(); + } + _inputStream = null; + } + } + + /** + * Method called to release internal buffers owned by the base + * reader. This may be called along with {@link #_closeInput} (for + * example, when explicitly closing this reader instance), or + * separately (if need be). + */ + @Override + protected void _releaseBuffers() throws IOException + { + super._releaseBuffers(); + // Merge found symbols, if any: + _symbols.release(); + if (_bufferRecyclable) { + byte[] buf = _inputBuffer; + if (buf != null) { + /* 21-Nov-2014, tatu: 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 = ByteArrayBuilder.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); + } + + // // // 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 == '"' && !b64variant.usesPadding()) { + decodedData >>= 4; + buffer[outputPtr++] = (byte) decodedData; + break; + } + bits = _decodeBase64Escape(b64variant, ch, 2); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + // Ok, must get padding + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + if (!b64variant.usesPaddingChar(ch)) { + throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'"); + } + // Got 12 bits, only need 8, need to shift + decodedData >>= 4; + buffer[outputPtr++] = (byte) decodedData; + continue; + } + } + // Nope, 2 or 3 bytes + decodedData = (decodedData << 6) | bits; + // fourth and last base64 char; can be padding, but not ws + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + // as per [JACKSON-631], could also just be 'missing' padding + if (ch == '"' && !b64variant.usesPadding()) { + decodedData >>= 2; + buffer[outputPtr++] = (byte) (decodedData >> 8); + buffer[outputPtr++] = (byte) decodedData; + break; + } + bits = _decodeBase64Escape(b64variant, ch, 3); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + /* With padding we only get 2 bytes; but we have + * to shift it a bit so it is identical to triplet + * case with partial output. + * 3 chars gives 3x6 == 18 bits, of which 2 are + * dummies, need to discard: + */ + decodedData >>= 2; + buffer[outputPtr++] = (byte) (decodedData >> 8); + buffer[outputPtr++] = (byte) decodedData; + continue; + } + } + // otherwise, our triplet is now complete + decodedData = (decodedData << 6) | bits; + buffer[outputPtr++] = (byte) (decodedData >> 16); + buffer[outputPtr++] = (byte) (decodedData >> 8); + buffer[outputPtr++] = (byte) decodedData; + } + _tokenIncomplete = false; + if (outputPtr > 0) { + outputCount += outputPtr; + out.write(buffer, 0, outputPtr); + } + return outputCount; + } + + /* + /********************************************************** + /* Public API, traversal, basic + /********************************************************** + */ + + /** + * @return Next token from the stream, if any found, or null + * to indicate end-of-input + */ + @Override + public JsonToken nextToken() throws IOException + { + /* 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) { + _updateLocation(); + if (!_parsingContext.inArray()) { + _reportMismatchedEndMarker(i, '}'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + return (_currToken = JsonToken.END_ARRAY); + } + if (i == INT_RCURLY) { + _updateLocation(); + if (!_parsingContext.inObject()) { + _reportMismatchedEndMarker(i, ']'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + return (_currToken = JsonToken.END_OBJECT); + } + + // Nope: do we then expect a comma? + if (_parsingContext.expectComma()) { + if (i != INT_COMMA) { + _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries"); + } + i = _skipWS(); + } + + /* And should we now have a name? Always true for + * Object contexts, since the intermediate 'expect-value' + * state is never retained. + */ + if (!_parsingContext.inObject()) { + _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': + _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; + + // !!! 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); + } + + /* + /********************************************************** + /* 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) { + _updateLocation(); + if (!_parsingContext.inArray()) { + _reportMismatchedEndMarker(i, '}'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_ARRAY; + return false; + } + if (i == INT_RCURLY) { + _updateLocation(); + if (!_parsingContext.inObject()) { + _reportMismatchedEndMarker(i, ']'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_OBJECT; + return false; + } + + // Nope: do we then expect a comma? + if (_parsingContext.expectComma()) { + if (i != INT_COMMA) { + _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries"); + } + i = _skipWS(); + } + + if (!_parsingContext.inObject()) { + _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) { + _updateLocation(); + if (!_parsingContext.inArray()) { + _reportMismatchedEndMarker(i, '}'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_ARRAY; + return null; + } + if (i == INT_RCURLY) { + _updateLocation(); + if (!_parsingContext.inObject()) { + _reportMismatchedEndMarker(i, ']'); + } + _parsingContext = _parsingContext.clearAndGetParent(); + _currToken = JsonToken.END_OBJECT; + return null; + } + + // Nope: do we then expect a comma? + if (_parsingContext.expectComma()) { + if (i != INT_COMMA) { + _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries"); + } + i = _skipWS(); + } + 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': + _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; + } + + // 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': + _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 = _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': + _matchToken("true", 1); + t = JsonToken.VALUE_TRUE; + break; + case 'f': + _matchToken("false", 1); + t = JsonToken.VALUE_FALSE; + break; + case 'n': + _matchToken("null", 1); + 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 + int end = _inputPtr + outBuf.length - 1; // 1 == outPtr + if (end > _inputEnd) { + end = _inputEnd; + } + // 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 == '.' || c == 'e' || c == '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 || c > INT_9) { + return _handleInvalidNumberStart(c, true); + } + + // One special case: if first char is 0, must not be followed by a digit + if (c == INT_0) { + c = _verifyNoLeadingZeroes(); + } + + // Ok: we can first just add digit we saw first: + outBuf[outPtr++] = (char) c; + int intLen = 1; + + // And then figure out how far we can read without further checks + // for either input or output + int end = _inputPtr + outBuf.length - outPtr; + if (end > _inputEnd) { + end = _inputEnd; + } + + // With this, we have a nice and tight loop: + while (true) { + if (_inputPtr >= end) { + // Long enough to be split across boundary, so: + return _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 == '.' || c == 'e' || c == '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 (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { + reportInvalidNumber("Leading zeroes not allowed"); + } + // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) + ++_inputPtr; // Leading zero to be skipped + if (ch == INT_0) { + while (_inputPtr < _inputEnd || loadMore()) { + ch = _inputBuffer[_inputPtr] & 0xFF; + if (ch < INT_0 || ch > INT_9) { // followed by non-number; retain one zero + return INT_0; + } + ++_inputPtr; // skip previous zeroes + if (ch != INT_0) { // followed by other number; return + break; + } + } + } + return ch; + } + + private 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 + 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_9 && c >= INT_0) { + ++expLen; + if (outPtr >= outBuf.length) { + outBuf = _textBuffer.finishCurrentSegment(); + outPtr = 0; + } + outBuf[outPtr++] = (char) c; + if (_inputPtr >= _inputEnd && !loadMore()) { + eof = true; + break exp_loop; + } + c = (int) _inputBuffer[_inputPtr++] & 0xFF; + } + // must be followed by sequence of ints, one minimum + if (expLen == 0) { + reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit"); + } + } + + // Ok; unless we hit end-of-input, need to push last char read back + if (!eof) { + --_inputPtr; + // As per #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"); + } + } + 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 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; + } + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidEOF(" in field name"); + } + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + } + + if (currQuadBytes > 0) { + if (qlen >= quads.length) { + _quadBuffer = quads = growArrayBy(quads, quads.length); + } + quads[qlen++] = 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 + { + // [JACKSON-173]: allow single quotes + if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { + return _parseAposName(); + } + // [JACKSON-69]: allow unquoted names if feature enabled: + if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { + char c = (char) _decodeCharForError(ch); + _reportUnexpectedChar(c, "was expecting double-quote to start field name"); + } + /* Also: note that although we use a different table here, + * it does NOT handle UTF-8 decoding. It'll just pass those + * high-bit codes as acceptable for later decoding. + */ + final int[] codes = CharTypes.getInputCodeUtf8JsNames(); + // Also: must start with a valid character... + if (codes[ch] != 0) { + _reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name"); + } + + /* Ok, now; instead of ultra-optimizing parsing here (as with + * regular JSON names), let's just use the generic "slow" + * variant. Can measure its impact later on if need be + */ + int[] quads = _quadBuffer; + int qlen = 0; + int currQuad = 0; + int currQuadBytes = 0; + + while (true) { + // Ok, we have one more byte to add at any rate: + if (currQuadBytes < 4) { + ++currQuadBytes; + currQuad = (currQuad << 8) | ch; + } else { + if (qlen >= quads.length) { + _quadBuffer = quads = growArrayBy(quads, quads.length); + } + quads[qlen++] = currQuad; + currQuad = ch; + currQuadBytes = 1; + } + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidEOF(" in field name"); + } + } + ch = _inputBuffer[_inputPtr] & 0xFF; + if (codes[ch] != 0) { + break; + } + ++_inputPtr; + } + + if (currQuadBytes > 0) { + if (qlen >= quads.length) { + _quadBuffer = quads = growArrayBy(quads, quads.length); + } + quads[qlen++] = currQuad; + } + 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 name"); + } + } + int ch = _inputBuffer[_inputPtr++] & 0xFF; + 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; + } + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidEOF(" in field name"); + } + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + } + + if (currQuadBytes > 0) { + if (qlen >= quads.length) { + _quadBuffer = quads = growArrayBy(quads, quads.length); + } + quads[qlen++] = 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"); + } + + // 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 + { + // 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(c); + break; + case 3: // 3-byte UTF + _skipUtf8_3(c); + break; + case 4: // 4-byte UTF + _skipUtf8_4(c); + break; + default: + if (c < INT_SPACE) { + // As per [JACKSON-208], call can now return: + _throwUnquotedSpace(c, "string value"); + } else { + // Is this good enough error message? + _reportInvalidChar(c); + } + } + } + } + + /** + * Method for handling cases where first non-space character + * of an expected value token is not legal for standard JSON content. + */ + protected JsonToken _handleUnexpectedValue(int c) + throws IOException + { + // Most likely an error, unless we are to allow single-quote-strings + switch (c) { + case ']': + case '}': + // Error: neither is valid at this point; valid closers have + // been handled earlier + _reportUnexpectedChar(c, "expected a value"); + case '\'': + if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { + return _handleApos(); + } + break; + case 'N': + _matchToken("NaN", 1); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN("NaN", Double.NaN); + } + _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + break; + case 'I': + _matchToken("Infinity", 1); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN("Infinity", Double.POSITIVE_INFINITY); + } + _reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + break; + case '+': // note: '-' is taken as number + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidEOFInValue(); + } + } + return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false); + } + // [Issue#77] Try to decode most likely token + if (Character.isJavaIdentifierStart(c)) { + _reportInvalidToken(""+((char) c), "('true', 'false' or 'null')"); + } + // but if it doesn't look like a token: + _reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); + return null; + } + + protected JsonToken _handleApos() + throws IOException + { + int c = 0; + // Otherwise almost verbatim copy of _finishString() + int outPtr = 0; + char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); + + // Here we do want to do full decoding, hence: + final int[] codes = _icUTF8; + 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 == '\'' || codes[c] != 0) { + break ascii_loop; + } + outBuf[outPtr++] = (char) c; + } + } + + // Ok: end marker, escape or multi-byte? + if (c == '\'') { + break main_loop; + } + + switch (codes[c]) { + case 1: // backslash + if (c != '\'') { // marked as special, isn't here + c = _decodeEscaped(); + } + break; + case 2: // 2-byte UTF + c = _decodeUtf8_2(c); + break; + case 3: // 3-byte UTF + if ((_inputEnd - _inputPtr) >= 2) { + c = _decodeUtf8_3fast(c); + } else { + c = _decodeUtf8_3(c); + } + break; + case 4: // 4-byte UTF + c = _decodeUtf8_4(c); + // Let's add first part right away: + outBuf[outPtr++] = (char) (0xD800 | (c >> 10)); + if (outPtr >= outBuf.length) { + outBuf = _textBuffer.finishCurrentSegment(); + outPtr = 0; + } + c = 0xDC00 | (c & 0x3FF); + // And let the other char output down below + break; + default: + if (c < INT_SPACE) { + _throwUnquotedSpace(c, "string value"); + } + // Is this good enough error message? + _reportInvalidChar(c); + } + // Need more room? + if (outPtr >= outBuf.length) { + outBuf = _textBuffer.finishCurrentSegment(); + outPtr = 0; + } + // Ok, let's add char to output: + outBuf[outPtr++] = (char) c; + } + _textBuffer.setCurrentLength(outPtr); + + return JsonToken.VALUE_STRING; + } + + /** + * Method called if expected numeric value (due to leading sign) does not + * look like a number + */ + protected JsonToken _handleInvalidNumberStart(int ch, boolean neg) + throws IOException + { + while (ch == 'I') { + if (_inputPtr >= _inputEnd) { + if (!loadMore()) { + _reportInvalidEOFInValue(); + } + } + ch = _inputBuffer[_inputPtr++]; + String match; + if (ch == 'N') { + match = neg ? "-INF" :"+INF"; + } else if (ch == 'n') { + match = neg ? "-Infinity" :"+Infinity"; + } else { + break; + } + _matchToken(match, 3); + if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { + return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); + } + _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); + } + reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); + return null; + } + + protected 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.getTypeDesc()+" 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) { + if (i < INT_SPACE) { + _throwInvalidSpace(i); + } + _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); + } + } + } + throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries"); + } + + private final void _skipComment() throws IOException + { + if (!isEnabled(Feature.ALLOW_COMMENTS)) { + _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); + } + // First: check which comment (if either) it is: + if (_inputPtr >= _inputEnd && !loadMore()) { + _reportInvalidEOF(" in a comment"); + } + int c = _inputBuffer[_inputPtr++] & 0xFF; + 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(); + + // 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(i); + break; + case 3: // 3-byte UTF + _skipUtf8_3(i); + break; + case 4: // 4-byte UTF + _skipUtf8_4(i); + break; + default: // e.g. -1 + // Is this good enough error message? + _reportInvalidChar(i); + } + } + } + _reportInvalidEOF(" in a comment"); + } + + private final boolean _skipYAMLComment() throws IOException + { + if (!isEnabled(Feature.ALLOW_YAML_COMMENTS)) { + return false; + } + _skipLine(); + return true; + } + + /** + * Method for skipping contents of an input line; usually for CPP + * and YAML style comments. + */ + private final void _skipLine() throws IOException + { + // Ok: need to find EOF or linefeed + final int[] codes = CharTypes.getInputCodeComment(); + while ((_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(i); + break; + case 3: // 3-byte UTF + _skipUtf8_3(i); + 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"); + } + } + 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"); + } + } + int ch = (int) _inputBuffer[_inputPtr++]; + int digit = CharTypes.charToHex(ch); + if (digit < 0) { + _reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence"); + } + value = (value << 4) | digit; + } + return (char) value; + } + + protected int _decodeCharForError(int firstByte) throws IOException + { + 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(int c) throws IOException + { + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + c = (int) _inputBuffer[_inputPtr++]; + if ((c & 0xC0) != 0x080) { + _reportInvalidOther(c & 0xFF, _inputPtr); + } + } + + /* Alas, can't heavily optimize skipping, since we still have to + * do validity checks... + */ + private final void _skipUtf8_3(int c) throws IOException + { + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + //c &= 0x0F; + c = (int) _inputBuffer[_inputPtr++]; + if ((c & 0xC0) != 0x080) { + _reportInvalidOther(c & 0xFF, _inputPtr); + } + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + c = (int) _inputBuffer[_inputPtr++]; + if ((c & 0xC0) != 0x080) { + _reportInvalidOther(c & 0xFF, _inputPtr); + } + } + + private 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) throws IOException + { + _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN"); + } + + protected void _reportInvalidToken(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) { + if (_inputPtr >= _inputEnd && !loadMore()) { + break; + } + int i = (int) _inputBuffer[_inputPtr++]; + char c = (char) _decodeCharForError(i); + if (!Character.isJavaIdentifierPart(c)) { + break; + } + sb.append(c); + } + _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg); + } + + protected void _reportInvalidChar(int c) + throws JsonParseException + { + // Either invalid WS or illegal UTF-8 start char + if (c < INT_SPACE) { + _throwInvalidSpace(c); + } + _reportInvalidInitial(c); + } + + protected void _reportInvalidInitial(int mask) + throws JsonParseException + { + _reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask)); + } + + protected void _reportInvalidOther(int mask) + throws JsonParseException + { + _reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask)); + } + + protected void _reportInvalidOther(int mask, int ptr) + throws JsonParseException + { + _inputPtr = ptr; + _reportInvalidOther(mask); + } + + public static int[] growArrayBy(int[] arr, int more) + { + if (arr == null) { + return new int[more]; + } + 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 { + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = (int) _inputBuffer[_inputPtr++] & 0xFF; + } while (ch <= INT_SPACE); + int bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { // reached the end, fair and square? + if (ch == INT_QUOTE) { + return builder.toByteArray(); + } + bits = _decodeBase64Escape(b64variant, ch, 0); + if (bits < 0) { // white space to skip + continue; + } + } + int decodedData = bits; + + // then second base64 char; can't get padding yet, nor ws + + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + bits = _decodeBase64Escape(b64variant, ch, 1); + } + decodedData = (decodedData << 6) | bits; + + // third base64 char; can be padding, but not ws + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + bits = b64variant.decodeBase64Char(ch); + + // First branch: can get padding (-> 1 byte) + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + // as per [JACKSON-631], could also just be 'missing' padding + if (ch == '"' && !b64variant.usesPadding()) { + decodedData >>= 4; + builder.append(decodedData); + return builder.toByteArray(); + } + bits = _decodeBase64Escape(b64variant, ch, 2); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + // Ok, must get padding + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + if (!b64variant.usesPaddingChar(ch)) { + throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'"); + } + // Got 12 bits, only need 8, need to shift + decodedData >>= 4; + builder.append(decodedData); + continue; + } + } + // Nope, 2 or 3 bytes + decodedData = (decodedData << 6) | bits; + // fourth and last base64 char; can be padding, but not ws + if (_inputPtr >= _inputEnd) { + loadMoreGuaranteed(); + } + ch = _inputBuffer[_inputPtr++] & 0xFF; + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + // as per [JACKSON-631], could also just be 'missing' padding + if (ch == '"' && !b64variant.usesPadding()) { + decodedData >>= 2; + builder.appendTwoBytes(decodedData); + return builder.toByteArray(); + } + bits = _decodeBase64Escape(b64variant, ch, 3); + } + if (bits == Base64Variant.BASE64_VALUE_PADDING) { + /* With padding we only get 2 bytes; but we have + * to shift it a bit so it is identical to triplet + * case with partial output. + * 3 chars gives 3x6 == 18 bits, of which 2 are + * dummies, need to discard: + */ + decodedData >>= 2; + builder.appendTwoBytes(decodedData); + continue; + } + } + // otherwise, our triplet is now complete + decodedData = (decodedData << 6) | bits; + builder.appendThreeBytes(decodedData); + } + } + + /* + /********************************************************** + /* Improved location updating (refactored in 2.7) + /********************************************************** + */ + + // As per [core#108], must ensure we call the right method + @Override + public JsonLocation getTokenLocation() + { + final Object src = _ioContext.getSourceReference(); + if (_currToken == JsonToken.FIELD_NAME) { + long total = _currInputProcessed + (_nameStartOffset-1); + return new JsonLocation(src, + total, -1L, _nameStartRow, _nameStartCol); + } + return new JsonLocation(src, + _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(_ioContext.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 + /********************************************************** + */ + + /** + * 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))); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1883 @@ +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.*; + +/** + * {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer} + * which handles character encoding. + */ +public final class WriterBasedJsonGenerator + extends JsonGeneratorImpl +{ + final protected static int SHORT_WRITE = 32; + + final protected static char[] HEX_CHARS = CharTypes.copyHexChars(); + + /* + /********************************************************** + /* Output buffering + /********************************************************** + */ + + final protected Writer _writer; + + /** + * Intermediate buffer in which contents are buffered before + * being written using {@link #_writer}. + */ + protected char[] _outputBuffer; + + /** + * Pointer to the first buffered character to output + */ + protected int _outputHead; + + /** + * 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; + + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public WriterBasedJsonGenerator(IOContext ctxt, int features, + ObjectCodec codec, Writer w) + { + super(ctxt, features, codec); + _writer = w; + _outputBuffer = ctxt.allocConcatBuffer(); + _outputEnd = _outputBuffer.length; + } + + /* + /********************************************************** + /* Overridden configuration methods + /********************************************************** + */ + + @Override + public Object getOutputTarget() { + return _writer; + } + + @Override + public int getOutputBuffered() { + // Assuming tail and head are kept but... trust and verify: + int len = _outputTail - _outputHead; + return Math.max(0, len); + } + + /* + /********************************************************** + /* 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 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++] = '"'; + // The beef: + _writeString(name); + // and closing quotes; need room for one more char: + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + + protected 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? + final char[] quoted = name.asQuotedChars(); + if (_cfgUnqNames) { + writeRaw(quoted, 0, quoted.length); + return; + } + // we know there's room for at least one more char + _outputBuffer[_outputTail++] = '"'; + // The beef: + final int qlen = quoted.length; + if ((_outputTail + qlen + 1) >= _outputEnd) { + writeRaw(quoted, 0, qlen); + // and closing quotes; need room for one more char: + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } else { + System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen); + _outputTail += qlen; + _outputBuffer[_outputTail++] = '"'; + } + } + + /* + /********************************************************** + /* Output method implementations, structural + /********************************************************** + */ + + @Override + public void writeStartArray() throws IOException, JsonGenerationException + { + _verifyValueWrite("start an array"); + _writeContext = _writeContext.createChildArrayContext(); + if (_cfgPrettyPrinter != null) { + _cfgPrettyPrinter.writeStartArray(this); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '['; + } + } + + @Override + public void writeEndArray() throws IOException, JsonGenerationException + { + if (!_writeContext.inArray()) { + _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc()); + } + if (_cfgPrettyPrinter != null) { + _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = ']'; + } + _writeContext = _writeContext.clearAndGetParent(); + } + + @Override + public void writeStartObject() throws IOException, JsonGenerationException + { + _verifyValueWrite("start an object"); + _writeContext = _writeContext.createChildObjectContext(); + if (_cfgPrettyPrinter != null) { + _cfgPrettyPrinter.writeStartObject(this); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '{'; + } + } + + @Override + public void writeEndObject() throws IOException, JsonGenerationException + { + if (!_writeContext.inObject()) { + _reportError("Current context not an object but "+_writeContext.getTypeDesc()); + } + if (_cfgPrettyPrinter != null) { + _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '}'; + } + _writeContext = _writeContext.clearAndGetParent(); + } + + /** + * Specialized version of _writeFieldName, off-lined + * to keep the "fast path" as simple (and hopefully fast) as possible. + */ + protected void _writePPFieldName(String name, boolean commaBefore) throws IOException + { + if (commaBefore) { + _cfgPrettyPrinter.writeObjectEntrySeparator(this); + } else { + _cfgPrettyPrinter.beforeObjectEntries(this); + } + + if (_cfgUnqNames) {// non-standard, omit quotes + _writeString(name); + } else { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + _writeString(name); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + } + + protected 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++] = '"'; + writeRaw(quoted, 0, quoted.length); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + } + + /* + /********************************************************** + /* 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++] = '"'; + _writeString(text); + // And finally, closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + + @Override + public void writeString(char[] text, int offset, int len) throws IOException + { + _verifyValueWrite(WRITE_STRING); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + _writeString(text, offset, len); + // And finally, closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + + @Override + public void writeString(SerializableString sstr) throws IOException + { + _verifyValueWrite(WRITE_STRING); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + // Note: copied from writeRaw: + char[] text = sstr.asQuotedChars(); + final int len = text.length; + // Only worth buffering if it's a short write? + if (len < SHORT_WRITE) { + int room = _outputEnd - _outputTail; + if (len > room) { + _flushBuffer(); + } + System.arraycopy(text, 0, _outputBuffer, _outputTail, len); + _outputTail += len; + } else { + // Otherwise, better just pass through: + _flushBuffer(); + _writer.write(text, 0, len); + } + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + + @Override + public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { + // 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 { + writeRaw(text.getValue()); + } + + @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++] = '"'; + _writeBinary(b64variant, data, offset, offset+len); + // and closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + + @Override + public int writeBinary(Base64Variant b64variant, + InputStream data, int dataLength) + throws IOException, JsonGenerationException + { + _verifyValueWrite(WRITE_BINARY); + // Starting quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + byte[] encodingBuffer = _ioContext.allocBase64Buffer(); + int bytes; + try { + if (dataLength < 0) { // length unknown + bytes = _writeBinary(b64variant, data, encodingBuffer); + } else { + int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength); + if (missing > 0) { + _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")"); + } + bytes = dataLength; + } + } finally { + _ioContext.releaseBase64Buffer(encodingBuffer); + } + // and closing quotes + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + return bytes; + } + + /* + /********************************************************** + /* Output method implementations, primitive + /********************************************************** + */ + + @Override + public void writeNumber(short s) throws IOException + { + _verifyValueWrite(WRITE_NUMBER); + if (_cfgNumbersAsStrings) { + _writeQuotedShort(s); + return; + } + // up to 5 digits and possible minus sign + if ((_outputTail + 6) >= _outputEnd) { + _flushBuffer(); + } + _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); + } + + private void _writeQuotedShort(short s) throws IOException { + if ((_outputTail + 8) >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); + _outputBuffer[_outputTail++] = '"'; + } + + @Override + public void writeNumber(int i) throws IOException + { + _verifyValueWrite(WRITE_NUMBER); + if (_cfgNumbersAsStrings) { + _writeQuotedInt(i); + return; + } + // up to 10 digits and possible minus sign + if ((_outputTail + 11) >= _outputEnd) { + _flushBuffer(); + } + _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); + } + + private void _writeQuotedInt(int i) throws IOException { + if ((_outputTail + 13) >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); + _outputBuffer[_outputTail++] = '"'; + } + + @Override + public void writeNumber(long l) throws IOException + { + _verifyValueWrite(WRITE_NUMBER); + if (_cfgNumbersAsStrings) { + _writeQuotedLong(l); + return; + } + if ((_outputTail + 21) >= _outputEnd) { + // up to 20 digits, minus sign + _flushBuffer(); + } + _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); + } + + private void _writeQuotedLong(long l) throws IOException { + if ((_outputTail + 23) >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); + _outputBuffer[_outputTail++] = '"'; + } + + // !!! 05-Aug-2008, tatus: Any ways to optimize these? + + @Override + public void writeNumber(BigInteger value) throws IOException + { + _verifyValueWrite(WRITE_NUMBER); + if (value == null) { + _writeNull(); + } else if (_cfgNumbersAsStrings) { + _writeQuotedRaw(value.toString()); + } else { + writeRaw(value.toString()); + } + } + + + @Override + public void writeNumber(double d) throws IOException + { + if (_cfgNumbersAsStrings || + // [JACKSON-139] + (isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS) && ((Double.isNaN(d) || Double.isInfinite(d))))) { + writeString(String.valueOf(d)); + return; + } + // What is the max length for doubles? 40 chars? + _verifyValueWrite(WRITE_NUMBER); + writeRaw(String.valueOf(d)); + } + + @Override + public void writeNumber(float f) throws IOException + { + if (_cfgNumbersAsStrings || + // [JACKSON-139] + (isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS) && ((Float.isNaN(f) || Float.isInfinite(f))))) { + 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) { + String raw = isEnabled(Feature.WRITE_BIGDECIMAL_AS_PLAIN) ? value.toPlainString() : value.toString(); + _writeQuotedRaw(raw); + } else if (isEnabled(Feature.WRITE_BIGDECIMAL_AS_PLAIN)) { + writeRaw(value.toPlainString()); + } else { + writeRaw(value.toString()); + } + } + + @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++] = '"'; + writeRaw(value); + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = '"'; + } + + @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 void _verifyValueWrite(String typeMsg) throws IOException + { + if (_cfgPrettyPrinter != null) { + // Otherwise, pretty printer knows what to do... + _verifyPrettyValueWrite(typeMsg); + return; + } + char c; + final int status = _writeContext.writeValue(); + if (status == JsonWriteContext.STATUS_EXPECT_NAME) { + _reportError("Can not "+typeMsg+", expecting field name"); + } + switch (status) { + case JsonWriteContext.STATUS_OK_AFTER_COMMA: + c = ','; + break; + case JsonWriteContext.STATUS_OK_AFTER_COLON: + c = ':'; + break; + case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator + if (_rootValueSeparator != null) { + writeRaw(_rootValueSeparator.getValue()); + } + return; + case JsonWriteContext.STATUS_OK_AS_IS: + default: + return; + } + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail] = c; + ++_outputTail; + } + + protected void _verifyPrettyValueWrite(String typeMsg) throws IOException + { + final int status = _writeContext.writeValue(); + if (status == JsonWriteContext.STATUS_EXPECT_NAME) { + _reportError("Can not "+typeMsg+", expecting field name"); + } + + // If we have a pretty printer, it knows what to do: + switch (status) { + case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array + _cfgPrettyPrinter.writeArrayValueSeparator(this); + break; + case JsonWriteContext.STATUS_OK_AFTER_COLON: + _cfgPrettyPrinter.writeObjectFieldValueSeparator(this); + break; + case JsonWriteContext.STATUS_OK_AFTER_SPACE: + _cfgPrettyPrinter.writeRootValueSeparator(this); + break; + case JsonWriteContext.STATUS_OK_AS_IS: + // First entry, but of which context? + if (_writeContext.inArray()) { + _cfgPrettyPrinter.beforeArrayValues(this); + } else if (_writeContext.inObject()) { + _cfgPrettyPrinter.beforeObjectEntries(this); + } + break; + default: + _throwInternal(); + break; + } + } + + /* + /********************************************************** + /* 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); + } + } + + /* + /********************************************************** + /* Internal methods, low-level writing; text, default + /********************************************************** + */ + + private void _writeString(String text) throws IOException + { + /* One check first: if String won't fit in the buffer, let's + * segment writes. No point in extending buffer to huge sizes + * (like if someone wants to include multi-megabyte base64 + * encoded stuff or such) + */ + final int len = text.length(); + if (len > _outputEnd) { // Let's reserve space for entity at begin/end + _writeLongString(text); + return; + } + + // Ok: we know String will fit in buffer ok + // But do we need to flush first? + if ((_outputTail + len) > _outputEnd) { + _flushBuffer(); + } + text.getChars(0, len, _outputBuffer, _outputTail); + + if (_characterEscapes != null) { + _writeStringCustom(len); + } else if (_maximumNonEscapedChar != 0) { + _writeStringASCII(len, _maximumNonEscapedChar); + } else { + _writeString2(len); + } + } + + private void _writeString2(final int len) throws IOException + { + // And then we'll need to verify need for escaping etc: + final int end = _outputTail + len; + final int[] escCodes = _outputEscapes; + final int escLen = escCodes.length; + + output_loop: + while (_outputTail < end) { + // Fast loop for chars not needing escaping + escape_loop: + while (true) { + char c = _outputBuffer[_outputTail]; + if (c < escLen && escCodes[c] != 0) { + break escape_loop; + } + if (++_outputTail >= end) { + break output_loop; + } + } + + // Ok, bumped into something that needs escaping. + /* First things first: need to flush the buffer. + * Inlined, as we don't want to lose tail pointer + */ + int flushLen = (_outputTail - _outputHead); + if (flushLen > 0) { + _writer.write(_outputBuffer, _outputHead, flushLen); + } + /* In any case, tail will be the new start, so hopefully + * we have room now. + */ + char c = _outputBuffer[_outputTail++]; + _prependOrWriteCharacterEscape(c, escCodes[c]); + } + } + + /** + * Method called to write "long strings", strings whose length exceeds + * output buffer length. + */ + private void _writeLongString(String text) throws IOException + { + // First things first: let's flush the buffer to get some more room + _flushBuffer(); + + // Then we can write + final int textLen = text.length(); + int offset = 0; + do { + int max = _outputEnd; + int segmentLen = ((offset + max) > textLen) + ? (textLen - offset) : max; + text.getChars(offset, offset+segmentLen, _outputBuffer, 0); + if (_characterEscapes != null) { + _writeSegmentCustom(segmentLen); + } else if (_maximumNonEscapedChar != 0) { + _writeSegmentASCII(segmentLen, _maximumNonEscapedChar); + } else { + _writeSegment(segmentLen); + } + offset += segmentLen; + } while (offset < textLen); + } + + /** + * Method called to output textual context which has been copied + * to the output buffer prior to call. If any escaping is needed, + * it will also be handled by the method. + *

+ * Note: when called, textual content to write is within output + * buffer, right after buffered content (if any). That's why only + * length of that text is passed, as buffer and offset are implied. + */ + private void _writeSegment(int end) throws IOException + { + final int[] escCodes = _outputEscapes; + final int escLen = escCodes.length; + + int ptr = 0; + int start = ptr; + + output_loop: + while (ptr < end) { + // Fast loop for chars not needing escaping + char c; + while (true) { + c = _outputBuffer[ptr]; + if (c < escLen && escCodes[c] != 0) { + break; + } + if (++ptr >= end) { + break; + } + } + + // Ok, bumped into something that needs escaping. + /* First things first: need to flush the buffer. + * Inlined, as we don't want to lose tail pointer + */ + int flushLen = (ptr - start); + if (flushLen > 0) { + _writer.write(_outputBuffer, start, flushLen); + if (ptr >= end) { + break output_loop; + } + } + ++ptr; + // So; either try to prepend (most likely), or write directly: + start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]); + } + } + + /** + * This method called when the string content is already in + * a char buffer, and need not be copied for processing. + */ + private void _writeString(char[] text, int offset, int len) throws IOException + { + if (_characterEscapes != null) { + _writeStringCustom(text, offset, len); + return; + } + if (_maximumNonEscapedChar != 0) { + _writeStringASCII(text, offset, len, _maximumNonEscapedChar); + return; + } + + /* Let's just find longest spans of non-escapable + * content, and for each see if it makes sense + * to copy them, or write through + */ + len += offset; // -> len marks the end from now on + final int[] escCodes = _outputEscapes; + final int escLen = escCodes.length; + while (offset < len) { + int start = offset; + + while (true) { + char c = text[offset]; + if (c < escLen && escCodes[c] != 0) { + break; + } + if (++offset >= len) { + break; + } + } + + // Short span? Better just copy it to buffer first: + int newAmount = offset - start; + if (newAmount < SHORT_WRITE) { + // Note: let's reserve room for escaped char (up to 6 chars) + if ((_outputTail + newAmount) > _outputEnd) { + _flushBuffer(); + } + if (newAmount > 0) { + System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); + _outputTail += newAmount; + } + } else { // Nope: better just write through + _flushBuffer(); + _writer.write(text, start, newAmount); + } + // Was this the end? + if (offset >= len) { // yup + break; + } + // Nope, need to escape the char. + char c = text[offset++]; + _appendCharacterEscape(c, escCodes[c]); + } + } + + /* + /********************************************************** + /* Internal methods, low-level writing, text segment + /* with additional escaping (ASCII or such) + /********************************************************** + */ + + /* Same as "_writeString2()", except needs additional escaping + * for subset of characters + */ + private void _writeStringASCII(final int len, final int maxNonEscaped) + throws IOException, JsonGenerationException + { + // And then we'll need to verify need for escaping etc: + int end = _outputTail + len; + final int[] escCodes = _outputEscapes; + final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); + int escCode = 0; + + output_loop: + while (_outputTail < end) { + char c; + // Fast loop for chars not needing escaping + escape_loop: + while (true) { + c = _outputBuffer[_outputTail]; + if (c < escLimit) { + escCode = escCodes[c]; + if (escCode != 0) { + break escape_loop; + } + } else if (c > maxNonEscaped) { + escCode = CharacterEscapes.ESCAPE_STANDARD; + break escape_loop; + } + if (++_outputTail >= end) { + break output_loop; + } + } + int flushLen = (_outputTail - _outputHead); + if (flushLen > 0) { + _writer.write(_outputBuffer, _outputHead, flushLen); + } + ++_outputTail; + _prependOrWriteCharacterEscape(c, escCode); + } + } + + private void _writeSegmentASCII(int end, final int maxNonEscaped) + throws IOException, JsonGenerationException + { + final int[] escCodes = _outputEscapes; + final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); + + int ptr = 0; + int escCode = 0; + int start = ptr; + + output_loop: + while (ptr < end) { + // Fast loop for chars not needing escaping + char c; + while (true) { + c = _outputBuffer[ptr]; + if (c < escLimit) { + escCode = escCodes[c]; + if (escCode != 0) { + break; + } + } else if (c > maxNonEscaped) { + escCode = CharacterEscapes.ESCAPE_STANDARD; + break; + } + if (++ptr >= end) { + break; + } + } + int flushLen = (ptr - start); + if (flushLen > 0) { + _writer.write(_outputBuffer, start, flushLen); + if (ptr >= end) { + break output_loop; + } + } + ++ptr; + start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode); + } + } + + private void _writeStringASCII(char[] text, int offset, int len, + final int maxNonEscaped) + throws IOException, JsonGenerationException + { + len += offset; // -> len marks the end from now on + final int[] escCodes = _outputEscapes; + final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); + + int escCode = 0; + + while (offset < len) { + int start = offset; + char c; + + while (true) { + c = text[offset]; + if (c < escLimit) { + escCode = escCodes[c]; + if (escCode != 0) { + break; + } + } else if (c > maxNonEscaped) { + escCode = CharacterEscapes.ESCAPE_STANDARD; + break; + } + if (++offset >= len) { + break; + } + } + + // Short span? Better just copy it to buffer first: + int newAmount = offset - start; + if (newAmount < SHORT_WRITE) { + // Note: let's reserve room for escaped char (up to 6 chars) + if ((_outputTail + newAmount) > _outputEnd) { + _flushBuffer(); + } + if (newAmount > 0) { + System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); + _outputTail += newAmount; + } + } else { // Nope: better just write through + _flushBuffer(); + _writer.write(text, start, newAmount); + } + // Was this the end? + if (offset >= len) { // yup + break; + } + // Nope, need to escape the char. + ++offset; + _appendCharacterEscape(c, escCode); + } + } + + /* + /********************************************************** + /* Internal methods, low-level writing, text segment + /* with custom escaping (possibly coupling with ASCII limits) + /********************************************************** + */ + + /* Same as "_writeString2()", except needs additional escaping + * for subset of characters + */ + private void _writeStringCustom(final int len) + throws IOException, JsonGenerationException + { + // And then we'll need to verify need for escaping etc: + int end = _outputTail + len; + final int[] escCodes = _outputEscapes; + final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; + final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); + int escCode = 0; + final CharacterEscapes customEscapes = _characterEscapes; + + output_loop: + while (_outputTail < end) { + char c; + // Fast loop for chars not needing escaping + escape_loop: + while (true) { + c = _outputBuffer[_outputTail]; + if (c < escLimit) { + escCode = escCodes[c]; + if (escCode != 0) { + break escape_loop; + } + } else if (c > maxNonEscaped) { + escCode = CharacterEscapes.ESCAPE_STANDARD; + break escape_loop; + } else { + if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { + escCode = CharacterEscapes.ESCAPE_CUSTOM; + break escape_loop; + } + } + if (++_outputTail >= end) { + break output_loop; + } + } + int flushLen = (_outputTail - _outputHead); + if (flushLen > 0) { + _writer.write(_outputBuffer, _outputHead, flushLen); + } + ++_outputTail; + _prependOrWriteCharacterEscape(c, escCode); + } + } + + private void _writeSegmentCustom(int end) + throws IOException, JsonGenerationException + { + final int[] escCodes = _outputEscapes; + final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; + final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); + final CharacterEscapes customEscapes = _characterEscapes; + + int ptr = 0; + int escCode = 0; + int start = ptr; + + output_loop: + while (ptr < end) { + // Fast loop for chars not needing escaping + char c; + while (true) { + c = _outputBuffer[ptr]; + if (c < escLimit) { + escCode = escCodes[c]; + if (escCode != 0) { + break; + } + } else if (c > maxNonEscaped) { + escCode = CharacterEscapes.ESCAPE_STANDARD; + break; + } else { + if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { + escCode = CharacterEscapes.ESCAPE_CUSTOM; + break; + } + } + if (++ptr >= end) { + break; + } + } + int flushLen = (ptr - start); + if (flushLen > 0) { + _writer.write(_outputBuffer, start, flushLen); + if (ptr >= end) { + break output_loop; + } + } + ++ptr; + start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode); + } + } + + private void _writeStringCustom(char[] text, int offset, int len) + throws IOException, JsonGenerationException + { + len += offset; // -> len marks the end from now on + final int[] escCodes = _outputEscapes; + final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; + final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); + final CharacterEscapes customEscapes = _characterEscapes; + + int escCode = 0; + + while (offset < len) { + int start = offset; + char c; + + while (true) { + c = text[offset]; + if (c < escLimit) { + escCode = escCodes[c]; + if (escCode != 0) { + break; + } + } else if (c > maxNonEscaped) { + escCode = CharacterEscapes.ESCAPE_STANDARD; + break; + } else { + if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { + escCode = CharacterEscapes.ESCAPE_CUSTOM; + break; + } + } + if (++offset >= len) { + break; + } + } + + // Short span? Better just copy it to buffer first: + int newAmount = offset - start; + if (newAmount < SHORT_WRITE) { + // Note: let's reserve room for escaped char (up to 6 chars) + if ((_outputTail + newAmount) > _outputEnd) { + _flushBuffer(); + } + if (newAmount > 0) { + System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); + _outputTail += newAmount; + } + } else { // Nope: better just write through + _flushBuffer(); + _writer.write(text, start, newAmount); + } + // Was this the end? + if (offset >= len) { // yup + break; + } + // Nope, need to escape the char. + ++offset; + _appendCharacterEscape(c, escCode); + } + } + + /* + /********************************************************** + /* Internal methods, low-level writing; binary + /********************************************************** + */ + + protected void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd) + throws IOException, JsonGenerationException + { + // Encoding is by chunks of 3 input, 4 output chars, so: + int safeInputEnd = inputEnd - 3; + // Let's also reserve room for possible (and quoted) lf char each round + int safeOutputEnd = _outputEnd - 6; + int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; + + // Ok, first we loop through all full triplets of data: + while (inputPtr <= safeInputEnd) { + if (_outputTail > safeOutputEnd) { // need to flush + _flushBuffer(); + } + // First, mash 3 bytes into lsb of 32-bit int + int b24 = ((int) input[inputPtr++]) << 8; + b24 |= ((int) input[inputPtr++]) & 0xFF; + b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); + _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); + if (--chunksBeforeLF <= 0) { + // note: must quote in JSON value + _outputBuffer[_outputTail++] = '\\'; + _outputBuffer[_outputTail++] = 'n'; + chunksBeforeLF = b64variant.getMaxLineLength() >> 2; + } + } + + // And then we may have 1 or 2 leftover bytes to encode + int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 + if (inputLeft > 0) { // yes, but do we have room for output? + if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... + _flushBuffer(); + } + int b24 = ((int) input[inputPtr++]) << 16; + if (inputLeft == 2) { + b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; + } + _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail); + } + } + + // write-method called when length is definitely known + protected int _writeBinary(Base64Variant b64variant, + InputStream data, byte[] readBuffer, int bytesLeft) + throws IOException, JsonGenerationException + { + int inputPtr = 0; + int inputEnd = 0; + int lastFullOffset = -3; + + // Let's also reserve room for possible (and quoted) lf char each round + int safeOutputEnd = _outputEnd - 6; + int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; + + while (bytesLeft > 2) { // main loop for full triplets + if (inputPtr > lastFullOffset) { + inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); + inputPtr = 0; + if (inputEnd < 3) { // required to try to read to have at least 3 bytes + break; + } + lastFullOffset = inputEnd-3; + } + if (_outputTail > safeOutputEnd) { // need to flush + _flushBuffer(); + } + int b24 = ((int) readBuffer[inputPtr++]) << 8; + b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; + b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); + bytesLeft -= 3; + _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); + if (--chunksBeforeLF <= 0) { + _outputBuffer[_outputTail++] = '\\'; + _outputBuffer[_outputTail++] = 'n'; + chunksBeforeLF = b64variant.getMaxLineLength() >> 2; + } + } + + // And then we may have 1 or 2 leftover bytes to encode + if (bytesLeft > 0) { + inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); + inputPtr = 0; + if (inputEnd > 0) { // yes, but do we have room for output? + if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... + _flushBuffer(); + } + int b24 = ((int) readBuffer[inputPtr++]) << 16; + int amount; + if (inputPtr < inputEnd) { + b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; + amount = 2; + } else { + amount = 1; + } + _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); + bytesLeft -= amount; + } + } + return bytesLeft; + } + + // write method when length is unknown + protected int _writeBinary(Base64Variant b64variant, + InputStream data, byte[] readBuffer) + throws IOException, JsonGenerationException + { + int inputPtr = 0; + int inputEnd = 0; + int lastFullOffset = -3; + int bytesDone = 0; + + // Let's also reserve room for possible (and quoted) LF char each round + int safeOutputEnd = _outputEnd - 6; + int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; + + // Ok, first we loop through all full triplets of data: + while (true) { + if (inputPtr > lastFullOffset) { // need to load more + inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length); + inputPtr = 0; + if (inputEnd < 3) { // required to try to read to have at least 3 bytes + break; + } + lastFullOffset = inputEnd-3; + } + if (_outputTail > safeOutputEnd) { // need to flush + _flushBuffer(); + } + // First, mash 3 bytes into lsb of 32-bit int + int b24 = ((int) readBuffer[inputPtr++]) << 8; + b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; + b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); + bytesDone += 3; + _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); + if (--chunksBeforeLF <= 0) { + _outputBuffer[_outputTail++] = '\\'; + _outputBuffer[_outputTail++] = 'n'; + chunksBeforeLF = b64variant.getMaxLineLength() >> 2; + } + } + + // And then we may have 1 or 2 leftover bytes to encode + if (inputPtr < inputEnd) { // yes, but do we have room for output? + if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... + _flushBuffer(); + } + int b24 = ((int) readBuffer[inputPtr++]) << 16; + int amount = 1; + if (inputPtr < inputEnd) { + b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; + amount = 2; + } + bytesDone += amount; + _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); + } + return bytesDone; + } + + private int _readMore(InputStream in, + byte[] readBuffer, int inputPtr, int inputEnd, + int maxRead) throws IOException + { + // anything to shift to front? + int i = 0; + while (inputPtr < inputEnd) { + readBuffer[i++] = readBuffer[inputPtr++]; + } + inputPtr = 0; + inputEnd = i; + maxRead = Math.min(maxRead, readBuffer.length); + + do { + int length = maxRead - inputEnd; + if (length == 0) { + break; + } + int count = in.read(readBuffer, inputEnd, length); + if (count < 0) { + return inputEnd; + } + inputEnd += count; + } while (inputEnd < 3); + return inputEnd; + } + + /* + /********************************************************** + /* Internal methods, low-level writing, other + /********************************************************** + */ + + private 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; + } + + protected void _flushBuffer() throws IOException + { + int len = _outputTail - _outputHead; + if (len > 0) { + int offset = _outputHead; + _outputTail = _outputHead = 0; + _writer.write(_outputBuffer, offset, len); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/json/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,7 @@ +/** + * 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; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +/** + * Main public API classes of the core streaming JSON + * processor: most importantly {@link com.fasterxml.jackson.core.JsonFactory} + * used for constructing + * JSON parser ({@link com.fasterxml.jackson.core.JsonParser}) + * and generator + * ({@link com.fasterxml.jackson.core.JsonGenerator}) + * instances. + *

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

+ */ + +package com.fasterxml.jackson.core; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1272 @@ +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 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. + * + * @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_tertiaryStart + * (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; + * this should protected against OOMEs caused by large documents + * 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. + */ + 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 protected ByteQuadsCanonicalizer _parent; + + /** + * Member that is only used by the root table instance: root + * passes immutable state into child instances, and children + * may return new state if they add entries to the table. + * Child tables do NOT use the reference. + */ + final protected AtomicReference _tableInfo; + + /** + * Seed value we use as the base to make hash codes non-static between + * different runs, but still stable for lifetime of a single symbol table + * instance. + * This is done for security reasons, to avoid potential DoS attack via + * hash collisions. + */ + final private int _seed; + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Whether canonical symbol Strings are to be intern()ed before added + * to the table or not. + *

+ * NOTE: non-final to allow disabling intern()ing in case of excessive + * collisions. + */ + protected 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 + */ + protected final boolean _failOnDoS; + + /* + /********************************************************** + /* First, main hash area info + /********************************************************** + */ + + /** + * Primary hash information area: consists of 2 * _hashSize + * entries of 16 bytes (4 ints), arranged in a cascading lookup + * structure (details of which may be tweaked depending on expected rates + * of collisions). + */ + protected 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). + */ + protected int _hashSize; + + /** + * Offset within {@link #_hashArea} where secondary entries start + */ + protected int _secondaryStart; + + /** + * Offset within {@link #_hashArea} where tertiary entries start + */ + protected int _tertiaryStart; + + /** + * Constant that determines size of buckets for tertiary entries: + * 1 << _tertiaryShift is the size, and shift value + * is also used for translating from primary offset into + * tertiary bucket (shift right by 4 + _tertiaryShift). + *

+ * Default value is 2, for buckets of 4 slots; grows bigger with + * bigger table sizes. + */ + protected int _tertiaryShift; + + /** + * Total number of Strings in the symbol table; only used for child tables. + */ + protected int _count; + + /** + * Array that contains String instances matching + * entries in {@link #_hashArea}. + * Contains nulls for unused entries. Note that this size is twice + * that of {@link #_hashArea} + */ + protected 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}. + */ + protected int _spilloverEnd; + + /** + * Offset within {@link #_hashArea} that follows main slots and contains + * quads for longer names (13 bytes or longers), 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}). + */ + protected int _longNameOffset; + + /** + * This flag is set if, after adding a new entry, it is deemed + * that a rehash is warranted if any more entries are to be added. + */ + private transient boolean _needRehash; + + /* + /********************************************************** + /* Sharing, versioning + /********************************************************** + */ + + // // // Which of the buffers may be shared (and are copy-on-write)? + + /** + * Flag that indicates whether underlying data structures for + * the main hash area are shared or not. If they are, then they + * need to be handled in copy-on-write way, i.e. if they need + * to be modified, a copy needs to be made first; at this point + * it will not be shared any more, and can be modified. + *

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

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

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

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

+ * Note that JDK uses 31; but it seems that 33 produces fewer collisions, + * at least with tests we have. + */ + public final static int HASH_MULT = 33; + + /** + * Default initial table size. Shouldn't be miniscule (as there's + * cost to both array realloc and rehashing), but let's keep + * it reasonably small. For systems that properly + * reuse factories it doesn't matter either way; but when + * recreating factories often, initial overhead may dominate. + */ + protected static final int DEFAULT_T_SIZE = 64; + + /** + * Let's not expand symbol tables past some maximum size; + * this should protected against OOMEs caused by large documents + * with unique (~= random) names. + */ + protected static final int MAX_T_SIZE = 0x10000; // 64k entries == 256k mem + + /** + * Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k; + * this corresponds to 64k main hash index. This should allow for enough distinct + * names for almost any case. + */ + final static int MAX_ENTRIES_FOR_REUSE = 12000; + + /** + * Also: to thwart attacks based on hash collisions (which may or may not + * be cheap to calculate), we will need to detect "too long" + * collision chains. Let's start with static value of 255 entries + * for the longest legal chain. + *

+ * Note: longest chain we have been able to produce without malicious + * intent has been 38 (with "com.fasterxml.jackson.core.main.TestWithTonsaSymbols"); + * our setting should be reasonable here. + *

+ * Also note that value was lowered from 255 (2.3 and earlier) to 100 for 2.4 + * + * @since 2.1 + */ + final static int MAX_COLL_CHAIN_LENGTH = 100; + + final static CharsToNameCanonicalizer sBootstrapSymbolTable = new CharsToNameCanonicalizer(); + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Sharing of learnt symbols is done by optional linking of symbol + * table instances with their parents. When parent linkage is + * defined, and child instance is released (call to release), + * parent's shared tables may be updated from the child instance. + */ + protected CharsToNameCanonicalizer _parent; + + /** + * Seed value we use as the base to make hash codes non-static between + * different runs, but still stable for lifetime of a single symbol table + * instance. + * This is done for security reasons, to avoid potential DoS attack via + * hash collisions. + * + * @since 2.1 + */ + final private int _hashSeed; + + final protected int _flags; + + /** + * Whether any canonicalization should be attempted (whether using + * intern or not) + */ + protected boolean _canonicalize; + + /* + /********************************************************** + /* Actual symbol table data + /********************************************************** + */ + + /** + * Primary matching symbols; it's expected most match occur from + * here. + */ + protected String[] _symbols; + + /** + * Overflow buckets; if primary doesn't match, lookup is done + * from here. + *

+ * Note: Number of buckets is half of number of symbol entries, on + * assumption there's less need for buckets. + */ + protected Bucket[] _buckets; + + /** + * Current size (number of entries); needed to know if and when + * rehash. + */ + protected int _size; + + /** + * Limit that indicates maximum size this instance can hold before + * it needs to be expanded and rehashed. Calculated using fill + * factor passed in to constructor. + */ + protected int _sizeThreshold; + + /** + * Mask used to get index from hash values; equal to + * _buckets.length - 1, when _buckets.length is + * a power of two. + */ + protected int _indexMask; + + /** + * We need to keep track of the longest collision list; this is needed + * both to indicate problems with attacks and to allow flushing for + * other cases. + * + * @since 2.1 + */ + protected int _longestCollisionList; + + /* + /********************************************************** + /* State regarding shared arrays + /********************************************************** + */ + + /** + * Flag that indicates if any changes have been made to the data; + * used to both determine if bucket array needs to be copied when + * (first) change is made, and potentially if updated bucket list + * is to be resync'ed back to master instance. + */ + protected boolean _dirty; + + /* + /********************************************************** + /* 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 + */ + protected BitSet _overflows; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Method called to create root canonicalizer for a {@link com.fasterxml.jackson.core.JsonFactory} + * instance. Root instance is never used directly; its main use is for + * storing and sharing underlying symbol arrays as needed. + */ + public static CharsToNameCanonicalizer createRoot() { + /* [Issue-21]: Need to use a variable seed, to thwart hash-collision + * based attacks. + */ + long now = System.currentTimeMillis(); + // ensure it's not 0; and might as well require to be odd so: + int seed = (((int) now) + ((int) (now >>> 32))) | 1; + return createRoot(seed); + } + + protected static CharsToNameCanonicalizer createRoot(int hashSeed) { + return sBootstrapSymbolTable.makeOrphan(hashSeed); + } + + /** + * Main method for constructing a master symbol table instance. + */ + private CharsToNameCanonicalizer() { + // 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: + _dirty = true; + _hashSeed = 0; + _longestCollisionList = 0; + initTables(DEFAULT_T_SIZE); + } + + private void initTables(int initialSize) + { + _symbols = new String[initialSize]; + _buckets = new Bucket[initialSize >> 1]; + // Mask is easy to calc for powers of two. + _indexMask = initialSize - 1; + _size = 0; + _longestCollisionList = 0; + // Hard-coded fill factor is 75% + _sizeThreshold = _thresholdSize(initialSize); + } + + private static int _thresholdSize(int hashAreaSize) { return hashAreaSize - (hashAreaSize >> 2); } + + /** + * Internal constructor used when creating child instances. + */ + private CharsToNameCanonicalizer(CharsToNameCanonicalizer parent, int flags, + String[] symbols, Bucket[] buckets, int size, int hashSeed, int longestColl) { + _parent = parent; + + _flags = flags; + _canonicalize = JsonFactory.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(flags); + + _symbols = symbols; + _buckets = buckets; + _size = size; + _hashSeed = hashSeed; + // Hard-coded fill factor, 75% + int arrayLen = (symbols.length); + _sizeThreshold = _thresholdSize(arrayLen); + _indexMask = (arrayLen - 1); + _longestCollisionList = longestColl; + + // Need to make copies of arrays, if/when adding new entries + _dirty = false; + } + + /** + * "Factory" method; will create a new child instance of this symbol + * table. It will be a copy-on-write instance, ie. it will only use + * read-only copy of parent's data, but when changes are needed, a + * copy will be created. + *

+ * Note: while this method is synchronized, it is generally not + * safe to both use makeChild/mergeChild, AND to use instance + * actively. Instead, a separate 'root' instance should be used + * on which only makeChild/mergeChild are called, but instance itself + * is not used as a symbol table. + */ + public CharsToNameCanonicalizer makeChild(int flags) { + /* 24-Jul-2012, tatu: Trying to reduce scope of synchronization, assuming + * that synchronizing construction is the (potentially) expensive part, + * and not so much short copy-the-variables thing. + */ + final String[] symbols; + final Bucket[] buckets; + final int size; + final int hashSeed; + final int longestCollisionList; + + synchronized (this) { + symbols = _symbols; + buckets = _buckets; + size = _size; + hashSeed = _hashSeed; + longestCollisionList = _longestCollisionList; + } + return new CharsToNameCanonicalizer(this, flags, + symbols, buckets, size, hashSeed, longestCollisionList); + } + + private CharsToNameCanonicalizer makeOrphan(int seed) { + return new CharsToNameCanonicalizer(null, -1, _symbols, _buckets, _size, seed, _longestCollisionList); + } + + /** + * Method that allows contents of child table to potentially be + * "merged in" with contents of this symbol table. + *

+ * Note that caller has to make sure symbol table passed in is + * really a child or sibling of this symbol table. + */ + private void mergeChild(CharsToNameCanonicalizer child) { + /* One caveat: let's try to avoid problems with + * degenerate cases of documents with generated "random" + * names: for these, symbol tables would bloat indefinitely. + * One way to do this is to just purge tables if they grow + * too large, and that's what we'll do here. + */ + if (child.size() > MAX_ENTRIES_FOR_REUSE) { + // Should there be a way to get notified about this event, to log it or such? + // (as it's somewhat abnormal thing to happen) + // At any rate, need to clean up the tables, then: + synchronized (this) { + initTables(DEFAULT_T_SIZE * 4); // no point in starting from tiny tho + // Dirty flag... well, let's just clear it. Shouldn't really matter for master tables + // (which this is, given something is merged to it) + _dirty = false; + } + } else { + // Otherwise, we'll merge changed stuff in, if there are more entries (which + // may not be the case if one of siblings has added symbols first or such) + if (child.size() <= size()) { // nothing to add + return; + } + // Okie dokie, let's get the data in! + synchronized (this) { + _symbols = child._symbols; + _buckets = child._buckets; + _size = child._size; + _sizeThreshold = child._sizeThreshold; + _indexMask = child._indexMask; + _longestCollisionList = child._longestCollisionList; + // Dirty flag... well, let's just clear it. Shouldn't really matter for master tables + // (which this is, given something is merged to it) + _dirty = false; + } + } + } + + public void release(){ + // If nothing has been added, nothing to do + if (!maybeDirty()) { return; } + if (_parent != null && _canonicalize) { // canonicalize set to false if max size was reached + _parent.mergeChild(this); + /* Let's also mark this instance as dirty, so that just in + * case release was too early, there's no corruption + * of possibly shared data. + */ + _dirty = false; + } + } + + /* + /********************************************************** + /* Public API, generic accessors: + /********************************************************** + */ + + public int size() { return _size; } + + /** + * Method for checking number of primary hash buckets this symbol + * table uses. + * + * @since 2.1 + */ + public int bucketCount() { return _symbols.length; } + public boolean maybeDirty() { return _dirty; } + public int hashSeed() { return _hashSeed; } + + /** + * Method mostly needed by unit tests; calculates number of + * entries that are in collision list. Value can be at most + * ({@link #size} - 1), but should usually be much lower, ideally 0. + * + * @since 2.1 + */ + public int collisionCount() { + int count = 0; + + for (Bucket bucket : _buckets) { + if (bucket != null) { + count += bucket.length; + } + } + return count; + } + + /** + * Method mostly needed by unit tests; calculates length of the + * longest collision chain. This should typically be a low number, + * but may be up to {@link #size} - 1 in the pathological case + * + * @since 2.1 + */ + public int maxCollisionLength() { return _longestCollisionList; } + + /* + /********************************************************** + /* Public API, accessing symbols: + /********************************************************** + */ + + public String findSymbol(char[] buffer, int start, int len, int h) + { + if (len < 1) { // empty Strings are simplest to handle up front + return ""; + } + if (!_canonicalize) { // [JACKSON-259] + return new String(buffer, start, len); + } + + /* Related to problems with sub-standard hashing (somewhat + * relevant for collision attacks too), let's try little + * bit of shuffling to improve hash codes. + * (note, however, that this can't help with full collisions) + */ + int index = _hashToIndex(h); + String sym = _symbols[index]; + + // Optimal case; checking existing primary symbol for hash index: + if (sym != null) { + // Let's inline primary String equality checking: + if (sym.length() == len) { + int i = 0; + 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 (!_dirty) { //need to do copy-on-write? + copyArrays(); + _dirty = true; + } else if (_size >= _sizeThreshold) { // Need to expand? + rehash(); + /* Need to recalc hash; rare occurence (index mask has been + * recalculated as part of rehash) + */ + index = _hashToIndex(calcHash(buffer, start, len)); + } + + String newSymbol = new String(buffer, start, len); + if (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); + } else { + _buckets[bix] = newB; + _longestCollisionList = Math.max(collLen, _longestCollisionList); + } + } + + return newSymbol; + } + + private void _handleSpillOverflow(int bindex, Bucket newBucket) + { + if (_overflows == null) { + _overflows = new BitSet(); + _overflows.set(bindex); + } else { + if (_overflows.get(bindex)) { + // Has happened once already, so not a coincident... + 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: + _canonicalize = false; + } else { + _overflows.set(bindex); + } + } + // regardless, if we get this far, clear up the bucket, adjust size appropriately. + _symbols[bindex + bindex] = newBucket.symbol; + _buckets[bindex] = null; + // newBucket contains new symbol; but we wil + _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 = _hashSeed; + 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 = _hashSeed; + for (int i = 0; i < len; ++i) { + hash = (hash * HASH_MULT) + (int) key.charAt(i); + } + // NOTE: shuffling, if any, is done in 'findSymbol()', not here: + return (hash == 0) ? 1 : hash; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * Method called when copy-on-write is needed; generally when first + * change is made to a derived symbol table. + */ + private void copyArrays() { + 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() { + 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; + _dirty = true; + return; + } + + String[] oldSyms = _symbols; + Bucket[] oldBuckets = _buckets; + _symbols = new String[newSize]; + _buckets = new Bucket[newSize >> 1]; + // Let's update index mask, threshold, now (needed for rehashing) + _indexMask = newSize - 1; + _sizeThreshold = _thresholdSize(newSize); + + int count = 0; // let's do sanity check + + /* Need to do two loops, unfortunately, since spill-over area is + * only half the size: + */ + int maxColl = 0; + for (int i = 0; i < size; ++i) { + String symbol = oldSyms[i]; + if (symbol != null) { + ++count; + int index = _hashToIndex(calcHash(symbol)); + if (_symbols[index] == null) { + _symbols[index] = symbol; + } else { + int bix = (index >> 1); + Bucket newB = new Bucket(symbol, _buckets[bix]); + _buckets[bix] = newB; + maxColl = Math.max(maxColl, newB.length); + } + } + } + + size >>= 1; + for (int i = 0; i < size; ++i) { + Bucket b = oldBuckets[i]; + while (b != null) { + ++count; + String symbol = b.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 Error("Internal error on SymbolTable.rehash(): had "+_size+" entries; now have "+count+"."); + } + } + + /** + * @since 2.1 + */ + protected void reportTooManyCollisions(int maxLen) { + throw new IllegalStateException("Longest collision chain in symbol table (of size "+_size + +") now exceeds maximum, "+maxLen+" -- suspect a DoS attack based on hash collisions"); + } + + // 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(); + } +*/ + + /* + /********************************************************** + /* Bucket class + /********************************************************** + */ + + /** + * 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; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,55 @@ +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); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name1.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name1.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name1.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,29 @@ +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); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name2.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name2.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name2.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,32 @@ +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); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name3.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name3.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/Name3.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,36 @@ +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); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/NameN.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/NameN.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/NameN.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,100 @@ +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; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/sym/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,5 @@ +/** + * Internal implementation classes for efficient handling of + * of symbols in JSON (field names in Objects) + */ +package com.fasterxml.jackson.core.sym; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/ResolvedType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/ResolvedType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/ResolvedType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,169 @@ +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 JavaType from "databind" bundle -- this + * abstraction is only needed so that types can be passed through + * {@link com.fasterxml.jackson.core.JsonParser#readValueAs} methods. + * + * @since 2.0 + */ +public abstract class ResolvedType +{ + /* + /********************************************************** + /* Public API, simple property accessors + /********************************************************** + */ + + /** + * Accessor for type-erased {@link Class} of resolved type. + */ + public abstract Class getRawClass(); + + public abstract boolean hasRawClass(Class clz); + + public abstract boolean isAbstract(); + + public abstract boolean isConcrete(); + + public abstract boolean isThrowable(); + + public abstract boolean isArrayType(); + + public abstract boolean isEnumType(); + + public abstract boolean isInterface(); + + public abstract boolean isPrimitive(); + + public abstract boolean isFinal(); + + public abstract boolean isContainerType(); + + public abstract boolean isCollectionLikeType(); + + /** + * Whether this type is a referential type, meaning that values are + * basically pointers to "real" values (or null) and not regular + * values themselves. Typical examples include things like + * {@link java.util.concurrent.atomic.AtomicReference}, and various + * Optional types (in JDK8, Guava). + * + * @since 2.6 + */ + public boolean isReferenceType() { + return getReferencedType() != null; + } + + public abstract boolean isMapLikeType(); + + /* + /********************************************************** + /* Public API, type parameter access + /********************************************************** + */ + + /** + * Method that can be used to find out if the type directly declares generic + * parameters (for its direct super-class and/or super-interfaces). + */ + public abstract boolean hasGenericTypes(); + + /** + * Accessor that can be used to find out type for which parameterization + * is applied: this is often NOT same as what {@link #getRawClass} returns, + * but rather one of it supertype. + *

+ * For example: for type like {@link java.util.HashMap}, raw type is + * {@link java.util.HashMap}; but this method would return + * {@link java.util.Map}, because relevant type parameters that are + * resolved (and accessible using {@link #containedType(int)} and + * {@link #getKeyType()}) are parameter for {@link java.util.Map} + * (which may or may not be same as type parameters for subtype; + * in case of {@link java.util.HashMap} they are, but for further + * subtypes they may be different parameters or possibly none at all). + * + * @since 2.5 + * + * @deprecated Since 2.7: does not have meaning as parameters depend on type + * resolved. + */ + @Deprecated // since 2.7 + public Class getParameterSource() { + return null; + } + + /** + * Method for accessing key type for this type, assuming type + * has such a concept (only Map types do) + */ + public abstract ResolvedType getKeyType(); + + /** + * Method for accessing content type of this type, if type has + * such a thing: simple types do not, structured types do + * (like arrays, Collections and Maps) + */ + public abstract ResolvedType getContentType(); + + /** + * Method for accessing type of value that instances of this + * type references, if any. + * + * @return Referenced type, if any; null if not. + * + * @since 2.6 + */ + public abstract ResolvedType getReferencedType(); + + /** + * Method for checking how many contained types this type + * has. Contained types are usually generic types, so that + * generic Maps have 2 contained types. + */ + public abstract int containedTypeCount(); + + /** + * Method for accessing definitions of contained ("child") + * types. + * + * @param index Index of contained type to return + * + * @return Contained type at index, or null if no such type + * exists (no exception thrown) + */ + public abstract ResolvedType containedType(int index); + + /** + * Method for accessing name of type variable in indicated + * position. If no name is bound, will use placeholders (derived + * from 0-based index); if no type variable or argument exists + * with given index, null is returned. + * + * @param index Index of contained type to return + * + * @return Contained type at index, or null if no such type + * exists (no exception thrown) + */ + public abstract String containedTypeName(int index); + + /* + /********************************************************** + /* Public API, other + /********************************************************** + */ + + /** + * Method that can be used to serialize type into form from which + * it can be fully deserialized from at a later point (using + * TypeFactory from mapper package). + * For simple types this is same as calling + * {@link Class#getName}, but for structured types it may additionally + * contain type information about contents. + */ + public abstract String toCanonical(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/TypeReference.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/TypeReference.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/TypeReference.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,58 @@ +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. + *

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

+ *  TypeReference ref = new TypeReference<List<Integer>>() { };
+ *
+ * which can be passed to methods that accept TypeReference, or resolved + * using TypeFactory to obtain {@link ResolvedType}. + */ +public abstract class TypeReference implements Comparable> +{ + protected final Type _type; + + protected TypeReference() + { + Type superClass = getClass().getGenericSuperclass(); + if (superClass instanceof Class) { // sanity check, should never happen + throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information"); + } + /* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect + * it is possible to make it fail? + * But let's deal with specific + * case when we know an actual use case, and thereby suitable + * workarounds for valid case(s) and/or error to throw + * on invalid one(s). + */ + _type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; + } + + public Type getType() { return _type; } + + /** + * The only reason we define this method (and require implementation + * of Comparable) is to prevent constructing a + * reference without type information. + */ + @Override + public int compareTo(TypeReference o) { return 0; } + // just need an implementation, not a good one... hence ^^^ +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/type/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,8 @@ +/** + * Contains classes needed for type introspection, mostly used by data binding + * functionality. Most of this functionality is needed to properly handled + * generic types, and to simplify and unify processing of things Jackson needs + * to determine how contained types (of {@link java.util.Collection} and + * {@link java.util.Map} classes) are to be handled. + */ +package com.fasterxml.jackson.core.type; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/BufferRecycler.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/BufferRecycler.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/BufferRecycler.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,157 @@ +package com.fasterxml.jackson.core.util; + +/** + * This is a small utility class, whose main functionality is to allow + * simple reuse of raw byte/char buffers. It is usually used through + * ThreadLocal member of the owning class pointing to + * instance of this class through a SoftReference. The + * end result is a low-overhead GC-cleanable recycling: hopefully + * ideal for use by stream readers. + */ +public class BufferRecycler +{ + /** + * 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; + + public final static int CHAR_TOKEN_BUFFER = 0; // Tokenizable input + public final static int CHAR_CONCAT_BUFFER = 1; // concatenated output + public final static int CHAR_TEXT_BUFFER = 2; // Text content from input + public final static int CHAR_NAME_COPY_BUFFER = 3; // Temporary buffer for getting name characters + + // Buffer lengths, defined in 2.4 (smaller before that) + + 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 }; + + final protected byte[][] _byteBuffers; + final protected char[][] _charBuffers; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + /** + * Default constructor used for creating instances of this default + * implementation. + */ + public BufferRecycler() { + this(4, 4); + } + + /** + * Alternate constructor to be used by sub-classes, to allow customization + * of number of low-level buffers in use. + * + * @since 2.4 + */ + protected BufferRecycler(int bbCount, int cbCount) { + _byteBuffers = new byte[bbCount][]; + _charBuffers = new char[cbCount][]; + } + + /* + /********************************************************** + /* Public API, byte buffers + /********************************************************** + */ + + /** + * @param ix One of READ_IO_BUFFER constants. + */ + public final byte[] allocByteBuffer(int ix) { + return allocByteBuffer(ix, 0); + } + + public byte[] allocByteBuffer(int ix, int minSize) { + final int DEF_SIZE = byteBufferLength(ix); + if (minSize < DEF_SIZE) { + minSize = DEF_SIZE; + } + byte[] buffer = _byteBuffers[ix]; + if (buffer == null || buffer.length < minSize) { + buffer = balloc(minSize); + } else { + _byteBuffers[ix] = null; + } + return buffer; + } + + public final void releaseByteBuffer(int ix, byte[] buffer) { + _byteBuffers[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[ix]; + if (buffer == null || buffer.length < minSize) { + buffer = calloc(minSize); + } else { + _charBuffers[ix] = null; + } + return buffer; + } + + public void releaseCharBuffer(int ix, char[] buffer) { + _charBuffers[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]; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/ByteArrayBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/ByteArrayBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/ByteArrayBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,244 @@ +/* Jackson JSON-processor. + * + * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + */ + +package com.fasterxml.jackson.core.util; + +import java.io.OutputStream; +import java.util.*; + +/** + * Helper class that is similar to {@link java.io.ByteArrayOutputStream} + * in usage, but more geared to Jackson use cases internally. + * Specific changes include segment storage (no need to have linear + * backing buffer, can avoid reallocations, copying), as well API + * not based on {@link java.io.OutputStream}. In short, a very much + * specialized builder object. + *

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

+ * Note: simply constructs a {@link SerializedString} out of parameter, + * calls {@link #DefaultPrettyPrinter(SerializableString)} + * + * @param rootSeparator + * + * @since 2.1 + */ + public DefaultPrettyPrinter(String rootSeparator) { + this((rootSeparator == null) ? null : new SerializedString(rootSeparator)); + } + + /** + * Constructor that specifies separator String to use between root values; + * if null, no separator is printed. + * + * @param rootSeparator + * + * @since 2.1 + */ + public DefaultPrettyPrinter(SerializableString rootSeparator) { + _rootSeparator = rootSeparator; + } + + public DefaultPrettyPrinter(DefaultPrettyPrinter base) { + this(base, base._rootSeparator); + } + + public DefaultPrettyPrinter(DefaultPrettyPrinter base, + SerializableString rootSeparator) + { + _arrayIndenter = base._arrayIndenter; + _objectIndenter = base._objectIndenter; + _spacesInObjectEntries = base._spacesInObjectEntries; + _nesting = base._nesting; + + _rootSeparator = rootSeparator; + } + + public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator) + { + if (_rootSeparator == rootSeparator || + (rootSeparator != null && rootSeparator.equals(_rootSeparator))) { + return this; + } + return new DefaultPrettyPrinter(this, rootSeparator); + } + + /** + * @since 2.6.0 + */ + 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; + } + + /** + * @deprecated Since 2.3 use {@link #withSpacesInObjectEntries} and {@link #withoutSpacesInObjectEntries()} + */ + @Deprecated + public void spacesInObjectEntries(boolean b) { _spacesInObjectEntries = b; } + + /** + * @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; + } + + /* + /********************************************************** + /* Instantiatable impl + /********************************************************** + */ + + @Override + public DefaultPrettyPrinter createInstance() { + return new DefaultPrettyPrinter(this); + } + + /* + /********************************************************** + /* PrettyPrinter impl + /********************************************************** + */ + + @Override + public void writeRootValueSeparator(JsonGenerator jg) throws IOException + { + if (_rootSeparator != null) { + jg.writeRaw(_rootSeparator); + } + } + + @Override + public void writeStartObject(JsonGenerator jg) throws IOException + { + jg.writeRaw('{'); + if (!_objectIndenter.isInline()) { + ++_nesting; + } + } + + @Override + public void beforeObjectEntries(JsonGenerator jg) throws IOException + { + _objectIndenter.writeIndentation(jg, _nesting); + } + + /** + * Method called after an object field has been output, but + * before the value is output. + *

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

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

+ * Default handling (without pretty-printing) will output a single + * comma to separate the two. Pretty-printer is + * to output a comma as well, but can surround that with other + * (white-space) decoration. + */ + @Override + public void writeArrayValueSeparator(JsonGenerator gen) throws IOException + { + gen.writeRaw(','); + _arrayIndenter.writeIndentation(gen, _nesting); + } + + @Override + public void writeEndArray(JsonGenerator gen, int nrOfValues) throws IOException + { + if (!_arrayIndenter.isInline()) { + --_nesting; + } + if (nrOfValues > 0) { + _arrayIndenter.writeIndentation(gen, _nesting); + } else { + gen.writeRaw(' '); + } + gen.writeRaw(']'); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Dummy implementation that adds no indentation whatsoever + */ + public static class NopIndenter + implements Indenter, java.io.Serializable + { + public static final NopIndenter instance = new NopIndenter(); + + @Override + public void writeIndentation(JsonGenerator jg, int level) throws IOException { } + + @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 + { + @SuppressWarnings("hiding") + public static final FixedSpaceIndenter instance = new FixedSpaceIndenter(); + + @Override + public void writeIndentation(JsonGenerator jg, int level) throws IOException + { + jg.writeRaw(' '); + } + + @Override + public boolean isInline() { return true; } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/Instantiatable.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/Instantiatable.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/Instantiatable.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,24 @@ +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 +{ + /** + * Method called to ensure that we have a non-blueprint object to use; + * it is either this object (if stateless), or a newly created object + * with separate state. + */ + T createInstance(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/InternCache.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/InternCache.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/InternCache.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,64 @@ +package com.fasterxml.jackson.core.util; + +import java.util.LinkedHashMap; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Singleton class that adds a simple first-level cache in front of + * regular String.intern() functionality. This is done as a minor + * performance optimization, to avoid calling native intern() method + * in cases where same String is being interned multiple times. + *

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

+ * One consideration is possible attack via colliding {@link String#hashCode}; + * because of this, limit to reasonably low setting. + */ + private final static int MAX_ENTRIES = 180; + + public final static InternCache instance = new InternCache(); + + /** + * As minor optimization let's try to avoid "flush storms", + * cases where multiple threads might try to concurrently + * flush the map. + */ + private final Object lock = new Object(); + + private InternCache() { super(MAX_ENTRIES, 0.8f, 4); } + + public String intern(String input) { + String result = get(input); + if (result != null) { return result; } + + /* 18-Sep-2013, tatu: We used to use LinkedHashMap, which has simple LRU + * method. No such functionality exists with CHM; and let's use simplest + * possible limitation: just clear all contents. This because otherwise + * we are simply likely to keep on clearing same, commonly used entries. + */ + if (size() >= MAX_ENTRIES) { + /* Not incorrect wrt well-known double-locking anti-pattern because underlying + * storage gives close enough answer to real one here; and we are + * more concerned with flooding than starvation. + */ + synchronized (lock) { + if (size() >= MAX_ENTRIES) { + clear(); + } + } + } + result = input.intern(); + put(result, result); + return result; + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,421 @@ +package com.fasterxml.jackson.core.util; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharacterEscapes; + +public class JsonGeneratorDelegate extends JsonGenerator +{ + /** + * Delegate object that method calls are delegated to. + */ + protected JsonGenerator delegate; + + /** + * Whether copy methods + * ({@link #copyCurrentEvent}, {@link #copyCurrentStructure}, {@link #writeTree} and {@link #writeObject}) + * are to be called (true), or handled by this object (false). + */ + protected boolean delegateCopyMethods; + + /* + /********************************************************** + /* Construction, initialization + /********************************************************** + */ + + public JsonGeneratorDelegate(JsonGenerator d) { + this(d, true); + } + + /** + * @param delegateCopyMethods Flag assigned to delagateCopyMethod + * and which defines whether copy methods are handled locally (false), or + * delegated to configured + */ + public JsonGeneratorDelegate(JsonGenerator d, boolean delegateCopyMethods) { + delegate = d; + this.delegateCopyMethods = delegateCopyMethods; + } + + @Override + public Object getCurrentValue() { + return delegate.getCurrentValue(); + } + + @Override + public void setCurrentValue(Object v) { + delegate.setCurrentValue(v); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + public JsonGenerator getDelegate() { return delegate; } + + /* + /********************************************************** + /* Public API, metadata + /********************************************************** + */ + + @Override public ObjectCodec getCodec() { return delegate.getCodec(); } + + @Override public JsonGenerator setCodec(ObjectCodec oc) { + delegate.setCodec(oc); + return this; + } + + @Override public void setSchema(FormatSchema schema) { delegate.setSchema(schema); } + @Override public FormatSchema getSchema() { return delegate.getSchema(); } + @Override public Version version() { return delegate.version(); } + @Override public Object getOutputTarget() { return delegate.getOutputTarget(); } + @Override public int getOutputBuffered() { return delegate.getOutputBuffered(); } + + /* + /********************************************************** + /* Public API, capability introspection (since 2.3, mostly) + /********************************************************** + */ + + @Override + public boolean canUseSchema(FormatSchema schema) { return delegate.canUseSchema(schema); } + + @Override + public boolean canWriteTypeId() { return delegate.canWriteTypeId(); } + + @Override + public boolean canWriteObjectId() { return delegate.canWriteObjectId(); } + + @Override + public boolean canWriteBinaryNatively() { return delegate.canWriteBinaryNatively(); } + + @Override + public boolean canOmitFields() { return delegate.canOmitFields(); } + + /* + /********************************************************** + /* Public API, configuration + /********************************************************** + */ + + @Override + public JsonGenerator enable(Feature f) { + delegate.enable(f); + return this; + } + + @Override + public JsonGenerator disable(Feature f) { + delegate.disable(f); + return this; + } + + @Override + public boolean isEnabled(Feature f) { return delegate.isEnabled(f); } + + // final, can't override (and no need to) + //public final JsonGenerator configure(Feature f, boolean state) + + @Override + public int getFeatureMask() { return delegate.getFeatureMask(); } + + @Override + @Deprecated + public JsonGenerator setFeatureMask(int mask) { + delegate.setFeatureMask(mask); + return this; + } + + @Override + public JsonGenerator overrideStdFeatures(int values, int mask) { + delegate.overrideStdFeatures(values, mask); + return this; + } + + @Override + public JsonGenerator overrideFormatFeatures(int values, int mask) { + delegate.overrideFormatFeatures(values, mask); + return this; + } + + /* + /********************************************************** + /* Configuring generator + /********************************************************** + */ + + @Override + public JsonGenerator setPrettyPrinter(PrettyPrinter pp) { + delegate.setPrettyPrinter(pp); + return this; + } + + @Override + public PrettyPrinter getPrettyPrinter() { return delegate.getPrettyPrinter(); } + + @Override + public JsonGenerator useDefaultPrettyPrinter() { delegate.useDefaultPrettyPrinter(); + return this; } + + @Override + public JsonGenerator setHighestNonEscapedChar(int charCode) { delegate.setHighestNonEscapedChar(charCode); + return this; } + + @Override + public int getHighestEscapedChar() { return delegate.getHighestEscapedChar(); } + + @Override + public CharacterEscapes getCharacterEscapes() { return delegate.getCharacterEscapes(); } + + @Override + public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { delegate.setCharacterEscapes(esc); + return this; } + + @Override + public JsonGenerator setRootValueSeparator(SerializableString sep) { delegate.setRootValueSeparator(sep); + return this; } + + /* + /********************************************************** + /* Public API, write methods, structural + /********************************************************** + */ + + @Override + public void writeStartArray() throws IOException { delegate.writeStartArray(); } + + @Override + public void writeStartArray(int size) throws IOException { delegate.writeStartArray(size); } + + @Override + public void writeEndArray() throws IOException { delegate.writeEndArray(); } + + @Override + public void writeStartObject() throws IOException { delegate.writeStartObject(); } + + @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); } + + /* + /********************************************************** + /* Public API, write methods, text/String values + /********************************************************** + */ + + @Override + public void writeString(String text) throws IOException { delegate.writeString(text); } + + @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); } + + /* + /********************************************************** + /* Public API, write methods, serializing Java objects + /********************************************************** + */ + + @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, convenience field write methods + /********************************************************** + */ + + // // These are fine, just delegate to other methods... + + /* + /********************************************************** + /* Public API, copy-through methods + /********************************************************** + */ + + @Override + public void copyCurrentEvent(JsonParser jp) throws IOException { + 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); + } + + /* + /********************************************************** + /* 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(); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonParserDelegate.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonParserDelegate.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonParserDelegate.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,230 @@ +package com.fasterxml.jackson.core.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; + +/** + * Helper class that implements + * delegation pattern for {@link JsonParser}, + * to allow for simple overridability of basic parsing functionality. + * The idea is that any functionality to be modified can be simply + * overridden; and anything else will be delegated by default. + */ +public class JsonParserDelegate extends JsonParser +{ + /** + * Delegate object that method calls are delegated to. + */ + protected JsonParser delegate; + + public JsonParserDelegate(JsonParser d) { + delegate = d; + } + + @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 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(); } + + /* + /********************************************************** + /* 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(); } + + /* + /********************************************************** + /* 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 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(); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonParserSequence.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonParserSequence.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/JsonParserSequence.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,145 @@ +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}. + *

+ * Fairly simple use of {@link JsonParserDelegate}: only need + * to override {@link #nextToken} to handle transition + */ +public class JsonParserSequence extends JsonParserDelegate +{ + /** + * Parsers other than the first one (which is initially assigned + * as delegate) + */ + protected final JsonParser[] _parsers; + + /** + * Index of the next parser in {@link #_parsers}. + */ + protected int _nextParser; + + /* + ******************************************************* + * Construction + ******************************************************* + */ + + protected JsonParserSequence(JsonParser[] parsers) + { + super(parsers[0]); + _parsers = parsers; + _nextParser = 1; + } + + /** + * Method that will construct a parser (possibly a sequence) that + * contains all given sub-parsers. + * All parsers given are checked to see if they are sequences: and + * if so, they will be "flattened", that is, contained parsers are + * directly added in a new sequence instead of adding sequences + * within sequences. This is done to minimize delegation depth, + * ideally only having just a single level of delegation. + */ + public static JsonParserSequence createFlattened(JsonParser first, JsonParser second) + { + if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) { + // simple: + return new JsonParserSequence(new JsonParser[] { first, second }); + } + ArrayList p = new ArrayList(); + if (first instanceof JsonParserSequence) { + ((JsonParserSequence) first).addFlattenedActiveParsers(p); + } else { + p.add(first); + } + if (second instanceof JsonParserSequence) { + ((JsonParserSequence) second).addFlattenedActiveParsers(p); + } else { + p.add(second); + } + return new JsonParserSequence(p.toArray(new JsonParser[p.size()])); + } + + @SuppressWarnings("resource") + protected void addFlattenedActiveParsers(List result) + { + for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) { + JsonParser p = _parsers[i]; + if (p instanceof JsonParserSequence) { + ((JsonParserSequence) p).addFlattenedActiveParsers(result); + } else { + result.add(p); + } + } + } + + /* + ******************************************************* + * Overridden methods, needed: cases where default + * delegation does not work + ******************************************************* + */ + + @Override + public void close() throws IOException { + do { delegate.close(); } while (switchToNext()); + } + + @Override + public JsonToken nextToken() throws IOException, JsonParseException + { + JsonToken t = delegate.nextToken(); + if (t != null) return t; + while (switchToNext()) { + t = delegate.nextToken(); + if (t != null) return t; + } + return null; + } + + /* + /******************************************************* + /* Additional extended API + /******************************************************* + */ + + /** + * Method that is most useful for debugging or testing; + * returns actual number of underlying parsers sequence + * was constructed with (nor just ones remaining active) + */ + public int containedParsersCount() { + return _parsers.length; + } + + /* + /******************************************************* + /* Helper methods + /******************************************************* + */ + + /** + * Method that will switch active parser from the current one + * to next parser in sequence, if there is another parser left, + * making this the new delegate. Old delegate is returned if + * switch succeeds. + * + * @return True if switch succeeded; false otherwise + */ + protected boolean switchToNext() + { + if (_nextParser >= _parsers.length) { + return false; + } + delegate = _parsers[_nextParser++]; + return true; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,153 @@ +package com.fasterxml.jackson.core.util; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.PrettyPrinter; + +/** + * {@link PrettyPrinter} implementation that adds no indentation, + * just implements everything necessary for value output to work + * as expected, and provide simpler extension points to allow + * for creating simple custom implementations that add specific + * decoration or overrides. Since behavior then is very similar + * to using no pretty printer at all, usually sub-classes are used. + *

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

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

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

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

+ * Default handling (without pretty-printing) will output a single + * comma to separate values. + */ + @Override + public void writeArrayValueSeparator(JsonGenerator jg) + throws IOException, JsonGenerationException + { + jg.writeRaw(','); + } + + @Override + public void writeEndArray(JsonGenerator jg, int nrOfValues) + throws IOException, JsonGenerationException + { + jg.writeRaw(']'); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/TextBuffer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/TextBuffer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/TextBuffer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,740 @@ +package com.fasterxml.jackson.core.util; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; + +import com.fasterxml.jackson.core.io.NumberInput; + +/** + * TextBuffer is a class similar to {@link StringBuffer}, with + * following differences: + *

    + *
  • TextBuffer uses segments character arrays, to avoid having + * to do additional array copies when array is not big enough. + * This means that only reallocating that is necessary is done only once: + * if and when caller + * wants to access contents in a linear array (char[], String). + *
  • +*
  • TextBuffer can also be initialized in "shared mode", in which +* it will just act as a wrapper to a single char array managed +* by another object (like parser that owns it) + *
  • + *
  • TextBuffer is not synchronized. + *
  • + *
+ */ +public final class TextBuffer +{ + final static char[] NO_CHARS = new char[0]; + + /** + * Let's start with sizable but not huge buffer, will grow as necessary + */ + final static int MIN_SEGMENT_LEN = 1000; + + /** + * Let's limit maximum segment length to something sensible + * like 256k + */ + final static int MAX_SEGMENT_LEN = 0x40000; + + /* + /********************************************************** + /* Configuration: + /********************************************************** + */ + + private final BufferRecycler _allocator; + + /* + /********************************************************** + /* Shared input buffers + /********************************************************** + */ + + /** + * Shared input buffer; stored here in case some input can be returned + * as is, without being copied to collector's own buffers. Note that + * this is read-only for this Object. + */ + private char[] _inputBuffer; + + /** + * Character offset of first char in input buffer; -1 to indicate + * that input buffer currently does not contain any useful char data + */ + private int _inputStart; + + private int _inputLen; + + /* + /********************************************************** + /* Aggregation segments (when not using input buf) + /********************************************************** + */ + + /** + * List of segments prior to currently active segment. + */ + private ArrayList _segments; + + /** + * Flag that indicates whether _seqments is non-empty + */ + private boolean _hasSegments; + + // // // Currently used segment; not (yet) contained in _seqments + + /** + * Amount of characters in segments in {@link _segments} + */ + private int _segmentSize; + + private char[] _currentSegment; + + /** + * Number of characters in currently active (last) segment + */ + private int _currentSize; + + /* + /********************************************************** + /* Caching of results + /********************************************************** + */ + + /** + * String that will be constructed when the whole contents are + * needed; will be temporarily stored in case asked for again. + */ + private String _resultString; + + private char[] _resultArray; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public TextBuffer(BufferRecycler allocator) { + _allocator = allocator; + } + + /** + * Method called to indicate that the underlying buffers should now + * be recycled if they haven't yet been recycled. Although caller + * can still use this text buffer, it is not advisable to call this + * method if that is likely, since next time a buffer is needed, + * buffers need to reallocated. + * Note: calling this method automatically also clears contents + * of the buffer. + */ + public void releaseBuffers() + { + if (_allocator == null) { + resetWithEmpty(); + } else { + if (_currentSegment != null) { + // First, let's get rid of all but the largest char array + resetWithEmpty(); + // And then return that array + char[] buf = _currentSegment; + _currentSegment = null; + _allocator.releaseCharBuffer(BufferRecycler.CHAR_TEXT_BUFFER, buf); + } + } + } + + /** + * Method called to clear out any content text buffer may have, and + * initializes buffer to use non-shared data. + */ + public void resetWithEmpty() + { + _inputStart = -1; // indicates shared buffer not used + _currentSize = 0; + _inputLen = 0; + + _inputBuffer = null; + _resultString = null; + _resultArray = null; + + // And then reset internal input buffers, if necessary: + if (_hasSegments) { + clearSegments(); + } + } + + /** + * Method called to initialize the buffer with a shared copy of data; + * this means that buffer will just have pointers to actual data. It + * also means that if anything is to be appended to the buffer, it + * will first have to unshare it (make a local copy). + */ + public void resetWithShared(char[] buf, int start, int len) + { + // First, let's clear intermediate values, if any: + _resultString = null; + _resultArray = null; + + // Then let's mark things we need about input buffer + _inputBuffer = buf; + _inputStart = start; + _inputLen = len; + + // And then reset internal input buffers, if necessary: + if (_hasSegments) { + clearSegments(); + } + } + + public void resetWithCopy(char[] buf, int start, int len) + { + _inputBuffer = null; + _inputStart = -1; // indicates shared buffer not used + _inputLen = 0; + + _resultString = null; + _resultArray = null; + + // And then reset internal input buffers, if necessary: + if (_hasSegments) { + clearSegments(); + } else if (_currentSegment == null) { + _currentSegment = buf(len); + } + _currentSize = _segmentSize = 0; + append(buf, start, len); + } + + public void resetWithString(String value) + { + _inputBuffer = null; + _inputStart = -1; + _inputLen = 0; + + _resultString = value; + _resultArray = null; + + if (_hasSegments) { + clearSegments(); + } + _currentSize = 0; + + } + + /** + * Helper method used to find a buffer to use, ideally one + * recycled earlier. + */ + private char[] buf(int needed) + { + if (_allocator != null) { + return _allocator.allocCharBuffer(BufferRecycler.CHAR_TEXT_BUFFER, needed); + } + return new char[Math.max(needed, MIN_SEGMENT_LEN)]; + } + + private void clearSegments() + { + _hasSegments = false; + /* Let's start using _last_ segment from list; for one, it's + * the biggest one, and it's also most likely to be cached + */ + /* 28-Aug-2009, tatu: Actually, the current segment should + * be the biggest one, already + */ + //_currentSegment = _segments.get(_segments.size() - 1); + _segments.clear(); + _currentSize = _segmentSize = 0; + } + + /* + /********************************************************** + /* Accessors for implementing public interface + /********************************************************** + */ + + /** + * @return Number of characters currently stored by this collector + */ + public int size() { + if (_inputStart >= 0) { // shared copy from input buf + return _inputLen; + } + if (_resultArray != null) { + return _resultArray.length; + } + if (_resultString != null) { + return _resultString.length(); + } + // local segmented buffers + return _segmentSize + _currentSize; + } + + public int getTextOffset() { + /* Only shared input buffer can have non-zero offset; buffer + * segments start at 0, and if we have to create a combo buffer, + * that too will start from beginning of the buffer + */ + return (_inputStart >= 0) ? _inputStart : 0; + } + + /** + * Method that can be used to check whether textual contents can + * be efficiently accessed using {@link #getTextBuffer}. + */ + public boolean hasTextAsCharacters() + { + // if we have array in some form, sure + if (_inputStart >= 0 || _resultArray != null) return true; + // not if we have String as value + if (_resultString != null) return false; + return true; + } + + /** + * Accessor that may be used to get the contents of this buffer in a single + * char array regardless of whether they were collected in a segmented + * fashion or not. + */ + public char[] getTextBuffer() + { + // Are we just using shared input buffer? + if (_inputStart >= 0) return _inputBuffer; + if (_resultArray != null) return _resultArray; + if (_resultString != null) { + return (_resultArray = _resultString.toCharArray()); + } + // Nope; but does it fit in just one segment? + if (!_hasSegments) { + return (_currentSegment == null) ? NO_CHARS : _currentSegment; + } + // Nope, need to have/create a non-segmented array and return it + return contentsAsArray(); + } + + /* + /********************************************************** + /* Other accessors: + /********************************************************** + */ + + public String contentsAsString() + { + if (_resultString == null) { + // Has array been requested? Can make a shortcut, if so: + if (_resultArray != null) { + _resultString = new String(_resultArray); + } else { + // Do we use shared array? + if (_inputStart >= 0) { + if (_inputLen < 1) { + return (_resultString = ""); + } + _resultString = new String(_inputBuffer, _inputStart, _inputLen); + } else { // nope... need to copy + // But first, let's see if we have just one buffer + int segLen = _segmentSize; + int currLen = _currentSize; + + if (segLen == 0) { // yup + _resultString = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen); + } else { // no, need to combine + StringBuilder sb = new StringBuilder(segLen + currLen); + // First stored segments + if (_segments != null) { + for (int i = 0, len = _segments.size(); i < len; ++i) { + char[] curr = _segments.get(i); + sb.append(curr, 0, curr.length); + } + } + // And finally, current segment: + sb.append(_currentSegment, 0, _currentSize); + _resultString = sb.toString(); + } + } + } + } + return _resultString; + } + + public char[] contentsAsArray() { + char[] result = _resultArray; + if (result == null) { + _resultArray = result = resultArray(); + } + return result; + } + + /** + * Convenience method for converting contents of the buffer + * into a {@link BigDecimal}. + */ + public BigDecimal contentsAsDecimal() throws NumberFormatException + { + // Already got a pre-cut array? + if (_resultArray != null) { + return NumberInput.parseBigDecimal(_resultArray); + } + // Or a shared buffer? + if ((_inputStart >= 0) && (_inputBuffer != null)) { + return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen); + } + // Or if not, just a single buffer (the usual case) + if ((_segmentSize == 0) && (_currentSegment != null)) { + return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize); + } + // If not, let's just get it aggregated... + return NumberInput.parseBigDecimal(contentsAsArray()); + } + + /** + * Convenience method for converting contents of the buffer + * into a Double value. + */ + public double contentsAsDouble() throws NumberFormatException { + return NumberInput.parseDouble(contentsAsString()); + } + + /* + /********************************************************** + /* Public mutators: + /********************************************************** + */ + + /** + * Method called to make sure that buffer is not using shared input + * buffer; if it is, it will copy such contents to private buffer. + */ + public void ensureNotShared() { + if (_inputStart >= 0) { + unshare(16); + } + } + + public void append(char c) { + // Using shared buffer so far? + if (_inputStart >= 0) { + unshare(16); + } + _resultString = null; + _resultArray = null; + // Room in current segment? + char[] curr = _currentSegment; + if (_currentSize >= curr.length) { + expand(1); + curr = _currentSegment; + } + curr[_currentSize++] = c; + } + + public void append(char[] c, int start, int len) + { + // Can't append to shared buf (sanity check) + if (_inputStart >= 0) { + unshare(len); + } + _resultString = null; + _resultArray = null; + + // Room in current segment? + char[] curr = _currentSegment; + int max = curr.length - _currentSize; + + if (max >= len) { + System.arraycopy(c, start, curr, _currentSize, len); + _currentSize += len; + return; + } + // No room for all, need to copy part(s): + if (max > 0) { + System.arraycopy(c, start, curr, _currentSize, max); + start += max; + len -= max; + } + /* And then allocate new segment; we are guaranteed to now + * have enough room in segment. + */ + // Except, as per [Issue-24], not for HUGE appends... so: + do { + expand(len); + int amount = Math.min(_currentSegment.length, len); + System.arraycopy(c, start, _currentSegment, 0, amount); + _currentSize += amount; + start += amount; + len -= amount; + } while (len > 0); + } + + public void append(String str, int offset, int len) + { + // Can't append to shared buf (sanity check) + if (_inputStart >= 0) { + unshare(len); + } + _resultString = null; + _resultArray = null; + + // Room in current segment? + char[] curr = _currentSegment; + int max = curr.length - _currentSize; + if (max >= len) { + str.getChars(offset, offset+len, curr, _currentSize); + _currentSize += len; + return; + } + // No room for all, need to copy part(s): + if (max > 0) { + str.getChars(offset, offset+max, curr, _currentSize); + len -= max; + offset += max; + } + /* And then allocate new segment; we are guaranteed to now + * have enough room in segment. + */ + // Except, as per [Issue-24], not for HUGE appends... so: + do { + expand(len); + int amount = Math.min(_currentSegment.length, len); + str.getChars(offset, offset+amount, _currentSegment, 0); + _currentSize += amount; + offset += amount; + len -= amount; + } while (len > 0); + } + + /* + /********************************************************** + /* Raw access, for high-performance use: + /********************************************************** + */ + + public char[] getCurrentSegment() + { + /* Since the intention of the caller is to directly add stuff into + * buffers, we should NOT have anything in shared buffer... ie. may + * need to unshare contents. + */ + if (_inputStart >= 0) { + unshare(1); + } else { + char[] curr = _currentSegment; + if (curr == null) { + _currentSegment = buf(0); + } else if (_currentSize >= curr.length) { + // Plus, we better have room for at least one more char + expand(1); + } + } + return _currentSegment; + } + + public char[] emptyAndGetCurrentSegment() + { + // inlined 'resetWithEmpty()' + _inputStart = -1; // indicates shared buffer not used + _currentSize = 0; + _inputLen = 0; + + _inputBuffer = null; + _resultString = null; + _resultArray = null; + + // And then reset internal input buffers, if necessary: + if (_hasSegments) { + clearSegments(); + } + char[] curr = _currentSegment; + if (curr == null) { + _currentSegment = curr = buf(0); + } + return curr; + } + + public int getCurrentSegmentSize() { return _currentSize; } + public void setCurrentLength(int len) { _currentSize = len; } + + /** + * @since 2.6 + */ + public String setCurrentAndReturn(int len) { + _currentSize = len; + // We can simplify handling here compared to full `contentsAsString()`: + if (_segmentSize > 0) { // longer text; call main method + return contentsAsString(); + } + // more common case: single segment + int currLen = _currentSize; + String str = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen); + _resultString = str; + return str; + } + + public char[] finishCurrentSegment() { + if (_segments == null) { + _segments = new ArrayList(); + } + _hasSegments = true; + _segments.add(_currentSegment); + int oldLen = _currentSegment.length; + _segmentSize += oldLen; + _currentSize = 0; + + // Let's grow segments by 50% + int newLen = oldLen + (oldLen >> 1); + if (newLen < MIN_SEGMENT_LEN) { + newLen = MIN_SEGMENT_LEN; + } else if (newLen > MAX_SEGMENT_LEN) { + newLen = MAX_SEGMENT_LEN; + } + char[] curr = carr(newLen); + _currentSegment = curr; + return curr; + } + + /** + * Method called to expand size of the current segment, to + * accommodate for more contiguous content. Usually only + * used when parsing tokens like names if even then. + */ + public char[] expandCurrentSegment() + { + final char[] curr = _currentSegment; + // Let's grow by 50% by default + final int len = curr.length; + int newLen = len + (len >> 1); + // but above intended maximum, slow to increase by 25% + if (newLen > MAX_SEGMENT_LEN) { + newLen = len + (len >> 2); + } + return (_currentSegment = Arrays.copyOf(curr, newLen)); + } + + /** + * Method called to expand size of the current segment, to + * accommodate for more contiguous content. Usually only + * used when parsing tokens like names if even then. + * + * @param minSize Required minimum strength of the current segment + * + * @since 2.4.0 + */ + public char[] expandCurrentSegment(int minSize) { + char[] curr = _currentSegment; + if (curr.length >= minSize) return curr; + _currentSegment = curr = Arrays.copyOf(curr, minSize); + return curr; + } + + /* + /********************************************************** + /* Standard methods: + /********************************************************** + */ + + /** + * Note: calling this method may not be as efficient as calling + * {@link #contentsAsString}, since it's not guaranteed that resulting + * String is cached. + */ + @Override public String toString() { return contentsAsString(); } + + /* + /********************************************************** + /* Internal methods: + /********************************************************** + */ + + /** + * Method called if/when we need to append content when we have been + * initialized to use shared buffer. + */ + private void unshare(int needExtra) + { + int sharedLen = _inputLen; + _inputLen = 0; + char[] inputBuf = _inputBuffer; + _inputBuffer = null; + int start = _inputStart; + _inputStart = -1; + + // Is buffer big enough, or do we need to reallocate? + int needed = sharedLen+needExtra; + if (_currentSegment == null || needed > _currentSegment.length) { + _currentSegment = buf(needed); + } + if (sharedLen > 0) { + System.arraycopy(inputBuf, start, _currentSegment, 0, sharedLen); + } + _segmentSize = 0; + _currentSize = sharedLen; + } + + /** + * Method called when current segment is full, to allocate new + * segment. + */ + private void expand(int minNewSegmentSize) + { + // First, let's move current segment to segment list: + if (_segments == null) { + _segments = new ArrayList(); + } + char[] curr = _currentSegment; + _hasSegments = true; + _segments.add(curr); + _segmentSize += curr.length; + _currentSize = 0; + int oldLen = curr.length; + + // Let's grow segments by 50% minimum + int newLen = oldLen + (oldLen >> 1); + if (newLen < MIN_SEGMENT_LEN) { + newLen = MIN_SEGMENT_LEN; + } else if (newLen > MAX_SEGMENT_LEN) { + newLen = MAX_SEGMENT_LEN; + } + _currentSegment = carr(newLen); + } + + private char[] resultArray() + { + if (_resultString != null) { // Can take a shortcut... + return _resultString.toCharArray(); + } + // Do we use shared array? + if (_inputStart >= 0) { + final int len = _inputLen; + if (len < 1) { + return NO_CHARS; + } + final int start = _inputStart; + if (start == 0) { + return Arrays.copyOf(_inputBuffer, len); + } + return Arrays.copyOfRange(_inputBuffer, start, start+len); + } + // nope, not shared + int size = size(); + if (size < 1) { + return NO_CHARS; + } + int offset = 0; + final char[] result = carr(size); + if (_segments != null) { + for (int i = 0, len = _segments.size(); i < len; ++i) { + char[] curr = _segments.get(i); + int currLen = curr.length; + System.arraycopy(curr, 0, result, offset, currLen); + offset += currLen; + } + } + System.arraycopy(_currentSegment, 0, result, offset, _currentSize); + return result; + } + + private char[] carr(int len) { return new char[len]; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/VersionUtil.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/VersionUtil.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/VersionUtil.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,181 @@ +package com.fasterxml.jackson.core.util; + +import java.io.*; +import java.util.Properties; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.core.Versioned; + +/** + * Functionality for supporting exposing of component {@link Version}s. + * Also contains other misc methods that have no other place to live in. + *

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

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

+ * Note that functionality for reading "VERSION.txt" was removed completely + * from Jackson 2.6. + */ +public class VersionUtil +{ + private final static Pattern V_SEP = Pattern.compile("[-_./;:]"); + + private final Version _v; + + /* + /********************************************************** + /* Instance life-cycle + /********************************************************** + */ + + protected VersionUtil() + { + Version v = null; + try { + /* Class we pass only matters for resource-loading: can't use this Class + * (as it's just being loaded at this point), nor anything that depends on it. + */ + v = VersionUtil.versionFor(getClass()); + } catch (Exception e) { // not good to dump to stderr; but that's all we have at this low level + System.err.println("ERROR: Failed to load Version information from "+getClass()); + } + if (v == null) { + v = Version.unknownVersion(); + } + _v = v; + } + + public Version version() { return _v; } + + /* + /********************************************************** + /* 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.newInstance()).version(); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to get Versioned out of "+vClass); + } + } catch (Exception e) { // ok to be missing (not good but acceptable) + ; + } + return (v == null) ? Version.unknownVersion() : v; + } + + /** + * Will attempt to load the maven version for the given groupId and + * artifactId. Maven puts a pom.properties file in + * META-INF/maven/groupId/artifactId, containing the groupId, + * artifactId and version of the library. + * + * @param cl the ClassLoader to load the pom.properties file from + * @param groupId the groupId of the library + * @param artifactId the artifactId of the library + * @return The version + * + * @deprecated Since 2.6: functionality not used by any official Jackson component, should be + * moved out if anyone needs it + */ + @SuppressWarnings("resource") + @Deprecated // since 2.6 + public static Version mavenVersionFor(ClassLoader cl, String groupId, String artifactId) + { + InputStream pomProperties = cl.getResourceAsStream("META-INF/maven/" + + groupId.replaceAll("\\.", "/")+ "/" + artifactId + "/pom.properties"); + if (pomProperties != null) { + try { + Properties props = new Properties(); + props.load(pomProperties); + String versionStr = props.getProperty("version"); + String pomPropertiesArtifactId = props.getProperty("artifactId"); + String pomPropertiesGroupId = props.getProperty("groupId"); + return parseVersion(versionStr, pomPropertiesGroupId, pomPropertiesArtifactId); + } catch (IOException e) { + // Ignore + } finally { + _close(pomProperties); + } + } + return Version.unknownVersion(); + } + + /** + * Method used by PackageVersion classes to decode version injected by Maven build. + */ + public static Version parseVersion(String s, String groupId, String artifactId) + { + if (s != null && (s = s.trim()).length() > 0) { + String[] parts = V_SEP.split(s); + return new Version(parseVersionPart(parts[0]), + (parts.length > 1) ? parseVersionPart(parts[1]) : 0, + (parts.length > 2) ? parseVersionPart(parts[2]) : 0, + (parts.length > 3) ? parts[3] : null, + groupId, artifactId); + } + return Version.unknownVersion(); + } + + protected static int parseVersionPart(String s) { + int number = 0; + for (int i = 0, len = s.length(); i < len; ++i) { + char c = s.charAt(i); + if (c > '9' || c < '0') break; + number = (number * 10) + (c - '0'); + } + return number; + } + + private final static void _close(Closeable c) { + try { + c.close(); + } catch (IOException e) { } + } + + /* + /********************************************************** + /* Orphan utility methods + /********************************************************** + */ + + public final static void throwInternal() { + throw new RuntimeException("Internal error: this code path should never get executed"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/core/util/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,4 @@ +/** + * Utility classes used by Jackson Core functionality. + */ +package com.fasterxml.jackson.core.util; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/AbstractTypeResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/AbstractTypeResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/AbstractTypeResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind; + + +/** + * Defines interface for resolvers that can resolve abstract types into concrete + * ones; either by using static mappings, or possibly by materializing + * implementations dynamically. + */ +public abstract class AbstractTypeResolver +{ + /** + * Try to locate a subtype for given abstract type, to either resolve + * to a concrete type, or at least to a more-specific (and hopefully supported) + * abstract type, one which may have registered deserializers. + * Method is called before trying to locate registered deserializers + * (as well as standard abstract type defaulting that core Jackson does), + * so it is typically implemented to add custom mappings of common abstract + * types (like specify which concrete implementation to use for binding + * {@link java.util.List}s). + *

+ * Note that this method does not necessarily have to do full resolution + * of bindings; that is, it is legal to return type that could be further + * resolved: caller is expected to keep calling this method on registered + * resolvers, until a concrete type is located. + * + * @param config Configuration in use; should always be of type + * DeserializationConfig + */ + public JavaType findTypeMapping(DeserializationConfig config, JavaType type) { + return null; + } + + // !!! 29-Nov-2015, tatu: TODO: mark deprecated in 2.8 + /** + * Older variant of {@link #resolveAbstractType(DeserializationConfig, BeanDescription)}; + * obsoleted in 2.7, to be deprecated in 2.8 + */ + public JavaType resolveAbstractType(DeserializationConfig config, + JavaType type) { + return null; + } + + /** + * Method called to try to resolve an abstract type into + * concrete type (usually for purposes of deserializing), + * when no concrete implementation was found. + * It will be called after checking all other possibilities, + * including defaulting. + *

+ * Default implementation will call obsolete method for Jackson 2.7, + * to try to keep some level of backwards compatibility. + * + * @param config Configuration in use; should always be of type + * DeserializationConfig + * @param typeDesc Description of the POJO type to resolve + * + * @return Resolved concrete type (which should retain generic + * type parameters of input type, if any), if resolution succeeds; + * null if resolver does not know how to resolve given type + * + * @since 2.7 + */ + public JavaType resolveAbstractType(DeserializationConfig config, + BeanDescription typeDesc) { + return resolveAbstractType(config, typeDesc.getType()); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/AnnotationIntrospector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/AnnotationIntrospector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/AnnotationIntrospector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1393 @@ +package com.fasterxml.jackson.databind; + +import java.lang.annotation.Annotation; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.core.Versioned; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.type.MapLikeType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.Converter; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Abstract class that defines API used for introspecting annotation-based + * configuration for serialization and deserialization. Separated + * so that different sets of annotations can be supported, and support + * plugged-in dynamically. + *

+ * Although default implementations are based on using annotations as the only + * (or at least main) information source, custom implementations are not limited + * in such a way, and in fact there is no expectation they should be. So the name + * is bit of misnomer; this is a general configuration introspection facility. + *

+ * NOTE: due to rapid addition of new methods (and changes to existing methods), + * it is strongly recommended that custom implementations should not directly + * extend this class, but rather extend {@link NopAnnotationIntrospector}. + * This way added methods will not break backwards compatibility of custom annotation + * introspectors. + */ +@SuppressWarnings("serial") +public abstract class AnnotationIntrospector + implements Versioned, java.io.Serializable +{ + /* + /********************************************************** + /* Helper types + /********************************************************** + */ + + /** + * Value type used with managed and back references; contains type and + * logic name, used to link related references + */ + public static class ReferenceProperty + { + public enum Type { + /** + * Reference property that Jackson manages and that is serialized normally (by serializing + * reference object), but is used for resolving back references during + * deserialization. + * Usually this can be defined by using + * {@link com.fasterxml.jackson.annotation.JsonManagedReference} + */ + MANAGED_REFERENCE + + /** + * Reference property that Jackson manages by suppressing it during serialization, + * and reconstructing during deserialization. + * Usually this can be defined by using + * {@link com.fasterxml.jackson.annotation.JsonBackReference} + */ + ,BACK_REFERENCE + ; + } + + private final Type _type; + private final String _name; + + public ReferenceProperty(Type t, String n) { + _type = t; + _name = n; + } + + public static ReferenceProperty managed(String name) { return new ReferenceProperty(Type.MANAGED_REFERENCE, name); } + public static ReferenceProperty back(String name) { return new ReferenceProperty(Type.BACK_REFERENCE, name); } + + public Type getType() { return _type; } + public String getName() { return _name; } + + public boolean isManagedReference() { return _type == Type.MANAGED_REFERENCE; } + public boolean isBackReference() { return _type == Type.BACK_REFERENCE; } + } + + /* + /********************************************************** + /* Factory methods + /********************************************************** + */ + + /** + * Factory method for accessing "no operation" implementation + * of introspector: instance that will never find any annotation-based + * configuration. + */ + public static AnnotationIntrospector nopInstance() { + return NopAnnotationIntrospector.instance; + } + + public static AnnotationIntrospector pair(AnnotationIntrospector a1, AnnotationIntrospector a2) { + return new AnnotationIntrospectorPair(a1, a2); + } + + /* + /********************************************************** + /* Access to possibly chained introspectors + /********************************************************** + */ + + /** + * Method that can be used to collect all "real" introspectors that + * this introspector contains, if any; or this introspector + * if it is not a container. Used to get access to all container + * introspectors in their priority order. + *

+ * Default implementation returns a Singleton list with this introspector + * as contents. + * This usually works for sub-classes, except for proxy or delegating "container + * introspectors" which need to override implementation. + */ + public Collection allIntrospectors() { + return Collections.singletonList(this); + } + + /** + * Method that can be used to collect all "real" introspectors that + * this introspector contains, if any; or this introspector + * if it is not a container. Used to get access to all container + * introspectors in their priority order. + *

+ * Default implementation adds this introspector in result; this usually + * works for sub-classes, except for proxy or delegating "container + * introspectors" which need to override implementation. + */ + public Collection allIntrospectors(Collection result) { + result.add(this); + return result; + } + + /* + /********************************************************** + /* Default Versioned impl + /********************************************************** + */ + + @Override + public abstract Version version(); + + /* + /********************************************************** + /* Meta-annotations (annotations for annotation types) + /********************************************************** + */ + + /** + * Method for checking whether given annotation is considered an + * annotation bundle: if so, all meta-annotations it has will + * be used instead of annotation ("bundle") itself. + * + * @since 2.0 + */ + public boolean isAnnotationBundle(Annotation ann) { + return false; + } + + /* + /********************************************************** + /* Annotations for Object Id handling + /********************************************************** + */ + + /** + * Method for checking whether given annotated thing + * (type, or accessor) indicates that values + * referenced (values of type of annotated class, or + * values referenced by annotated property; latter + * having precedence) should include Object Identifier, + * and if so, specify details of Object Identity used. + * + * @since 2.0 + */ + public ObjectIdInfo findObjectIdInfo(Annotated ann) { + return null; + } + + /** + * Method for figuring out additional properties of an Object Identity reference + * + * @since 2.1 + */ + public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { + return objectIdInfo; + } + + /* + /********************************************************** + /* General class annotations + /********************************************************** + */ + + /** + * Method for locating name used as "root name" (for use by + * some serializers when outputting root-level object -- mostly + * for XML compatibility purposes) for given class, if one + * is defined. Returns null if no declaration found; can return + * explicit empty String, which is usually ignored as well as null. + *

+ * NOTE: method signature changed in 2.1, to return {@link PropertyName} + * instead of String. + */ + public PropertyName findRootName(AnnotatedClass ac) { + return null; + } + + /** + * Method for finding list of properties to ignore for given class + * (null is returned if not specified). + * List of property names is applied + * after other detection mechanisms, to filter out these specific + * properties from being serialized and deserialized. + * + * @param forSerialization True if requesting properties to ignore for serialization; + * false if for deserialization + */ + public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) { + return null; + } + + /** + * @deprecated Since 2.6, use variant that takes second argument. + */ + @Deprecated + public String[] findPropertiesToIgnore(Annotated ac) { + // Changed in 2.7 to call from old to new; with 2.6 was opposite + return findPropertiesToIgnore(ac, true); + } + + /** + * Method for checking whether an annotation indicates that all unknown properties + */ + public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) { return null; } + + /** + * Method for checking whether properties that have specified type + * (class, not generics aware) should be completely ignored for + * serialization and deserialization purposes. + * + * @param ac Type to check + * + * @return Boolean.TRUE if properties of type should be ignored; + * Boolean.FALSE if they are not to be ignored, null for default + * handling (which is 'do not ignore') + */ + public Boolean isIgnorableType(AnnotatedClass ac) { return null; } + + /** + * Method for finding if annotated class has associated filter; and if so, + * to return id that is used to locate filter. + * + * @return Id of the filter to use for filtering properties of annotated + * class, if any; or null if none found. + */ + public Object findFilterId(Annotated ann) { return null; } + + /** + * Method for finding {@link PropertyNamingStrategy} for given + * class, if any specified by annotations; and if so, either return + * a {@link PropertyNamingStrategy} instance, or Class to use for + * creating instance + * + * @return Sub-class or instance of {@link PropertyNamingStrategy}, if one + * is specified for given class; null if not. + * + * @since 2.1 + */ + public Object findNamingStrategy(AnnotatedClass ac) { return null; } + + /** + * Method used to check whether specified class defines a human-readable + * description to use for documentation. + * There are no further definitions for contents; for example, whether + * these may be marked up using HTML (or something like wiki format like Markup) + * is not defined. + * + * @return Human-readable description, if any. + * + * @since 2.7 + */ + public String findClassDescription(AnnotatedClass ac) { return null; } + + /* + /********************************************************** + /* Property auto-detection + /********************************************************** + */ + + /** + * Method for checking if annotations indicate changes to minimum visibility levels + * needed for auto-detecting property elements (fields, methods, constructors). + * A baseline checker is given, and introspector is to either return it as is + * (if no annotations are found), or build and return a derived instance (using + * checker's build methods). + */ + public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, VisibilityChecker checker) { + return checker; + } + + /* + /********************************************************** + /* Annotations for Polymorphic type handling + /********************************************************** + */ + + /** + * Method for checking if given class has annotations that indicate + * that specific type resolver is to be used for handling instances. + * This includes not only + * instantiating resolver builder, but also configuring it based on + * relevant annotations (not including ones checked with a call to + * {@link #findSubtypes} + * + * @param config Configuration settings in effect (for serialization or deserialization) + * @param ac Annotated class to check for annotations + * @param baseType Base java type of value for which resolver is to be found + * + * @return Type resolver builder for given type, if one found; null if none + */ + public TypeResolverBuilder findTypeResolver(MapperConfig config, + AnnotatedClass ac, JavaType baseType) { + return null; + } + + /** + * Method for checking if given property entity (field or method) has annotations + * that indicate that specific type resolver is to be used for handling instances. + * This includes not only + * instantiating resolver builder, but also configuring it based on + * relevant annotations (not including ones checked with a call to + * {@link #findSubtypes} + * + * @param config Configuration settings in effect (for serialization or deserialization) + * @param am Annotated member (field or method) to check for annotations + * @param baseType Base java type of property for which resolver is to be found + * + * @return Type resolver builder for properties of given entity, if one found; + * null if none + */ + public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, + AnnotatedMember am, JavaType baseType) { + return null; + } + + /** + * Method for checking if given structured property entity (field or method that + * has nominal value of Map, Collection or array type) has annotations + * that indicate that specific type resolver is to be used for handling type + * information of contained values. + * This includes not only + * instantiating resolver builder, but also configuring it based on + * relevant annotations (not including ones checked with a call to + * {@link #findSubtypes} + * + * @param config Configuration settings in effect (for serialization or deserialization) + * @param am Annotated member (field or method) to check for annotations + * @param containerType Type of property for which resolver is to be found (must be a container type) + * + * @return Type resolver builder for values contained in properties of given entity, + * if one found; null if none + */ + public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, + AnnotatedMember am, JavaType containerType) { + return null; + } + + /** + * Method for locating annotation-specified subtypes related to annotated + * entity (class, method, field). Note that this is only guaranteed to be + * a list of directly + * declared subtypes, no recursive processing is guarantees (i.e. caller + * has to do it if/as necessary) + * + * @param a Annotated entity (class, field/method) to check for annotations + */ + public List findSubtypes(Annotated a) { return null; } + + /** + * Method for checking if specified type has explicit name. + * + * @param ac Class to check for type name annotations + */ + public String findTypeName(AnnotatedClass ac) { return null; } + + /** + * Method for checking whether given accessor claims to represent + * type id: if so, its value may be used as an override, + * instead of generated type id. + */ + public Boolean isTypeId(AnnotatedMember member) { return null; } + + /* + /********************************************************** + /* General member (field, method/constructor) annotations + /********************************************************** + */ + + /** + * Method for checking if given member indicates that it is part + * of a reference (parent/child). + */ + public ReferenceProperty findReferenceType(AnnotatedMember member) { return null; } + + /** + * Method called to check whether given property is marked to be "unwrapped" + * when being serialized (and appropriately handled in reverse direction, + * i.e. expect unwrapped representation during deserialization). + * Return value is the name transformation to use, if wrapping/unwrapping + * should be done, or null if not -- note that transformation may simply + * be identity transformation (no changes). + */ + public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) { return null; } + + /** + * Method called to check whether given property is marked to + * be ignored. This is used to determine whether to ignore + * properties, on per-property basis, usually combining + * annotations from multiple accessors (getters, setters, fields, + * constructor parameters). + */ + public boolean hasIgnoreMarker(AnnotatedMember m) { return false; } + + /** + * Method called to find out whether given member expectes a value + * to be injected, and if so, what is the identifier of the value + * to use during injection. + * Type if identifier needs to be compatible with provider of + * values (of type {@link InjectableValues}); often a simple String + * id is used. + * + * @param m Member to check + * + * @return Identifier of value to inject, if any; null if no injection + * indicator is found + */ + public Object findInjectableValueId(AnnotatedMember m) { return null; } + + /** + * Method that can be called to check whether this member has + * an annotation that suggests whether value for matching property + * is required or not. + * + * @since 2.0 + */ + public Boolean hasRequiredMarker(AnnotatedMember m) { return null; } + + /** + * Method for checking if annotated property (represented by a field or + * getter/setter method) has definitions for views it is to be included in. + * If null is returned, no view definitions exist and property is always + * included (or always excluded as per default view inclusion configuration); + * otherwise it will only be included for views included in returned + * array. View matches are checked using class inheritance rules (sub-classes + * inherit inclusions of super-classes) + * + * @param a Annotated property (represented by a method, field or ctor parameter) + * @return Array of views (represented by classes) that the property is included in; + * if null, always included (same as returning array containing Object.class) + */ + public Class[] findViews(Annotated a) { return null; } + + /** + * Method for finding format annotations for property or class. + * Return value is typically used by serializers and/or + * deserializers to customize presentation aspects of the + * serialized value. + * + * @since 2.1 + */ + public JsonFormat.Value findFormat(Annotated memberOrClass) { return null; } + + /** + * Method used to check if specified property has annotation that indicates + * that it should be wrapped in an element; and if so, name to use. + * Note that not all serializers and deserializers support use this method: + * currently (2.1) it is only used by XML-backed handlers. + * + * @return Wrapper name to use, if any, or {@link PropertyName#USE_DEFAULT} + * to indicate that no wrapper element should be used. + * + * @since 2.1 + */ + public PropertyName findWrapperName(Annotated ann) { return null; } + + /** + * Method for finding suggested default value (as simple textual serialization) + * for the property. While core databind does not make any use of it, it is exposed + * for extension modules to use: an expected use is generation of schema representations + * and documentation. + * + * @since 2.5 + */ + public String findPropertyDefaultValue(Annotated ann) { return null; } + + /** + * Method used to check whether specified property member (accessor + * or mutator) defines human-readable description to use for documentation. + * There are no further definitions for contents; for example, whether + * these may be marked up using HTML is not defined. + * + * @return Human-readable description, if any. + * + * @since 2.3 + */ + public String findPropertyDescription(Annotated ann) { return null; } + + /** + * Method used to check whether specified property member (accessor + * or mutator) defines numeric index, and if so, what is the index value. + * Possible use cases for index values included use by underlying data format + * (some binary formats mandate use of index instead of name) and ordering + * of properties (for documentation, or during serialization). + * + * @since 2.4 + * + * @return Explicitly specified index for the property, if any + */ + public Integer findPropertyIndex(Annotated ann) { return null; } + + /** + * Method for finding implicit name for a property that given annotated + * member (field, method, creator parameter) may represent. + * This is different from explicit, annotation-based property name, in that + * it is "weak" and does not either proof that a property exists (for example, + * if visibility is not high enough), or override explicit names. + * In practice this method is used to introspect optional names for creator + * parameters (which may or may not be available and can not be detected + * by standard databind); or to provide alternate name mangling for + * fields, getters and/or setters. + * + * @since 2.4 + */ + public String findImplicitPropertyName(AnnotatedMember member) { return null; } + + /** + * Method for finding optional access definition for a property, annotated + * on one of its accessors. If a definition for read-only, write-only + * or read-write cases, visibility rules may be modified. Note, however, + * that even more specific annotations (like one for ignoring specific accessor) + * may further override behavior of the access definition. + * + * @since 2.6 + */ + public JsonProperty.Access findPropertyAccess(Annotated ann) { return null; } + + /** + * Method called in cases where a class has two methods eligible to be used + * for the same logical property, and default logic is not enough to figure + * out clear precedence. Introspector may try to choose one to use; or, if + * unable, return `null` to indicate it can not resolve the problem. + * + * @since 2.7 + */ + public AnnotatedMethod resolveSetterConflict(MapperConfig config, + AnnotatedMethod setter1, AnnotatedMethod setter2) { + return null; + } + + /* + /********************************************************** + /* Serialization: general annotations + /********************************************************** + */ + + /** + * Method for getting a serializer definition on specified method + * or field. Type of definition is either instance (of type + * {@link JsonSerializer}) or Class (of type + * Class<JsonSerializer>); if value of different + * type is returned, a runtime exception may be thrown by caller. + */ + public Object findSerializer(Annotated am) { + return null; + } + + /** + * Method for getting a serializer definition for keys of associated Map property. + * Type of definition is either instance (of type + * {@link JsonSerializer}) or Class (of type + * Class<JsonSerializer>); if value of different + * type is returned, a runtime exception may be thrown by caller. + */ + public Object findKeySerializer(Annotated am) { + return null; + } + + /** + * Method for getting a serializer definition for content (values) of + * associated Collection, array or Map property. + * Type of definition is either instance (of type + * {@link JsonSerializer}) or Class (of type + * Class<JsonSerializer>); if value of different + * type is returned, a runtime exception may be thrown by caller. + */ + public Object findContentSerializer(Annotated am) { + return null; + } + + /** + * Method for getting a serializer definition for serializer to use + * for nulls (null values) of associated property or type. + * + * @since 2.3 + */ + public Object findNullSerializer(Annotated am) { + return null; + } + + /** + * Method for accessing declared typing mode annotated (if any). + * This is used for type detection, unless more granular settings + * (such as actual exact type; or serializer to use which means + * no type information is needed) take precedence. + * + * @return Typing mode to use, if annotation is found; null otherwise + */ + public JsonSerialize.Typing findSerializationTyping(Annotated a) { + return null; + } + + /** + * Method for finding {@link Converter} that annotated entity + * (property or class) has indicated to be used as part of + * serialization. If not null, either has to be actual + * {@link Converter} instance, or class for such converter; + * and resulting converter will be used first to convert property + * value to converter target type, and then serializer for that + * type is used for actual serialization. + *

+ * This feature is typically used to convert internal values into types + * that Jackson can convert. + *

+ * Note also that this feature does not necessarily work well with polymorphic + * type handling, or object identity handling; if such features are needed + * an explicit serializer is usually better way to handle serialization. + * + * @param a Annotated property (field, method) or class to check for + * annotations + * + * @since 2.2 + */ + public Object findSerializationConverter(Annotated a) { + return null; + } + + /** + * Method for finding {@link Converter} that annotated property + * has indicated needs to be used for values of container type + * (this also means that method should only be called for properties + * of container types, List/Map/array properties). + *

+ * If not null, either has to be actual + * {@link Converter} instance, or class for such converter; + * and resulting converter will be used first to convert property + * value to converter target type, and then serializer for that + * type is used for actual serialization. + *

+ * Other notes are same as those for {@link #findSerializationConverter} + * + * @param a Annotated property (field, method) to check. + * + * @since 2.2 + */ + public Object findSerializationContentConverter(AnnotatedMember a) { + return null; + } + + /** + * Method for checking whether given annotated entity (class, method, + * field) defines which Bean/Map properties are to be included in + * serialization. + * If no annotation is found, method should return given second + * argument; otherwise value indicated by the annotation. + *

+ * Note that meaning of inclusion value depends on whether it is for + * a Class or property (field/method/constructor): in former case, + * it is the default for all properties; in latter case it is specific + * override for annotated property. + * + * @return Enumerated value indicating which properties to include + * in serialization + * + * @deprecated Since 2.7 Use {@link #findPropertyInclusion} instead + */ + @Deprecated // since 2.7 + public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue) { + return defValue; + } + + /** + * Method for checking whether content (entries) of a {@link java.util.Map} property + * are to be included during serialization or not. + * NOTE: this is NOT called for POJO properties, or array/Collection elements. + * + * @since 2.5 + * + * @deprecated Since 2.7 Use {@link #findPropertyInclusion} instead + */ + @Deprecated // since 2.7 + public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue) { + return defValue; + } + + /** + * Method for checking inclusion criteria for a type (Class) or property (yes, method + * name is bit unfortunate -- not just for properties!). + * In case of class, acts as the default for properties POJO contains; for properties + * acts as override for class defaults and possible global defaults. + * + * @since 2.6 + */ + public JsonInclude.Value findPropertyInclusion(Annotated a) { + return JsonInclude.Value.empty(); + } + + /* + /********************************************************** + /* Serialization: type refinements + /********************************************************** + */ + + /** + * Method for accessing annotated type definition that a + * method/field can have, to be used as the type for serialization + * instead of the runtime type. + * Type returned (if any) needs to be widening conversion (super-type). + * Declared return type of the method is also considered acceptable. + * + * @return Class to use instead of runtime type + * + * @deprecated Since 2.7 call {@link #refineSerializationType} instead + */ + @Deprecated // since 2.7 + public Class findSerializationType(Annotated a) { + return null; + } + + /** + * Method for finding possible widening type definition that a property + * value can have, to define less specific key type to use for serialization. + * It should be only be used with {@link java.util.Map} types. + * + * @return Class specifying more general type to use instead of + * declared type, if annotation found; null if not + * + * @deprecated Since 2.7 call {@link #refineSerializationType} instead + */ + @Deprecated // since 2.7 + public Class findSerializationKeyType(Annotated am, JavaType baseType) { + return null; + } + + /** + * Method for finding possible widening type definition that a property + * value can have, to define less specific key type to use for serialization. + * It should be only used with structured types (arrays, collections, maps). + * + * @return Class specifying more general type to use instead of + * declared type, if annotation found; null if not + * + * @deprecated Since 2.7 call {@link #refineSerializationType} instead + */ + @Deprecated // since 2.7 + public Class findSerializationContentType(Annotated am, JavaType baseType) { + return null; + } + + /** + * Method called to find out possible type refinements to use + * for deserialization. + * + * @since 2.7 + */ + public JavaType refineSerializationType(final MapperConfig config, + final Annotated a, final JavaType baseType) throws JsonMappingException + { + JavaType type = baseType; + final TypeFactory tf = config.getTypeFactory(); + + // 10-Oct-2015, tatu: For 2.7, we'll need to delegate back to + // now-deprecated secondary methods; this because while + // direct sub-class not yet retrofitted may only override + // those methods. With 2.8 or later we may consider removal + // of these methods + + + // Ok: start by refining the main type itself; common to all types + Class serClass = findSerializationType(a); + if (serClass != null) { + if (type.hasRawClass(serClass)) { + // 30-Nov-2015, tatu: As per [databind#1023], need to allow forcing of + // static typing this way + type = type.withStaticTyping(); + } else { + try { + // 11-Oct-2015, tatu: For deser, we call `TypeFactory.constructSpecializedType()`, + // may be needed here too in future? + type = tf.constructGeneralizedType(type, serClass); + } catch (IllegalArgumentException iae) { + throw new JsonMappingException(null, + String.format("Failed to widen type %s with annotation (value %s), from '%s': %s", + type, serClass.getName(), a.getName(), iae.getMessage()), + iae); + } + } + } + // Then further processing for container types + + // First, key type (for Maps, Map-like types): + if (type.isMapLikeType()) { + JavaType keyType = type.getKeyType(); + Class keyClass = findSerializationKeyType(a, keyType); + if (keyClass != null) { + if (keyType.hasRawClass(keyClass)) { + keyType = keyType.withStaticTyping(); + } else { + try { + keyType = tf.constructGeneralizedType(keyType, keyClass); + } catch (IllegalArgumentException iae) { + throw new JsonMappingException(null, + String.format("Failed to widen key type of %s with concrete-type annotation (value %s), from '%s': %s", + type, keyClass.getName(), a.getName(), iae.getMessage()), + iae); + } + } + type = ((MapLikeType) type).withKeyType(keyType); + } + } + + JavaType contentType = type.getContentType(); + if (contentType != null) { // collection[like], map[like], array, reference + // And then value types for all containers: + Class contentClass = findSerializationContentType(a, contentType); + if (contentClass != null) { + if (contentType.hasRawClass(contentClass)) { + contentType = contentType.withStaticTyping(); + } else { + // 03-Apr-2016, tatu: As per [databind#1178], may need to actually + // specialize (narrow) type sometimes, even if more commonly opposite + // is needed. + Class currRaw = contentType.getRawClass(); + try { + if (contentClass.isAssignableFrom(currRaw)) { // common case + contentType = tf.constructGeneralizedType(contentType, contentClass); + } else if (currRaw.isAssignableFrom(contentClass)) { // specialization, ok as well + contentType = tf.constructSpecializedType(contentType, contentClass); + } else { + throw new JsonMappingException(null, + String.format("Can not refine serialization content type %s into %s; types not related", + contentType, contentClass.getName())); + } + } catch (IllegalArgumentException iae) { // shouldn't really happen + throw new JsonMappingException(null, + String.format("Internal error: failed to refine value type of %s with concrete-type annotation (value %s), from '%s': %s", + type, contentClass.getName(), a.getName(), iae.getMessage()), + iae); + } + } + type = type.withContentType(contentType); + } + } + return type; + } + + /* + /********************************************************** + /* Serialization: class annotations + /********************************************************** + */ + + /** + * Method for accessing defined property serialization order (which may be + * partial). May return null if no ordering is defined. + */ + public String[] findSerializationPropertyOrder(AnnotatedClass ac) { + return null; + } + + /** + * Method for checking whether an annotation indicates that serialized properties + * for which no explicit is defined should be alphabetically (lexicograpically) + * ordered + */ + public Boolean findSerializationSortAlphabetically(Annotated ann) { + return null; + } + + /** + * Method for adding possible virtual properties to be serialized along + * with regular properties. + * + * @since 2.5 + */ + public void findAndAddVirtualProperties(MapperConfig config, AnnotatedClass ac, + List properties) { } + + /* + /********************************************************** + /* Serialization: property annotations + /********************************************************** + */ + + /** + * Method for checking whether given property accessors (method, + * field) has an annotation that suggests property name to use + * for serialization. + * Should return null if no annotation + * is found; otherwise a non-null name (possibly + * {@link PropertyName#USE_DEFAULT}, which means "use default heuristics"). + * + * @param a Property accessor to check + * + * @return Name to use if found; null if not. + * + * @since 2.1 + */ + public PropertyName findNameForSerialization(Annotated a) { + /* + if (name != null) { + if (name.length() == 0) { // empty String means 'default' + return PropertyName.USE_DEFAULT; + } + return new PropertyName(name); + } + */ + return null; + } + + /** + * Method for checking whether given method has an annotation + * that suggests that the return value of annotated method + * should be used as "the value" of the object instance; usually + * serialized as a primitive value such as String or number. + * + * @return True if such annotation is found (and is not disabled); + * false if no enabled annotation is found + */ + public boolean hasAsValueAnnotation(AnnotatedMethod am) { + return false; + } + + // TODO: Deprecate in 2.8? + /** + * Method for determining the String value to use for serializing + * given enumeration entry; used when serializing enumerations + * as Strings (the standard method). + * + * @return Serialized enum value. + */ + public String findEnumValue(Enum value) { + return value.name(); + } + + /** + * Method for efficiently figuring out which if given set of Enum values + * have explicitly defined name. Method will overwrite entries in incoming names + * array with explicit names found, if any, leaving other entries unmodified. + *

+ * Default implementation will simply delegate to {@link #findEnumValue}, which is close + * enough, although unfortunately NOT 100% equivalent (as it will also consider name() + * to give explicit value). + * + * @since 2.7 + */ + public String[] findEnumValues(Class enumType, Enum[] enumValues, String[] names) { + for (int i = 0, len = enumValues.length; i < len; ++i) { + /* 12-Mar-2016, tatu: This is quite tricky, considering that we should NOT + * overwrite values with default `name`... so for now, let's only delegate + * if no value has been set. Still not optimal but has to do + */ + // TODO: In 2.8, stop delegation? + if (names[i] == null) { + names[i] = findEnumValue(enumValues[i]); + } + } + return names; + } + + /* + /********************************************************** + /* Deserialization: general annotations + /********************************************************** + */ + + /** + * Method for getting a deserializer definition on specified method + * or field. + * Type of definition is either instance (of type + * {@link JsonDeserializer}) or Class (of type + * Class<JsonDeserializer>); if value of different + * type is returned, a runtime exception may be thrown by caller. + */ + public Object findDeserializer(Annotated am) { + return null; + } + + /** + * Method for getting a deserializer definition for keys of + * associated Map property. + * Type of definition is either instance (of type + * {@link JsonDeserializer}) or Class (of type + * Class<JsonDeserializer>); if value of different + * type is returned, a runtime exception may be thrown by caller. + */ + public Object findKeyDeserializer(Annotated am) { + return null; + } + + /** + * Method for getting a deserializer definition for content (values) of + * associated Collection, array or + * Map property. + * Type of definition is either instance (of type + * {@link JsonDeserializer}) or Class (of type + * Class<JsonDeserializer>); if value of different + * type is returned, a runtime exception may be thrown by caller. + */ + public Object findContentDeserializer(Annotated am) { + return null; + } + + /** + * Method for finding {@link Converter} that annotated entity + * (property or class) has indicated to be used as part of + * deserialization. + * If not null, either has to be actual + * {@link Converter} instance, or class for such converter; + * and resulting converter will be used after Jackson has deserializer + * data into intermediate type (Converter input type), and Converter + * needs to convert this into its target type to be set as property value. + *

+ * This feature is typically used to convert intermediate Jackson types + * (that default deserializers can produce) into custom type instances. + *

+ * Note also that this feature does not necessarily work well with polymorphic + * type handling, or object identity handling; if such features are needed + * an explicit deserializer is usually better way to handle deserialization. + * + * @param a Annotated property (field, method) or class to check for + * annotations + * + * @since 2.2 + */ + public Object findDeserializationConverter(Annotated a) { + return null; + } + + /** + * Method for finding {@link Converter} that annotated property + * has indicated needs to be used for values of container type + * (this also means that method should only be called for properties + * of container types, List/Map/array properties). + *

+ * If not null, either has to be actual + * {@link Converter} instance, or class for such converter; + * and resulting converter will be used after Jackson has deserializer + * data into intermediate type (Converter input type), and Converter + * needs to convert this into its target type to be set as property value. + *

+ * Other notes are same as those for {@link #findDeserializationConverter} + * + * @param a Annotated property (field, method) to check. + * + * @since 2.2 + */ + public Object findDeserializationContentConverter(AnnotatedMember a) { + return null; + } + + /* + /********************************************************** + /* Deserialization: type refinements + /********************************************************** + */ + + /** + * Method called to find out possible type refinements to use + * for deserialization. + * + * @since 2.7 + */ + public JavaType refineDeserializationType(final MapperConfig config, + final Annotated a, final JavaType baseType) throws JsonMappingException + { + JavaType type = baseType; + final TypeFactory tf = config.getTypeFactory(); + + // 10-Oct-2015, tatu: For 2.7, we'll need to delegate back to + // now-deprecated secondary methods; this because while + // direct sub-class not yet retrofitted may only override + // those methods. With 2.8 or later we may consider removal + // of these methods + + + // Ok: start by refining the main type itself; common to all types + Class valueClass = findDeserializationType(a, type); + if ((valueClass != null) && !type.hasRawClass(valueClass)) { + try { + type = tf.constructSpecializedType(type, valueClass); + } catch (IllegalArgumentException iae) { + throw new JsonMappingException(null, + String.format("Failed to narrow type %s with annotation (value %s), from '%s': %s", + type, valueClass.getName(), a.getName(), iae.getMessage()), + iae); + } + } + // Then further processing for container types + + // First, key type (for Maps, Map-like types): + if (type.isMapLikeType()) { + JavaType keyType = type.getKeyType(); + Class keyClass = findDeserializationKeyType(a, keyType); + if (keyClass != null) { + try { + keyType = tf.constructSpecializedType(keyType, keyClass); + type = ((MapLikeType) type).withKeyType(keyType); + } catch (IllegalArgumentException iae) { + throw new JsonMappingException(null, + String.format("Failed to narrow key type of %s with concrete-type annotation (value %s), from '%s': %s", + type, keyClass.getName(), a.getName(), iae.getMessage()), + iae); + } + } + } + JavaType contentType = type.getContentType(); + if (contentType != null) { // collection[like], map[like], array, reference + // And then value types for all containers: + Class contentClass = findDeserializationContentType(a, contentType); + if (contentClass != null) { + try { + contentType = tf.constructSpecializedType(contentType, contentClass); + type = type.withContentType(contentType); + } catch (IllegalArgumentException iae) { + throw new JsonMappingException(null, + String.format("Failed to narrow value type of %s with concrete-type annotation (value %s), from '%s': %s", + type, contentClass.getName(), a.getName(), iae.getMessage()), + iae); + } + } + } + return type; + } + + /** + * Method for accessing annotated type definition that a + * property can have, to be used as the type for deserialization + * instead of the static (declared) type. + * Type is usually narrowing conversion (i.e.subtype of declared type). + * Declared return type of the method is also considered acceptable. + * + * @param baseType Assumed type before considering annotations + * + * @return Class to use for deserialization instead of declared type + * + * @deprecated Since 2.7 call {@link #refineDeserializationType} instead + */ + @Deprecated + public Class findDeserializationType(Annotated am, JavaType baseType) { + return null; + } + + /** + * Method for accessing additional narrowing type definition that a + * method can have, to define more specific key type to use. + * It should be only be used with {@link java.util.Map} types. + * + * @param baseKeyType Assumed key type before considering annotations + * + * @return Class specifying more specific type to use instead of + * declared type, if annotation found; null if not + * + * @deprecated Since 2.7 call {@link #refineDeserializationType} instead + */ + @Deprecated + public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { + return null; + } + + /** + * Method for accessing additional narrowing type definition that a + * method can have, to define more specific content type to use; + * content refers to Map values and Collection/array elements. + * It should be only be used with Map, Collection and array types. + * + * @param baseContentType Assumed content (value) type before considering annotations + * + * @return Class specifying more specific type to use instead of + * declared type, if annotation found; null if not + * + * @deprecated Since 2.7 call {@link #refineDeserializationType} instead + */ + @Deprecated + public Class findDeserializationContentType(Annotated am, JavaType baseContentType) { + return null; + } + + /* + /********************************************************** + /* Deserialization: class annotations + /********************************************************** + */ + + /** + * Method getting {@link ValueInstantiator} to use for given + * type (class): return value can either be an instance of + * instantiator, or class of instantiator to create. + */ + public Object findValueInstantiator(AnnotatedClass ac) { + return null; + } + + /** + * Method for finding Builder object to use for constructing + * value instance and binding data (sort of combining value + * instantiators that can construct, and deserializers + * that can bind data). + *

+ * Note that unlike accessors for some helper Objects, this + * method does not allow returning instances: the reason is + * that builders have state, and a separate instance needs + * to be created for each deserialization call. + * + * @since 2.0 + */ + public Class findPOJOBuilder(AnnotatedClass ac) { + return null; + } + + /** + * @since 2.0 + */ + public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) { + return null; + } + + /* + /********************************************************** + /* Deserialization: property annotations + /********************************************************** + */ + + /** + * Method for checking whether given property accessors (method, + * field) has an annotation that suggests property name to use + * for deserialization (reading JSON into POJOs). + * Should return null if no annotation + * is found; otherwise a non-null name (possibly + * {@link PropertyName#USE_DEFAULT}, which means "use default heuristics"). + * + * @param a Property accessor to check + * + * @return Name to use if found; null if not. + * + * @since 2.1 + */ + public PropertyName findNameForDeserialization(Annotated a) { + /* + if (name != null) { + if (name.length() == 0) { // empty String means 'default' + return PropertyName.USE_DEFAULT; + } + return new PropertyName(name); + } + */ + return null; + } + + /** + * Method for checking whether given method has an annotation + * that suggests that the method is to serve as "any setter"; + * method to be used for setting values of any properties for + * which no dedicated setter method is found. + * + * @return True if such annotation is found (and is not disabled), + * false otherwise + */ + public boolean hasAnySetterAnnotation(AnnotatedMethod am) { + return false; + } + + /** + * Method for checking whether given method has an annotation + * that suggests that the method is to serve as "any setter"; + * method to be used for accessing set of miscellaneous "extra" + * properties, often bound with matching "any setter" method. + * + * @return True if such annotation is found (and is not disabled), + * false otherwise + */ + public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { + return false; + } + + /** + * Method for checking whether given annotated item (method, constructor) + * has an annotation + * that suggests that the method is a "creator" (aka factory) + * method to be used for construct new instances of deserialized + * values. + * + * @return True if such annotation is found (and is not disabled), + * false otherwise + */ + public boolean hasCreatorAnnotation(Annotated a) { + return false; + } + + /** + * Method for finding indication of creator binding mode for + * a creator (something for which {@link #hasCreatorAnnotation} returns + * true), for cases where there may be ambiguity (currently: single-argument + * creator with implicit but no explicit name for the argument). + * + * @since 2.5 + */ + public JsonCreator.Mode findCreatorBinding(Annotated a) { + return null; + } + + /* + /********************************************************** + /* Overridable methods: may be used as low-level extension + /* points. + /********************************************************** + */ + + /** + * Method that should be used by sub-classes for ALL + * annotation access; + * overridable so + * that sub-classes may, if they choose to, mangle actual access to + * block access ("hide" annotations) or perhaps change it. + *

+ * Default implementation is simply: + * + * return annotated.getAnnotation(annoClass); + * + * + * @since 2.5 + */ + protected A _findAnnotation(Annotated annotated, + Class annoClass) { + return annotated.getAnnotation(annoClass); + } + + /** + * Method that should be used by sub-classes for ALL + * annotation existence access; + * overridable so that sub-classes may, if they choose to, mangle actual access to + * block access ("hide" annotations) or perhaps change value seen. + *

+ * Default implementation is simply: + * + * return annotated.hasAnnotation(annoClass); + * + * + * @since 2.5 + */ + protected boolean _hasAnnotation(Annotated annotated, Class annoClass) { + return annotated.hasAnnotation(annoClass); + } + + /** + * Alternative lookup method that is used to see if annotation has at least one of + * annotations of types listed in second argument. + * + * @since 2.7 + */ + protected boolean _hasOneOf(Annotated annotated, Class[] annoClasses) { + return annotated.hasOneOf(annoClasses); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/BeanDescription.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/BeanDescription.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/BeanDescription.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,262 @@ +package com.fasterxml.jackson.databind; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.type.TypeBindings; +import com.fasterxml.jackson.databind.util.Annotations; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Basic container for information gathered by {@link ClassIntrospector} to + * help in constructing serializers and deserializers. + * Note that the main implementation type is + * {@link com.fasterxml.jackson.databind.introspect.BasicBeanDescription}, + * meaning that it is safe to upcast to this type. + */ +public abstract class BeanDescription +{ + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Bean type information, including raw class and possible + * * generics information + */ + protected final JavaType _type; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected BeanDescription(JavaType type) { + _type = type; + } + + /* + /********************************************************** + /* Simple accesors + /********************************************************** + */ + + /** + * Method for accessing declared type of bean being introspected, + * including full generic type information (from declaration) + */ + public JavaType getType() { return _type; } + + public Class getBeanClass() { return _type.getRawClass(); } + + /** + * Method for accessing low-level information about Class this + * item describes. + */ + public abstract AnnotatedClass getClassInfo(); + + /** + * Accessor for getting information about Object Id expected to + * be used for this POJO type, if any. + */ + public abstract ObjectIdInfo getObjectIdInfo(); + + /** + * Method for checking whether class being described has any + * annotations recognized by registered annotation introspector. + */ + public abstract boolean hasKnownClassAnnotations(); + + /** + * Accessor for type bindings that may be needed to fully resolve + * types of member object, such as return and argument types of + * methods and constructors, and types of fields. + * + * @deprecated Since 2.7, use {@link #resolveType(java.lang.reflect.Type)} instead. + */ + @Deprecated + public abstract TypeBindings bindingsForBeanType(); + + /** + * Method for resolving given JDK type, using this bean as the + * generic type resolution context. + */ + public abstract JavaType resolveType(java.lang.reflect.Type jdkType); + + /** + * Method for accessing collection of annotations the bean + * class has. + */ + public abstract Annotations getClassAnnotations(); + + /* + /********************************************************** + /* Basic API for finding properties + /********************************************************** + */ + + /** + * @return Ordered Map with logical property name as key, and + * matching getter method as value. + */ + public abstract List findProperties(); + + /** + * Method for locating all back-reference properties (setters, fields) bean has + */ + public abstract Map findBackReferenceProperties(); + + public abstract Set getIgnoredPropertyNames(); + + /* + /********************************************************** + /* Basic API for finding creator members + /********************************************************** + */ + + public abstract List getConstructors(); + + public abstract List getFactoryMethods(); + + /** + * Method that will locate the no-arg constructor for this class, + * if it has one, and that constructor has not been marked as + * ignorable. + */ + public abstract AnnotatedConstructor findDefaultConstructor(); + + /** + * Method that can be called to locate a single-arg constructor that + * takes specified exact type (will not accept supertype constructors) + * + * @param argTypes Type(s) of the argument that we are looking for + */ + public abstract Constructor findSingleArgConstructor(Class... argTypes); + + /** + * Method that can be called to find if introspected class declares + * a static "valueOf" factory method that returns an instance of + * introspected type, given one of acceptable types. + * + * @param expArgTypes Types that the matching single argument factory + * method can take: will also accept super types of these types + * (ie. arg just has to be assignable from expArgType) + */ + public abstract Method findFactoryMethod(Class... expArgTypes); + + /* + /********************************************************** + /* Basic API for finding property accessors + /********************************************************** + */ + + public abstract AnnotatedMember findAnyGetter(); + + /** + * Method used to locate the method of introspected class that + * implements {@link com.fasterxml.jackson.annotation.JsonAnySetter}. If no such method exists + * null is returned. If more than one are found, an exception + * is thrown. + * Additional checks are also made to see that method signature + * is acceptable: needs to take 2 arguments, first one String or + * Object; second any can be any type. + */ + public abstract AnnotatedMethod findAnySetter(); + + /** + * Method for locating the getter method that is annotated with + * {@link com.fasterxml.jackson.annotation.JsonValue} annotation, + * if any. If multiple ones are found, + * an error is reported by throwing {@link IllegalArgumentException} + */ + public abstract AnnotatedMethod findJsonValueMethod(); + + public abstract AnnotatedMethod findMethod(String name, Class[] paramTypes); + + /* + /********************************************************** + /* Basic API, class configuration + /********************************************************** + */ + + /** + * @since 2.7 + */ + public abstract JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue); + + /** + * Method for checking what is the expected format for POJO, as + * defined by defaults and possible annotations. + * Note that this may be further refined by per-property annotations. + * + * @since 2.1 + */ + public abstract JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue); + + /** + * Method for finding {@link Converter} used for serializing instances + * of this class. + * + * @since 2.2 + */ + public abstract Converter findSerializationConverter(); + + /** + * Method for finding {@link Converter} used for serializing instances + * of this class. + * + * @since 2.2 + */ + public abstract Converter findDeserializationConverter(); + + /** + * Accessor for possible description for the bean type, used for constructing + * documentation. + * + * @since 2.7 + */ + public String findClassDescription() { return null; } + + /* + /********************************************************** + /* Basic API, other + /********************************************************** + */ + + public abstract Map findInjectables(); + + /** + * Method for checking if the POJO type has annotations to + * indicate that a builder is to be used for instantiating + * instances and handling data binding, instead of standard + * bean deserializer. + */ + public abstract Class findPOJOBuilder(); + + /** + * Method for finding configuration for POJO Builder class. + */ + public abstract JsonPOJOBuilder.Value findPOJOBuilderConfig(); + + /** + * Method called to create a "default instance" of the bean, currently + * only needed for obtaining default field values which may be used for + * suppressing serialization of fields that have "not changed". + * + * @param fixAccess If true, method is allowed to fix access to the + * default constructor (to be able to call non-public constructor); + * if false, has to use constructor as is. + * + * @return Instance of class represented by this descriptor, if + * suitable default constructor was found; null otherwise. + */ + public abstract Object instantiateBean(boolean fixAccess); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/BeanProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/BeanProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/BeanProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,332 @@ +package com.fasterxml.jackson.databind; + +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.util.Annotations; +import com.fasterxml.jackson.databind.util.Named; + +/** + * Bean properties are logical entities that represent data + * that Java objects (POJOs (Plain Old Java Objects), sometimes also called "beans") + * contain; and that are accessed using accessors (methods like getters + * and setters, fields, constructor parametrers). + * Instances allow access to annotations directly associated + * to property (via field or method), as well as contextual + * annotations (annotations for class that contains properties). + *

+ * Instances are not typically passed when constructing serializers + * and deserializers, but rather only passed when context + * is known when + * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer} and + * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer} + * resolution occurs (createContextual(...) method is called). + * References may (need to) be retained by serializers and deserializers, + * especially when further resolving dependant handlers like value + * serializers/deserializers or structured types. + */ +public interface BeanProperty extends Named +{ + public final static JsonFormat.Value EMPTY_FORMAT = new JsonFormat.Value(); + public final static JsonInclude.Value EMPTY_INCLUDE = JsonInclude.Value.empty(); + + /** + * Method to get logical name of the property + */ + @Override + public String getName(); + + /** + * Method for getting full name definition, including possible + * format-specific additional properties (such as namespace when + * using XML backend). + * + * @since 2.3 + */ + public PropertyName getFullName(); + + /** + * Method to get declared type of the property. + */ + public JavaType getType(); + + /** + * If property is indicated to be wrapped, name of + * wrapper element to use. + * + * @since 2.2 + */ + public PropertyName getWrapperName(); + + /** + * Accessor for additional optional information about property. + * + * @since 2.3 + * + * @return Metadata about property; never null. + */ + public PropertyMetadata getMetadata(); + + /** + * Whether value for property is marked as required using + * annotations or associated schema. + * Equivalent to: + * + * getMetadata().isRequired() + * + * + * @since 2.2 + */ + public boolean isRequired(); + + /** + * Accessor for checking whether there is an actual physical property + * behind this property abstraction or not. + * + * @since 2.7 + */ + public boolean isVirtual(); + + /* + /********************************************************** + /* Access to annotation information + /********************************************************** + */ + + /** + * Method for finding annotation associated with this property; + * meaning annotation associated with one of entities used to + * access property. + *

+ * Note that this method should only be called for custom annotations; + * access to standard Jackson annotations (or ones supported by + * alternate {@link AnnotationIntrospector}s) should be accessed + * through {@link AnnotationIntrospector}. + */ + public A getAnnotation(Class acls); + + /** + * Method for finding annotation associated with context of + * this property; usually class in which member is declared + * (or its subtype if processing subtype). + *

+ * Note that this method should only be called for custom annotations; + * access to standard Jackson annotations (or ones supported by + * alternate {@link AnnotationIntrospector}s) should be accessed + * through {@link AnnotationIntrospector}. + */ + public A getContextAnnotation(Class acls); + + /** + * Method for accessing primary physical entity that represents the property; + * annotated field, method or constructor property. + */ + public AnnotatedMember getMember(); + + /** + * Convenience method that is roughly equivalent to + *

+     *   return intr.findFormat(getMember());
+     *
+ * and specifically does NOT try to find per-type format defaults to merge; + * use {@link #findPropertyFormat} if such defaults would be useful. + * + * @since 2.6 + */ + public JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr); + + /** + * Helper method used to look up format settings applicable to this property, + * considering both possible per-type configuration settings + * + * @since 2.7 + */ + public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType); + + /** + * Convenience method that is roughly equivalent to + *
+     *   return config.getAnnotationIntrospector().findPropertyInclusion(getMember());
+     *
+ * but also considers global default settings for inclusion + * + * @since 2.7 + */ + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Class baseType); + + /* + /********************************************************** + /* Schema/introspection support + /********************************************************** + */ + + /** + * Method that can be called to visit the type structure that this + * property is part of. + * Note that not all implementations support traversal with this + * method; those that do not should throw + * {@link UnsupportedOperationException}. + *

+ * NOTE: Starting with 2.7, takes explicit {@link SerializerProvider} + * argument to reduce the need to rely on provider visitor may or may not + * have assigned. + * + * @param objectVisitor Visitor to used as the callback handler + * + * @since 2.2 (although signature did change in 2.7) + */ + public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) + throws JsonMappingException; + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Simple stand-alone implementation, useful as a placeholder + * or base class for more complex implementations. + */ + public static class Std implements BeanProperty + { + + protected final PropertyName _name; + protected final JavaType _type; + protected final PropertyName _wrapperName; + + protected final PropertyMetadata _metadata; + + /** + * Physical entity (field, method or constructor argument) that + * is used to access value of property (or in case of constructor + * property, just placeholder) + */ + protected final AnnotatedMember _member; + + /** + * Annotations defined in the context class (if any); may be null + * if no annotations were found + */ + protected final Annotations _contextAnnotations; + + public Std(PropertyName name, JavaType type, PropertyName wrapperName, + Annotations contextAnnotations, AnnotatedMember member, + PropertyMetadata metadata) + { + _name = name; + _type = type; + _wrapperName = wrapperName; + _metadata = metadata; + _member = member; + _contextAnnotations = contextAnnotations; + } + + /** + * @since 2.6 + */ + public Std(Std base, JavaType newType) { + this(base._name, newType, base._wrapperName, base._contextAnnotations, base._member, base._metadata); + } + + @Deprecated // since 2.3 + public Std(String name, JavaType type, PropertyName wrapperName, + Annotations contextAnnotations, AnnotatedMember member, + boolean isRequired) + { + this(new PropertyName(name), type, wrapperName, contextAnnotations, + member, + isRequired ? PropertyMetadata.STD_REQUIRED : PropertyMetadata.STD_OPTIONAL); + } + + public Std withType(JavaType type) { + return new Std(this, type); + } + + @Override + public A getAnnotation(Class acls) { + return (_member == null) ? null : _member.getAnnotation(acls); + } + + @Override + public A getContextAnnotation(Class acls) { + return (_contextAnnotations == null) ? null : _contextAnnotations.get(acls); + } + + @Override + @Deprecated + public JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr) { + if ((_member != null) && (intr != null)) { + JsonFormat.Value v = intr.findFormat(_member); + if (v != null) { + return v; + } + } + return EMPTY_FORMAT; + } + + @Override + public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType) { + JsonFormat.Value v0 = config.getDefaultPropertyFormat(baseType); + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + if ((intr == null) || (_member == null)) { + return v0; + } + JsonFormat.Value v = intr.findFormat(_member); + if (v == null) { + return v0; + } + return v0.withOverrides(v); + } + + @Override + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Class baseType) + { + JsonInclude.Value v0 = config.getDefaultPropertyInclusion(baseType); + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + if ((intr == null) || (_member == null)) { + return v0; + } + JsonInclude.Value v = intr.findPropertyInclusion(_member); + if (v == null) { + return v0; + } + return v0.withOverrides(v); + } + + @Override public String getName() { return _name.getSimpleName(); } + @Override public PropertyName getFullName() { return _name; } + @Override public JavaType getType() { return _type; } + @Override public PropertyName getWrapperName() { return _wrapperName; } + @Override public boolean isRequired() { return _metadata.isRequired(); } + @Override public PropertyMetadata getMetadata() { return _metadata; } + @Override public AnnotatedMember getMember() { return _member; } + + /** + *

+ * TODO: move to {@link BeanProperty} in near future, once all standard + * implementations define it. + * + * @since 2.5 + */ + @Override + public boolean isVirtual() { return false; } + + /** + * Implementation of this method throws + * {@link UnsupportedOperationException}, since instances of this + * implementation should not be used as part of actual structure + * visited. Rather, other implementations should handle it. + */ + @Override + public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) { + throw new UnsupportedOperationException("Instances of "+getClass().getName()+" should not get visited"); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DatabindContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DatabindContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DatabindContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,228 @@ +package com.fasterxml.jackson.databind; + +import java.lang.reflect.Type; +import java.util.Locale; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Shared base class for {@link DeserializationContext} and + * {@link SerializerProvider}, context objects passed through data-binding + * process. Designed so that some of implementations can rely on shared + * aspects like access to secondary contextual objects like type factories + * or handler instantiators. + * + * @since 2.2 + */ +public abstract class DatabindContext +{ + /* + /********************************************************** + /* Generic config access + /********************************************************** + */ + + /** + * Accessor to currently active configuration (both per-request configs + * and per-mapper config). + */ + public abstract MapperConfig getConfig(); + + /** + * Convenience method for accessing serialization view in use (if any); equivalent to: + *

+     *   getConfig().getAnnotationIntrospector();
+     *
+ */ + public abstract AnnotationIntrospector getAnnotationIntrospector(); + + /* + /********************************************************** + /* Access to specific config settings + /********************************************************** + */ + + /** + * Convenience method for checking whether specified serialization + * feature is enabled or not. + * Shortcut for: + *
+     *  getConfig().isEnabled(feature);
+     *
+ */ + public abstract boolean isEnabled(MapperFeature feature); + + /** + * Convenience method for accessing serialization view in use (if any); equivalent to: + *
+     *   getConfig().canOverrideAccessModifiers();
+     *
+ */ + public abstract boolean canOverrideAccessModifiers(); + + /** + * Accessor for locating currently active view, if any; + * returns null if no view has been set. + */ + public abstract Class getActiveView(); + + /** + * @since 2.6 + */ + public abstract Locale getLocale(); + + /** + * @since 2.6 + */ + public abstract TimeZone getTimeZone(); + + /** + * @since 2.7 + */ + public abstract JsonFormat.Value getDefaultPropertyFormat(Class baseType); + + /* + /********************************************************** + /* Generic attributes (2.3+) + /********************************************************** + */ + + /** + * Method for accessing attributes available in this context. + * Per-call attributes have highest precedence; attributes set + * via {@link ObjectReader} or {@link ObjectWriter} have lower + * precedence. + * + * @param key Key of the attribute to get + * @return Value of the attribute, if any; null otherwise + * + * @since 2.3 + */ + public abstract Object getAttribute(Object key); + + /** + * Method for setting per-call value of given attribute. + * This will override any previously defined value for the + * attribute within this context. + * + * @param key Key of the attribute to set + * @param value Value to set attribute to + * + * @return This context object, to allow chaining + * + * @since 2.3 + */ + public abstract DatabindContext setAttribute(Object key, Object value); + + /* + /********************************************************** + /* Type instantiation/resolution + /********************************************************** + */ + + /** + * Convenience method for constructing {@link JavaType} for given JDK + * type (usually {@link java.lang.Class}) + */ + public JavaType constructType(Type type) { + return getTypeFactory().constructType(type); + } + + /** + * Convenience method for constructing subtypes, retaining generic + * type parameter (if any) + */ + public JavaType constructSpecializedType(JavaType baseType, Class subclass) { + // simple optimization to avoid costly introspection if type-erased type does NOT differ + if (baseType.getRawClass() == subclass) { + return baseType; + } + return getConfig().constructSpecializedType(baseType, subclass); + } + + public abstract TypeFactory getTypeFactory(); + + /* + /********************************************************** + /* Helper object construction + /********************************************************** + */ + + public ObjectIdGenerator objectIdGeneratorInstance(Annotated annotated, + ObjectIdInfo objectIdInfo) + throws JsonMappingException + { + Class implClass = objectIdInfo.getGeneratorType(); + final MapperConfig config = getConfig(); + HandlerInstantiator hi = config.getHandlerInstantiator(); + ObjectIdGenerator gen = (hi == null) ? null : hi.objectIdGeneratorInstance(config, annotated, implClass); + if (gen == null) { + gen = (ObjectIdGenerator) ClassUtil.createInstance(implClass, + config.canOverrideAccessModifiers()); + } + return gen.forScope(objectIdInfo.getScope()); + } + + public ObjectIdResolver objectIdResolverInstance(Annotated annotated, ObjectIdInfo objectIdInfo) + { + Class implClass = objectIdInfo.getResolverType(); + final MapperConfig config = getConfig(); + HandlerInstantiator hi = config.getHandlerInstantiator(); + ObjectIdResolver resolver = (hi == null) ? null : hi.resolverIdGeneratorInstance(config, annotated, implClass); + if (resolver == null) { + resolver = ClassUtil.createInstance(implClass, config.canOverrideAccessModifiers()); + } + + return resolver; + } + + /** + * Helper method to use to construct a {@link Converter}, given a definition + * that may be either actual converter instance, or Class for instantiating one. + * + * @since 2.2 + */ + @SuppressWarnings("unchecked") + public Converter converterInstance(Annotated annotated, + Object converterDef) + throws JsonMappingException + { + if (converterDef == null) { + return null; + } + if (converterDef instanceof Converter) { + return (Converter) converterDef; + } + if (!(converterDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type " + +converterDef.getClass().getName()+"; expected type Converter or Class instead"); + } + Class converterClass = (Class)converterDef; + // there are some known "no class" markers to consider too: + if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) { + return null; + } + if (!Converter.class.isAssignableFrom(converterClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class " + +converterClass.getName()+"; expected Class"); + } + final MapperConfig config = getConfig(); + HandlerInstantiator hi = config.getHandlerInstantiator(); + Converter conv = (hi == null) ? null : hi.converterInstance(config, annotated, converterClass); + if (conv == null) { + conv = (Converter) ClassUtil.createInstance(converterClass, + config.canOverrideAccessModifiers()); + } + return (Converter) conv; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationConfig.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationConfig.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationConfig.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,963 @@ +package com.fasterxml.jackson.databind; + +import java.text.DateFormat; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.cfg.*; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.*; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.LinkedNode; +import com.fasterxml.jackson.databind.util.RootNameLookup; + +/** + * Object that contains baseline configuration for deserialization + * process. An instance is owned by {@link ObjectMapper}, which + * passes an immutable instance to be used for deserialization process. + *

+ * Note that instances are considered immutable and as such no copies + * should need to be created for sharing; all copying is done with + * "fluent factory" methods. + */ +public final class DeserializationConfig + extends MapperConfigBase + implements java.io.Serializable // since 2.1 +{ + // since 2.5 + private static final long serialVersionUID = 1; + + /* + /********************************************************** + /* Configured helper objects + /********************************************************** + */ + + /** + * Linked list that contains all registered problem handlers. + * Implementation as front-added linked list allows for sharing + * of the list (tail) without copying the list. + */ + protected final LinkedNode _problemHandlers; + + /** + * Factory used for constructing {@link com.fasterxml.jackson.databind.JsonNode} instances. + */ + protected final JsonNodeFactory _nodeFactory; + + /* + /********************************************************** + /* Deserialization features + /********************************************************** + */ + + /** + * Set of {@link DeserializationFeature}s enabled. + */ + protected final int _deserFeatures; + + /* + /********************************************************** + /* Parser features: generic, format-specific + /********************************************************** + */ + + /** + * States of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable. + */ + protected final int _parserFeatures; + + /** + * Bitflag of {@link com.fasterxml.jackson.core.JsonParser.Feature}s to enable/disable + */ + protected final int _parserFeaturesToChange; + + /** + * States of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable. + * + * @since 2.7 + */ + protected final int _formatReadFeatures; + + /** + * Bitflag of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable + * + * @since 2.7 + */ + protected final int _formatReadFeaturesToChange; + + /* + /********************************************************** + /* Life-cycle, constructors + /********************************************************** + */ + + /** + * Constructor used by ObjectMapper to create default configuration object instance. + */ + public DeserializationConfig(BaseSettings base, + SubtypeResolver str, SimpleMixInResolver mixins, + RootNameLookup rootNames) + { + super(base, str, mixins, rootNames); + _deserFeatures = collectFeatureDefaults(DeserializationFeature.class); + _nodeFactory = JsonNodeFactory.instance; + _problemHandlers = null; + _parserFeatures = 0; + _parserFeaturesToChange = 0; + _formatReadFeatures = 0; + _formatReadFeaturesToChange = 0; + } + + private DeserializationConfig(DeserializationConfig src, + int mapperFeatures, int deserFeatures, + int parserFeatures, int parserFeatureMask, + int formatFeatures, int formatFeatureMask) + { + super(src, mapperFeatures); + _deserFeatures = deserFeatures; + _nodeFactory = src._nodeFactory; + _problemHandlers = src._problemHandlers; + _parserFeatures = parserFeatures; + _parserFeaturesToChange = parserFeatureMask; + _formatReadFeatures = formatFeatures; + _formatReadFeaturesToChange = formatFeatureMask; + } + + /** + * Copy constructor used to create a non-shared instance with given mix-in + * annotation definitions and subtype resolver. + */ + private DeserializationConfig(DeserializationConfig src, SubtypeResolver str) + { + super(src, str); + _deserFeatures = src._deserFeatures; + _nodeFactory = src._nodeFactory; + _problemHandlers = src._problemHandlers; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + private DeserializationConfig(DeserializationConfig src, BaseSettings base) + { + super(src, base); + _deserFeatures = src._deserFeatures; + _nodeFactory = src._nodeFactory; + _problemHandlers = src._problemHandlers; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + private DeserializationConfig(DeserializationConfig src, JsonNodeFactory f) + { + super(src); + _deserFeatures = src._deserFeatures; + _problemHandlers = src._problemHandlers; + _nodeFactory = f; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + private DeserializationConfig(DeserializationConfig src, + LinkedNode problemHandlers) + { + super(src); + _deserFeatures = src._deserFeatures; + _problemHandlers = problemHandlers; + _nodeFactory = src._nodeFactory; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + private DeserializationConfig(DeserializationConfig src, PropertyName rootName) + { + super(src, rootName); + _deserFeatures = src._deserFeatures; + _problemHandlers = src._problemHandlers; + _nodeFactory = src._nodeFactory; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + private DeserializationConfig(DeserializationConfig src, Class view) + { + super(src, view); + _deserFeatures = src._deserFeatures; + _problemHandlers = src._problemHandlers; + _nodeFactory = src._nodeFactory; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + protected DeserializationConfig(DeserializationConfig src, ContextAttributes attrs) + { + super(src, attrs); + _deserFeatures = src._deserFeatures; + _problemHandlers = src._problemHandlers; + _nodeFactory = src._nodeFactory; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + protected DeserializationConfig(DeserializationConfig src, SimpleMixInResolver mixins) + { + super(src, mixins); + _deserFeatures = src._deserFeatures; + _problemHandlers = src._problemHandlers; + _nodeFactory = src._nodeFactory; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + /** + * Copy-constructor used for making a copy to be used by new {@link ObjectMapper} + * or {@link ObjectReader}. + * + * @since 2.6 + */ + protected DeserializationConfig(DeserializationConfig src, SimpleMixInResolver mixins, + RootNameLookup rootNames) + { + super(src, mixins, rootNames); + _deserFeatures = src._deserFeatures; + _problemHandlers = src._problemHandlers; + _nodeFactory = src._nodeFactory; + _parserFeatures = src._parserFeatures; + _parserFeaturesToChange = src._parserFeaturesToChange; + _formatReadFeatures = src._formatReadFeatures; + _formatReadFeaturesToChange = src._formatReadFeaturesToChange; + } + + // for unit tests only: + protected BaseSettings getBaseSettings() { return _base; } + + /* + /********************************************************** + /* Life-cycle, factory methods from MapperConfig + /********************************************************** + */ + + @Override + public DeserializationConfig with(MapperFeature... features) + { + int newMapperFlags = _mapperFeatures; + for (MapperFeature f : features) { + newMapperFlags |= f.getMask(); + } + return (newMapperFlags == _mapperFeatures) ? this : + new DeserializationConfig(this, newMapperFlags, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + + } + + @Override + public DeserializationConfig without(MapperFeature... features) + { + int newMapperFlags = _mapperFeatures; + for (MapperFeature f : features) { + newMapperFlags &= ~f.getMask(); + } + return (newMapperFlags == _mapperFeatures) ? this : + new DeserializationConfig(this, newMapperFlags, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + @Override + public DeserializationConfig with(MapperFeature feature, boolean state) + { + int newMapperFlags; + if (state) { + newMapperFlags = _mapperFeatures | feature.getMask(); + } else { + newMapperFlags = _mapperFeatures & ~feature.getMask(); + } + return (newMapperFlags == _mapperFeatures) ? this : + new DeserializationConfig(this, newMapperFlags, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + @Override + public DeserializationConfig with(ClassIntrospector ci) { + return _withBase(_base.withClassIntrospector(ci)); + } + + @Override + public DeserializationConfig with(AnnotationIntrospector ai) { + return _withBase(_base.withAnnotationIntrospector(ai)); + } + + @Override + public DeserializationConfig with(VisibilityChecker vc) { + return _withBase(_base.withVisibilityChecker(vc)); + } + + @Override + public DeserializationConfig withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) { + return _withBase( _base.withVisibility(forMethod, visibility)); + } + + @Override + public DeserializationConfig with(TypeResolverBuilder trb) { + return _withBase(_base.withTypeResolverBuilder(trb)); + } + + @Override + public DeserializationConfig with(SubtypeResolver str) { + return (_subtypeResolver == str) ? this : new DeserializationConfig(this, str); + } + + @Override + public DeserializationConfig with(PropertyNamingStrategy pns) { + return _withBase(_base.withPropertyNamingStrategy(pns)); + } + + @Override + public DeserializationConfig withRootName(PropertyName rootName) { + if (rootName == null) { + if (_rootName == null) { + return this; + } + } else if (rootName.equals(_rootName)) { + return this; + } + return new DeserializationConfig(this, rootName); + } + + @Override + public DeserializationConfig with(TypeFactory tf) { + return _withBase( _base.withTypeFactory(tf)); + } + + @Override + public DeserializationConfig with(DateFormat df) { + return _withBase(_base.withDateFormat(df)); + } + + @Override + public DeserializationConfig with(HandlerInstantiator hi) { + return _withBase(_base.withHandlerInstantiator(hi)); + } + + @Override + public DeserializationConfig withInsertedAnnotationIntrospector(AnnotationIntrospector ai) { + return _withBase(_base.withInsertedAnnotationIntrospector(ai)); + } + + @Override + public DeserializationConfig withAppendedAnnotationIntrospector(AnnotationIntrospector ai) { + return _withBase(_base.withAppendedAnnotationIntrospector(ai)); + } + + @Override + public DeserializationConfig withView(Class view) { + return (_view == view) ? this : new DeserializationConfig(this, view); + } + + @Override + public DeserializationConfig with(Locale l) { + return _withBase(_base.with(l)); + } + + @Override + public DeserializationConfig with(TimeZone tz) { + return _withBase(_base.with(tz)); + } + + @Override + public DeserializationConfig with(Base64Variant base64) { + return _withBase(_base.with(base64)); + } + + @Override + public DeserializationConfig with(ContextAttributes attrs) { + return (attrs == _attributes) ? this : new DeserializationConfig(this, attrs); + } + + private final DeserializationConfig _withBase(BaseSettings newBase) { + return (_base == newBase) ? this : new DeserializationConfig(this, newBase); + } + + /* + /********************************************************** + /* Life-cycle, DeserializationFeature-based factory methods + /********************************************************** + */ + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + */ + public DeserializationConfig with(DeserializationFeature feature) + { + int newDeserFeatures = (_deserFeatures | feature.getMask()); + return (newDeserFeatures == _deserFeatures) ? this : + new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + */ + public DeserializationConfig with(DeserializationFeature first, + DeserializationFeature... features) + { + int newDeserFeatures = _deserFeatures | first.getMask(); + for (DeserializationFeature f : features) { + newDeserFeatures |= f.getMask(); + } + return (newDeserFeatures == _deserFeatures) ? this : + new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + */ + public DeserializationConfig withFeatures(DeserializationFeature... features) + { + int newDeserFeatures = _deserFeatures; + for (DeserializationFeature f : features) { + newDeserFeatures |= f.getMask(); + } + return (newDeserFeatures == _deserFeatures) ? this : + new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature disabled. + */ + public DeserializationConfig without(DeserializationFeature feature) + { + int newDeserFeatures = _deserFeatures & ~feature.getMask(); + return (newDeserFeatures == _deserFeatures) ? this : + new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + */ + public DeserializationConfig without(DeserializationFeature first, + DeserializationFeature... features) + { + int newDeserFeatures = _deserFeatures & ~first.getMask(); + for (DeserializationFeature f : features) { + newDeserFeatures &= ~f.getMask(); + } + return (newDeserFeatures == _deserFeatures) ? this : + new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + */ + public DeserializationConfig withoutFeatures(DeserializationFeature... features) + { + int newDeserFeatures = _deserFeatures; + for (DeserializationFeature f : features) { + newDeserFeatures &= ~f.getMask(); + } + return (newDeserFeatures == _deserFeatures) ? this : + new DeserializationConfig(this, _mapperFeatures, newDeserFeatures, + _parserFeatures, _parserFeaturesToChange, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /* + /********************************************************** + /* Life-cycle, JsonParser.Feature-based factory methods + /********************************************************** + */ + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + * + * @since 2.5 + */ + public DeserializationConfig with(JsonParser.Feature feature) + { + int newSet = _parserFeatures | feature.getMask(); + int newMask = _parserFeaturesToChange | feature.getMask(); + return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + newSet, newMask, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + * + * @since 2.5 + */ + public DeserializationConfig withFeatures(JsonParser.Feature... features) + { + int newSet = _parserFeatures; + int newMask = _parserFeaturesToChange; + for (JsonParser.Feature f : features) { + int mask = f.getMask(); + newSet |= mask; + newMask |= mask; + } + return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + newSet, newMask, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature disabled. + * + * @since 2.5 + */ + public DeserializationConfig without(JsonParser.Feature feature) + { + int newSet = _parserFeatures & ~feature.getMask(); + int newMask = _parserFeaturesToChange | feature.getMask(); + return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + newSet, newMask, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + * + * @since 2.5 + */ + public DeserializationConfig withoutFeatures(JsonParser.Feature... features) + { + int newSet = _parserFeatures; + int newMask = _parserFeaturesToChange; + for (JsonParser.Feature f : features) { + int mask = f.getMask(); + newSet &= ~mask; + newMask |= mask; + } + return ((_parserFeatures == newSet) && (_parserFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + newSet, newMask, + _formatReadFeatures, _formatReadFeaturesToChange); + } + + /* + /********************************************************** + /* Life-cycle, JsonParser.FormatFeature-based factory methods + /********************************************************** + */ + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + * + * @since 2.7 + */ + public DeserializationConfig with(FormatFeature feature) + { + int newSet = _formatReadFeatures | feature.getMask(); + int newMask = _formatReadFeaturesToChange | feature.getMask(); + return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + newSet, newMask); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + * + * @since 2.7 + */ + public DeserializationConfig withFeatures(FormatFeature... features) + { + int newSet = _formatReadFeatures; + int newMask = _formatReadFeaturesToChange; + for (FormatFeature f : features) { + int mask = f.getMask(); + newSet |= mask; + newMask |= mask; + } + return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + newSet, newMask); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature disabled. + * + * @since 2.7 + */ + public DeserializationConfig without(FormatFeature feature) + { + int newSet = _formatReadFeatures & ~feature.getMask(); + int newMask = _formatReadFeaturesToChange | feature.getMask(); + return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + newSet, newMask); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + * + * @since 2.7 + */ + public DeserializationConfig withoutFeatures(FormatFeature... features) + { + int newSet = _formatReadFeatures; + int newMask = _formatReadFeaturesToChange; + for (FormatFeature f : features) { + int mask = f.getMask(); + newSet &= ~mask; + newMask |= mask; + } + return ((_formatReadFeatures == newSet) && (_formatReadFeaturesToChange == newMask)) ? this : + new DeserializationConfig(this, _mapperFeatures, _deserFeatures, + _parserFeatures, _parserFeaturesToChange, + newSet, newMask); + } + + /* + /********************************************************** + /* Life-cycle, deserialization-specific factory methods + /********************************************************** + */ + + /** + * Fluent factory method that will construct a new instance with + * specified {@link JsonNodeFactory} + */ + public DeserializationConfig with(JsonNodeFactory f) { + if (_nodeFactory == f) { + return this; + } + return new DeserializationConfig(this, f); + } + + /** + * Method that can be used to add a handler that can (try to) + * resolve non-fatal deserialization problems. + */ + public DeserializationConfig withHandler(DeserializationProblemHandler h) + { + // Sanity check: let's prevent adding same handler multiple times + if (LinkedNode.contains(_problemHandlers, h)) { + return this; + } + return new DeserializationConfig(this, + new LinkedNode(h, _problemHandlers)); + } + + /** + * Method for removing all configured problem handlers; usually done to replace + * existing handler(s) with different one(s) + */ + public DeserializationConfig withNoProblemHandlers() { + if (_problemHandlers == null) { + return this; + } + return new DeserializationConfig(this, + (LinkedNode) null); + } + + /* + /********************************************************** + /* JsonParser initialization + /********************************************************** + */ + + /** + * Method called by {@link ObjectMapper} and {@link ObjectReader} + * to modify those {@link com.fasterxml.jackson.core.JsonParser.Feature} settings + * that have been configured via this config instance. + * + * @since 2.5 + */ + public void initialize(JsonParser p) { + if (_parserFeaturesToChange != 0) { + p.overrideStdFeatures(_parserFeatures, _parserFeaturesToChange); + } + if (_formatReadFeaturesToChange != 0) { + p.overrideFormatFeatures(_formatReadFeatures, _formatReadFeaturesToChange); + } + } + + /* + /********************************************************** + /* MapperConfig implementation/overrides: introspection + /********************************************************** + */ + + /** + * Method for getting {@link AnnotationIntrospector} configured + * to introspect annotation values used for configuration. + */ + @Override + public AnnotationIntrospector getAnnotationIntrospector() + { + /* 29-Jul-2009, tatu: it's now possible to disable use of + * annotations; can be done using "no-op" introspector + */ + if (isEnabled(MapperFeature.USE_ANNOTATIONS)) { + return super.getAnnotationIntrospector(); + } + return NopAnnotationIntrospector.instance; + } + + /** + * Accessor for getting bean description that only contains class + * annotations: useful if no getter/setter/creator information is needed. + */ + @Override + public BeanDescription introspectClassAnnotations(JavaType type) { + return getClassIntrospector().forClassAnnotations(this, type, this); + } + + /** + * Accessor for getting bean description that only contains immediate class + * annotations: ones from the class, and its direct mix-in, if any, but + * not from super types. + */ + @Override + public BeanDescription introspectDirectClassAnnotations(JavaType type) { + return getClassIntrospector().forDirectClassAnnotations(this, type, this); + } + + @Override + public VisibilityChecker getDefaultVisibilityChecker() + { + VisibilityChecker vchecker = super.getDefaultVisibilityChecker(); + if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) { + vchecker = vchecker.withSetterVisibility(Visibility.NONE); + } + if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) { + vchecker = vchecker.withCreatorVisibility(Visibility.NONE); + } + if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) { + vchecker = vchecker.withFieldVisibility(Visibility.NONE); + } + return vchecker; + } + + /* + /********************************************************** + /* Configuration: default settings with per-type overrides + /********************************************************** + */ + + // property inclusion not used on deserialization yet (2.7): may be added in future + @Override + public JsonInclude.Value getDefaultPropertyInclusion() { + return EMPTY_INCLUDE; + } + + @Override + public JsonInclude.Value getDefaultPropertyInclusion(Class baseType) { + return EMPTY_INCLUDE; + } + + @Override + public JsonFormat.Value getDefaultPropertyFormat(Class baseType) { + // !!! TODO: per-type defaults + return EMPTY_FORMAT; + } + + /* + /********************************************************** + /* MapperConfig implementation/overrides: other + /********************************************************** + */ + + @Override + public boolean useRootWrapping() + { + if (_rootName != null) { // empty String disables wrapping; non-empty enables + return !_rootName.isEmpty(); + } + return isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE); + } + + public final boolean isEnabled(DeserializationFeature f) { + return (_deserFeatures & f.getMask()) != 0; + } + + public final boolean isEnabled(JsonParser.Feature f, JsonFactory factory) { + int mask = f.getMask(); + if ((_parserFeaturesToChange & mask) != 0) { + return (_parserFeatures & f.getMask()) != 0; + } + return factory.isEnabled(f); + } + + /** + * Bulk access method for checking that all features specified by + * mask are enabled. + * + * @since 2.3 + */ + public final boolean hasDeserializationFeatures(int featureMask) { + return (_deserFeatures & featureMask) == featureMask; + } + + /** + * Bulk access method for checking that at least one of features specified by + * mask is enabled. + * + * @since 2.6 + */ + public final boolean hasSomeOfFeatures(int featureMask) { + return (_deserFeatures & featureMask) != 0; + } + + /** + * Bulk access method for getting the bit mask of all {@link DeserializationFeature}s + * that are enabled. + */ + public final int getDeserializationFeatures() { + return _deserFeatures; + } + + /* + /********************************************************** + /* Other configuration + /********************************************************** + */ + + /** + * Method for getting head of the problem handler chain. May be null, + * if no handlers have been added. + */ + public LinkedNode getProblemHandlers() { + return _problemHandlers; + } + + public final JsonNodeFactory getNodeFactory() { + return _nodeFactory; + } + + /* + /********************************************************** + /* Introspection methods + /********************************************************** + */ + + /** + * Method that will introspect full bean properties for the purpose + * of building a bean deserializer + * + * @param type Type of class to be introspected + */ + @SuppressWarnings("unchecked") + public T introspect(JavaType type) { + return (T) getClassIntrospector().forDeserialization(this, type, this); + } + + /** + * Method that will introspect subset of bean properties needed to + * construct bean instance. + */ + @SuppressWarnings("unchecked") + public T introspectForCreation(JavaType type) { + return (T) getClassIntrospector().forCreation(this, type, this); + } + + /** + * @since 2.0 + */ + @SuppressWarnings("unchecked") + public T introspectForBuilder(JavaType type) { + return (T) getClassIntrospector().forDeserializationWithBuilder(this, type, this); + } + + /* + /********************************************************** + /* Support for polymorphic type handling + /********************************************************** + */ + + /** + * Helper method that is needed to properly handle polymorphic referenced + * types, such as types referenced by {@link java.util.concurrent.atomic.AtomicReference}, + * or various "optional" types. + * + * @since 2.4 + */ + public TypeDeserializer findTypeDeserializer(JavaType baseType) + throws JsonMappingException + { + BeanDescription bean = introspectClassAnnotations(baseType.getRawClass()); + AnnotatedClass ac = bean.getClassInfo(); + TypeResolverBuilder b = getAnnotationIntrospector().findTypeResolver(this, ac, baseType); + + /* Ok: if there is no explicit type info handler, we may want to + * use a default. If so, config object knows what to use. + */ + Collection subtypes = null; + if (b == null) { + b = getDefaultTyper(baseType); + if (b == null) { + return null; + } + } else { + subtypes = getSubtypeResolver().collectAndResolveSubtypesByTypeId(this, ac); + } + /* 04-May-2014, tatu: When called from DeserializerFactory, additional code like + * this is invoked. But here we do not actually have access to mappings, so not + * quite sure what to do, if anything. May need to revisit if the underlying + * problem re-surfaces... + */ + /* + if ((b.getDefaultImpl() == null) && baseType.isAbstract()) { + JavaType defaultType = mapAbstractType(config, baseType); + if (defaultType != null && defaultType.getRawClass() != baseType.getRawClass()) { + b = b.defaultImpl(defaultType.getRawClass()); + } + } + */ + return b.buildTypeDeserializer(this, baseType, subtypes); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1160 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; +import com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.*; + +/** + * Context for the process of deserialization a single root-level value. + * Used to allow passing in configuration settings and reusable temporary + * objects (scrap arrays, containers). + *

+ * Instance life-cycle is such that an partially configured "blueprint" object + * is registered with {@link ObjectMapper} (and {@link ObjectReader}, + * and when an actual instance is needed for deserialization, + * a fully configured instance will + * be created using a method in excented API of sub-class + * ({@link com.fasterxml.jackson.databind.deser.DefaultDeserializationContext#createInstance}). + * Each instance is guaranteed to only be used from single-threaded context; + * instances may be reused iff no configuration has changed. + *

+ * Defined as abstract class so that implementations must define methods + * for reconfiguring blueprints and creating instances. + */ +public abstract class DeserializationContext + extends DatabindContext + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; // 2.6 + + /** + * Let's limit length of error messages, for cases where underlying data + * may be very large -- no point in spamming logs with megs of meaningless + * data. + */ + private final static int MAX_ERROR_STR_LEN = 500; + + /* + /********************************************************** + /* Configuration, immutable + /********************************************************** + */ + + /** + * Object that handle details of {@link JsonDeserializer} caching. + */ + protected final DeserializerCache _cache; + + /* + /********************************************************** + /* Configuration, changeable via fluent factories + /********************************************************** + */ + + /** + * Read-only factory instance; exposed to let + * owners (ObjectMapper, ObjectReader) + * access it. + */ + protected final DeserializerFactory _factory; + + /* + /********************************************************** + /* Configuration that gets set for instances (not blueprints) + /* (partly denormalized for performance) + /********************************************************** + */ + + /** + * Generic deserialization processing configuration + */ + protected final DeserializationConfig _config; + + /** + * Bitmap of {@link DeserializationFeature}s that are enabled + */ + protected final int _featureFlags; + + /** + * Currently active view, if any. + */ + protected final Class _view; + + /** + * Currently active parser used for deserialization. + * May be different from the outermost parser + * when content is buffered. + */ + protected transient JsonParser _parser; + + /** + * Object used for resolving references to injectable + * values. + */ + protected final InjectableValues _injectableValues; + + /* + /********************************************************** + /* Per-operation reusable helper objects (not for blueprints) + /********************************************************** + */ + + protected transient ArrayBuilders _arrayBuilders; + + protected transient ObjectBuffer _objectBuffer; + + protected transient DateFormat _dateFormat; + + /** + * Lazily-constructed holder for per-call attributes. + * + * @since 2.3 + */ + protected transient ContextAttributes _attributes; + + /** + * Type of {@link JsonDeserializer} (or, more specifically, + * {@link ContextualDeserializer}) that is being + * contextualized currently. + * + * @since 2.5 + */ + protected LinkedNode _currentType; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected DeserializationContext(DeserializerFactory df) { + this(df, null); + } + + protected DeserializationContext(DeserializerFactory df, + DeserializerCache cache) + { + if (df == null) { + throw new IllegalArgumentException("Can not pass null DeserializerFactory"); + } + _factory = df; + _cache = (cache == null) ? new DeserializerCache() : cache; + + _featureFlags = 0; + _config = null; + _injectableValues = null; + _view = null; + _attributes = null; + } + + protected DeserializationContext(DeserializationContext src, + DeserializerFactory factory) + { + _cache = src._cache; + _factory = factory; + + _config = src._config; + _featureFlags = src._featureFlags; + _view = src._view; + _parser = src._parser; + _injectableValues = src._injectableValues; + _attributes = src._attributes; + } + + /** + * Constructor used for creating actual per-call instances. + */ + protected DeserializationContext(DeserializationContext src, + DeserializationConfig config, JsonParser p, + InjectableValues injectableValues) + { + _cache = src._cache; + _factory = src._factory; + + _config = config; + _featureFlags = config.getDeserializationFeatures(); + _view = config.getActiveView(); + _parser = p; + _injectableValues = injectableValues; + _attributes = config.getAttributes(); + } + + /** + * Copy-constructor for use with copy() by {@link ObjectMapper#copy()} + */ + protected DeserializationContext(DeserializationContext src) { + _cache = new DeserializerCache(); + _factory = src._factory; + + _config = src._config; + _featureFlags = src._featureFlags; + _view = src._view; + _injectableValues = null; + } + + /* + /********************************************************** + /* DatabindContext implementation + /********************************************************** + */ + + @Override + public DeserializationConfig getConfig() { return _config; } + + @Override + public final Class getActiveView() { return _view; } + + @Override + public final boolean canOverrideAccessModifiers() { + return _config.canOverrideAccessModifiers(); + } + + @Override + public final boolean isEnabled(MapperFeature feature) { + return _config.isEnabled(feature); + } + + @Override + public final JsonFormat.Value getDefaultPropertyFormat(Class baseType) { + return _config.getDefaultPropertyFormat(baseType); + } + + @Override + public final AnnotationIntrospector getAnnotationIntrospector() { + return _config.getAnnotationIntrospector(); + } + + @Override + public final TypeFactory getTypeFactory() { + return _config.getTypeFactory(); + } + + /** + * Method for accessing default Locale to use: convenience method for + *

+     *   getConfig().getLocale();
+     *
+ */ + @Override + public Locale getLocale() { + return _config.getLocale(); + } + + /** + * Method for accessing default TimeZone to use: convenience method for + *
+     *   getConfig().getTimeZone();
+     *
+ */ + @Override + public TimeZone getTimeZone() { + return _config.getTimeZone(); + } + + /* + /********************************************************** + /* Access to per-call state, like generic attributes (2.3+) + /********************************************************** + */ + + @Override + public Object getAttribute(Object key) { + return _attributes.getAttribute(key); + } + + @Override + public DeserializationContext setAttribute(Object key, Object value) + { + _attributes = _attributes.withPerCallAttribute(key, value); + return this; + } + + /** + * Accessor to {@link JavaType} of currently contextualized + * {@link ContextualDeserializer}, if any. + * This is sometimes useful for generic {@link JsonDeserializer}s that + * do not get passed (or do not retain) type information when being + * constructed: happens for example for deserializers constructed + * from annotations. + * + * @since 2.5 + * + * @return Type of {@link ContextualDeserializer} being contextualized, + * if process is on-going; null if not. + */ + public JavaType getContextualType() { + return (_currentType == null) ? null : _currentType.value(); + } + + /* + /********************************************************** + /* Public API, config setting accessors + /********************************************************** + */ + + /** + * Method for getting current {@link DeserializerFactory}. + */ + public DeserializerFactory getFactory() { + return _factory; + } + + /** + * Convenience method for checking whether specified on/off + * feature is enabled + */ + public final boolean isEnabled(DeserializationFeature feat) { + /* 03-Dec-2010, tatu: minor shortcut; since this is called quite often, + * let's use a local copy of feature settings: + */ + return (_featureFlags & feat.getMask()) != 0; + } + + /** + * Bulk access method for getting the bit mask of all {@link DeserializationFeature}s + * that are enabled. + * + * @since 2.6 + */ + public final int getDeserializationFeatures() { + return _featureFlags; + } + + /** + * Bulk access method for checking that all features specified by + * mask are enabled. + * + * @since 2.3 + */ + public final boolean hasDeserializationFeatures(int featureMask) { + return (_featureFlags & featureMask) == featureMask; + } + + /** + * Bulk access method for checking that at least one of features specified by + * mask is enabled. + * + * @since 2.6 + */ + public final boolean hasSomeOfFeatures(int featureMask) { + return (_featureFlags & featureMask) != 0; + } + + /** + * Method for accessing the currently active parser. + * May be different from the outermost parser + * when content is buffered. + *

+ * Use of this method is discouraged: if code has direct access + * to the active parser, that should be used instead. + */ + public final JsonParser getParser() { return _parser; } + + public final Object findInjectableValue(Object valueId, + BeanProperty forProperty, Object beanInstance) + { + if (_injectableValues == null) { + throw new IllegalStateException("No 'injectableValues' configured, can not inject value with id ["+valueId+"]"); + } + return _injectableValues.findInjectableValue(valueId, this, forProperty, beanInstance); + } + + /** + * Convenience method for accessing the default Base64 encoding + * used for decoding base64 encoded binary content. + * Same as calling: + *

+     *  getConfig().getBase64Variant();
+     *
+ */ + public final Base64Variant getBase64Variant() { + return _config.getBase64Variant(); + } + + /** + * Convenience method, functionally equivalent to: + *
+     *  getConfig().getNodeFactory();
+     * 
+ */ + public final JsonNodeFactory getNodeFactory() { + return _config.getNodeFactory(); + } + + /* + /********************************************************** + /* Public API, pass-through to DeserializerCache + /********************************************************** + */ + + @Deprecated // since 2.3, use overloaded variant + public boolean hasValueDeserializerFor(JavaType type) { + return hasValueDeserializerFor(type, null); + } + + /** + * Method for checking whether we could find a deserializer + * for given type. + * + * @param type + * @since 2.3 + */ + public boolean hasValueDeserializerFor(JavaType type, AtomicReference cause) { + try { + return _cache.hasValueDeserializerFor(this, _factory, type); + } catch (JsonMappingException e) { + if (cause != null) { + cause.set(e); + } + } catch (RuntimeException e) { + if (cause == null) { // earlier behavior + throw e; + } + cause.set(e); + } + return false; + } + + /** + * Method for finding a value deserializer, and creating a contextual + * version if necessary, for value reached via specified property. + */ + @SuppressWarnings("unchecked") + public final JsonDeserializer findContextualValueDeserializer(JavaType type, + BeanProperty prop) throws JsonMappingException + { + JsonDeserializer deser = _cache.findValueDeserializer(this, _factory, type); + if (deser != null) { + deser = (JsonDeserializer) handleSecondaryContextualization(deser, prop, type); + } + return deser; + } + + /** + * Variant that will try to locate deserializer for current type, but without + * performing any contextualization (unlike {@link #findContextualValueDeserializer}) + * or checking for need to create a {@link TypeDeserializer} (unlike + * {@link #findRootValueDeserializer(JavaType)}. + * This method is usually called from within {@link ResolvableDeserializer#resolve}, + * and expectation is that caller then calls either + * {@link #handlePrimaryContextualization(JsonDeserializer, BeanProperty, JavaType)} or + * {@link #handleSecondaryContextualization(JsonDeserializer, BeanProperty, JavaType)} at a + * later point, as necessary. + * + * @since 2.5 + */ + public final JsonDeserializer findNonContextualValueDeserializer(JavaType type) + throws JsonMappingException + { + return _cache.findValueDeserializer(this, _factory, type); + } + + /** + * Method for finding a deserializer for root-level value. + */ + @SuppressWarnings("unchecked") + public final JsonDeserializer findRootValueDeserializer(JavaType type) + throws JsonMappingException + { + JsonDeserializer deser = _cache.findValueDeserializer(this, + _factory, type); + if (deser == null) { // can this occur? + return null; + } + deser = (JsonDeserializer) handleSecondaryContextualization(deser, null, type); + TypeDeserializer typeDeser = _factory.findTypeDeserializer(_config, type); + if (typeDeser != null) { + // important: contextualize to indicate this is for root value + typeDeser = typeDeser.forProperty(null); + return new TypeWrappedDeserializer(typeDeser, deser); + } + return deser; + } + + /** + * Convenience method, functionally same as: + *
+     *  getDeserializerProvider().findKeyDeserializer(getConfig(), prop.getType(), prop);
+     *
+ */ + public final KeyDeserializer findKeyDeserializer(JavaType keyType, + BeanProperty prop) throws JsonMappingException { + KeyDeserializer kd = _cache.findKeyDeserializer(this, + _factory, keyType); + // Second: contextualize? + if (kd instanceof ContextualKeyDeserializer) { + kd = ((ContextualKeyDeserializer) kd).createContextual(this, prop); + } + return kd; + } + + /* + /********************************************************** + /* Public API, ObjectId handling + /********************************************************** + */ + + /** + * Method called to find and return entry corresponding to given + * Object Id: will add an entry if necessary, and never returns null + */ + public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator generator, ObjectIdResolver resolver); + + @Deprecated // since 2.4 + public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator generator); + + /** + * Method called to ensure that every object id encounter during processing + * are resolved. + * + * @throws UnresolvedForwardReference + */ + public abstract void checkUnresolvedObjectId() + throws UnresolvedForwardReference; + + /* + /********************************************************** + /* Public API, type handling + /********************************************************** + */ + + /** + * Convenience method, functionally equivalent to: + *
+     *  getConfig().constructType(cls);
+     * 
+ */ + public final JavaType constructType(Class cls) { + return _config.constructType(cls); + } + + /** + * Helper method that is to be used when resolving basic class name into + * Class instance, the reason being that it may be necessary to work around + * various ClassLoader limitations, as well as to handle primitive type + * signatures. + * + * @since 2.6 + */ + public Class findClass(String className) throws ClassNotFoundException + { + // By default, delegate to ClassUtil: can be overridden with custom handling + return getTypeFactory().findClass(className); + } + + /* + /********************************************************** + /* Public API, helper object recycling + /********************************************************** + */ + + /** + * Method that can be used to get access to a reusable ObjectBuffer, + * useful for efficiently constructing Object arrays and Lists. + * Note that leased buffers should be returned once deserializer + * is done, to allow for reuse during same round of deserialization. + */ + public final ObjectBuffer leaseObjectBuffer() + { + ObjectBuffer buf = _objectBuffer; + if (buf == null) { + buf = new ObjectBuffer(); + } else { + _objectBuffer = null; + } + return buf; + } + + /** + * Method to call to return object buffer previously leased with + * {@link #leaseObjectBuffer}. + * + * @param buf Returned object buffer + */ + public final void returnObjectBuffer(ObjectBuffer buf) + { + /* Already have a reusable buffer? Let's retain bigger one + * (or if equal, favor newer one, shorter life-cycle) + */ + if (_objectBuffer == null + || buf.initialCapacity() >= _objectBuffer.initialCapacity()) { + _objectBuffer = buf; + } + } + + /** + * Method for accessing object useful for building arrays of + * primitive types (such as int[]). + */ + public final ArrayBuilders getArrayBuilders() + { + if (_arrayBuilders == null) { + _arrayBuilders = new ArrayBuilders(); + } + return _arrayBuilders; + } + + /* + /********************************************************** + /* Extended API: handler instantiation + /********************************************************** + */ + + public abstract JsonDeserializer deserializerInstance(Annotated annotated, + Object deserDef) + throws JsonMappingException; + + public abstract KeyDeserializer keyDeserializerInstance(Annotated annotated, + Object deserDef) + throws JsonMappingException; + + /* + /********************************************************** + /* Extended API: resolving contextual deserializers; called + /* by structured deserializers for their value/component + /* deserializers + /********************************************************** + */ + + /** + * Method called for primary property deserializers (ones + * directly created to deserialize values of a POJO property), + * to handle details of resolving + * {@link ContextualDeserializer} with given property context. + * + * @param prop Property for which the given primary deserializer is used; never null. + * + * @since 2.5 + */ + public JsonDeserializer handlePrimaryContextualization(JsonDeserializer deser, + BeanProperty prop, JavaType type) + throws JsonMappingException + { + if (deser instanceof ContextualDeserializer) { + _currentType = new LinkedNode(type, _currentType); + try { + deser = ((ContextualDeserializer) deser).createContextual(this, prop); + } finally { + _currentType = _currentType.next(); + } + } + return deser; + } + + /** + * Method called for secondary property deserializers (ones + * NOT directly created to deal with an annotatable POJO property, + * but instead created as a component -- such as value deserializers + * for structured types, or deserializers for root values) + * to handle details of resolving + * {@link ContextualDeserializer} with given property context. + * Given that these deserializers are not directly related to given property + * (or, in case of root value property, to any property), annotations + * accessible may or may not be relevant. + * + * @param prop Property for which deserializer is used, if any; null + * when deserializing root values + * + * @since 2.5 + */ + public JsonDeserializer handleSecondaryContextualization(JsonDeserializer deser, + BeanProperty prop, JavaType type) + throws JsonMappingException + { + if (deser instanceof ContextualDeserializer) { + _currentType = new LinkedNode(type, _currentType); + try { + deser = ((ContextualDeserializer) deser).createContextual(this, prop); + } finally { + _currentType = _currentType.next(); + } + } + return deser; + } + + @Deprecated // since 2.5; remove from 2.7 + public JsonDeserializer handlePrimaryContextualization(JsonDeserializer deser, BeanProperty prop) throws JsonMappingException { + return handlePrimaryContextualization(deser, prop, TypeFactory.unknownType()); + } + + @Deprecated // since 2.5; remove from 2.7 + public JsonDeserializer handleSecondaryContextualization(JsonDeserializer deser, BeanProperty prop) throws JsonMappingException { + if (deser instanceof ContextualDeserializer) { + deser = ((ContextualDeserializer) deser).createContextual(this, prop); + } + return deser; + } + + /* + /********************************************************** + /* Parsing methods that may use reusable/-cyclable objects + /********************************************************** + */ + + /** + * Convenience method for parsing a Date from given String, using + * currently configured date format (accessed using + * {@link DeserializationConfig#getDateFormat()}). + *

+ * Implementation will handle thread-safety issues related to + * date formats such that first time this method is called, + * date format is cloned, and cloned instance will be retained + * for use during this deserialization round. + */ + public Date parseDate(String dateStr) throws IllegalArgumentException + { + try { + DateFormat df = getDateFormat(); + return df.parse(dateStr); + } catch (ParseException e) { + throw new IllegalArgumentException(String.format( + "Failed to parse Date value '%s': %s", dateStr, e.getMessage())); + } + } + + /** + * Convenience method for constructing Calendar instance set + * to specified time, to be modified and used by caller. + */ + public Calendar constructCalendar(Date d) { + // 08-Jan-2008, tatu: not optimal, but should work for the most part; let's revise as needed. + Calendar c = Calendar.getInstance(getTimeZone()); + c.setTime(d); + return c; + } + + /* + /********************************************************** + /* Convenience methods for reading parsed values + /********************************************************** + */ + + /** + * Convenience method that may be used by composite or container deserializers, + * for reading one-off values contained (for sequences, it is more efficient + * to actually fetch deserializer once for the whole collection). + *

+ * NOTE: when deserializing values of properties contained in composite types, + * rather use {@link #readPropertyValue(JsonParser, BeanProperty, Class)}; + * this method does not allow use of contextual annotations. + * + * @since 2.4 + */ + public T readValue(JsonParser p, Class type) throws IOException { + return readValue(p, getTypeFactory().constructType(type)); + } + + /** + * @since 2.4 + */ + @SuppressWarnings("unchecked") + public T readValue(JsonParser p, JavaType type) throws IOException { + JsonDeserializer deser = findRootValueDeserializer(type); + if (deser == null) { + throw mappingException("Could not find JsonDeserializer for type %s", type); + } + return (T) deser.deserialize(p, this); + } + + /** + * Convenience method that may be used by composite or container deserializers, + * for reading one-off values for the composite type, taking into account + * annotations that the property (passed to this method -- usually property that + * has custom serializer that called this method) has. + * + * @since 2.4 + */ + public T readPropertyValue(JsonParser p, BeanProperty prop, Class type) throws IOException { + return readPropertyValue(p, prop, getTypeFactory().constructType(type)); + } + + /** + * @since 2.4 + */ + @SuppressWarnings("unchecked") + public T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type) throws IOException { + JsonDeserializer deser = findContextualValueDeserializer(type, prop); + if (deser == null) { + String propName = (prop == null) ? "NULL" : ("'"+prop.getName()+"'"); + throw mappingException("Could not find JsonDeserializer for type %s (via property %s)", + type, propName); + } + return (T) deser.deserialize(p, this); + } + + /* + /********************************************************** + /* Methods for problem handling, reporting + /********************************************************** + */ + + /** + * Method deserializers can call to inform configured {@link DeserializationProblemHandler}s + * of an unrecognized property. + * + * @return True if there was a configured problem handler that was able to handle the + * problem + */ + public boolean handleUnknownProperty(JsonParser p, JsonDeserializer deser, + Object instanceOrClass, String propName) + throws IOException, JsonProcessingException + { + LinkedNode h = _config.getProblemHandlers(); + if (h != null) { + while (h != null) { + // Can bail out if it's handled + if (h.value().handleUnknownProperty(this, p, deser, instanceOrClass, propName)) { + return true; + } + h = h.next(); + } + } + return false; + } + + /** + * Helper method for reporting a problem with unhandled unknown exception + * + * @param instanceOrClass Either value being populated (if one has been + * instantiated), or Class that indicates type that would be (or + * have been) instantiated + * @param deser Deserializer that had the problem, if called by deserializer + * (or on behalf of one) + */ + public void reportUnknownProperty(Object instanceOrClass, String fieldName, + JsonDeserializer deser) + throws JsonMappingException + { + if (!isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) { + return; + } + // Do we know properties that are expected instead? + Collection propIds = (deser == null) ? null : deser.getKnownPropertyNames(); + throw UnrecognizedPropertyException.from(_parser, + instanceOrClass, fieldName, propIds); + } + + /** + * @since 2.8 + */ + public JsonMappingException reportMappingException(String msg, Object... msgArgs) + throws JsonMappingException + { + if (msgArgs.length > 0) { + msg = String.format(msg, msgArgs); + } + throw mappingException(msg); + } + + /** + * @since 2.8 + */ + public JsonMappingException reportInstantiationException(Class instClass, Throwable t) + throws JsonMappingException + { + throw instantiationException(instClass, t); + } + + /** + * @since 2.8 + */ + public JsonMappingException reportInstantiationException(Class instClass, + String msg, Object... msgArgs) + throws JsonMappingException + { + if (msgArgs.length > 0) { + msg = String.format(msg, msgArgs); + } + throw instantiationException(instClass, msg); + } + + /** + * @since 2.8 + */ + public T reportWeirdStringException(String value, Class instClass, + String msg, Object... msgArgs) + throws JsonMappingException + { + if (msgArgs.length > 0) { + msg = String.format(msg, msgArgs); + } + throw weirdStringException(value, instClass, msg); + } + + /** + * @since 2.8 + */ + public T reportWeirdNumberException(Number value, Class instClass, + String msg, Object... msgArgs) + throws JsonMappingException + { + if (msgArgs.length > 0) { + msg = String.format(msg, msgArgs); + } + throw weirdNumberException(value, instClass, msg); + } + + /** + * @since 2.8 + */ + public T reportWeirdKeyException(Class keyClass, String keyValue, + String msg, Object... msgArgs) + throws JsonMappingException + { + if (msgArgs.length > 0) { + msg = String.format(msg, msgArgs); + } + throw weirdKeyException(keyClass, keyValue, msg); + } + + /** + * @since 2.8 + */ + public T reportWrongTokenException(JsonParser p, + JsonToken expToken, String msg, Object... msgArgs) + throws JsonMappingException + { + if (msgArgs.length > 0) { + msg = String.format(msg, msgArgs); + } + throw wrongTokenException(p, expToken, msg); + } + + /** + * @since 2.8 + */ + public T reportUnknownTypeException(JavaType type, String id, + String extraDesc) throws JsonMappingException + { + throw unknownTypeException(type, id, extraDesc); + } + + /** + * @since 2.8 + */ + public T reportEndOfInputException(Class instClass) throws JsonMappingException { + throw endOfInputException(instClass); + } + + /* + /********************************************************** + /* Methods for constructing exceptions + /********************************************************** + */ + + /** + * Helper method for constructing generic mapping exception for specified type + */ + public JsonMappingException mappingException(Class targetClass) { + return mappingException(targetClass, _parser.getCurrentToken()); + } + + public JsonMappingException mappingException(Class targetClass, JsonToken token) { + return JsonMappingException.from(_parser, + String.format("Can not deserialize instance of %s out of %s token", + _calcName(targetClass), token)); + } + + /** + * Helper method for constructing generic mapping exception with specified + * message and current location information + */ + public JsonMappingException mappingException(String message) { + return JsonMappingException.from(getParser(), message); + } + + /** + * Helper method for constructing generic mapping exception with specified + * message and current location information + * + * @since 2.6 + */ + public JsonMappingException mappingException(String msgTemplate, Object... args) { + String message = String.format(msgTemplate, args); + return JsonMappingException.from(getParser(), message); + } + + /** + * Helper method for constructing instantiation exception for specified type, + * to indicate problem with physically constructing instance of + * specified class (missing constructor, exception from constructor) + */ + public JsonMappingException instantiationException(Class instClass, Throwable t) { + return JsonMappingException.from(_parser, + String.format("Can not construct instance of %s, problem: %s", instClass.getName(), t.getMessage()), t); + } + + public JsonMappingException instantiationException(Class instClass, String msg) { + return JsonMappingException.from(_parser, + String.format("Can not construct instance of %s, problem: %s", instClass.getName(), msg)); + } + + /** + * Method that will construct an exception suitable for throwing when + * some String values are acceptable, but the one encountered is not. + * + * @param value String value from input being deserialized + * @param instClass Type that String should be deserialized into + * @param msg Message that describes specific problem + * + * @since 2.1 + */ + public JsonMappingException weirdStringException(String value, Class instClass, String msg) { + return InvalidFormatException.from(_parser, + String.format("Can not construct instance of %s from String value (%s): %s", + instClass.getName(), _quotedString(value), msg), + value, instClass); + } + + /** + * Helper method for constructing exception to indicate that input JSON + * Number was not suitable for deserializing into given target type. + */ + public JsonMappingException weirdNumberException(Number value, Class instClass, String msg) { + return InvalidFormatException.from(_parser, + String.format("Can not construct instance of %s from number value (%s): %s", + instClass.getName(), String.valueOf(value), msg), + value, instClass); + } + + /** + * Helper method for constructing exception to indicate that given JSON + * Object field name was not in format to be able to deserialize specified + * key type. + */ + public JsonMappingException weirdKeyException(Class keyClass, String keyValue, String msg) { + return InvalidFormatException.from(_parser, + String.format("Can not construct Map key of type %s from String (%s): %s", + keyClass.getName(), _quotedString(keyValue), msg), + keyValue, keyClass); + } + + /** + * Helper method for indicating that the current token was expected to be another + * token. + */ + public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken, String msg0) { + String msg = String.format("Unexpected token (%s), expected %s", + p.getCurrentToken(), expToken); + if (msg0 != null) { + msg = msg + ": "+msg0; + } + return JsonMappingException.from(p, msg); + } + + /** + * Helper method for constructing exception to indicate that given + * type id (parsed from JSON) could not be converted to a Java type. + */ + @Deprecated // since 2.5, use overloaded variant + public JsonMappingException unknownTypeException(JavaType type, String id) { + return JsonMappingException.from(_parser, "Could not resolve type id '"+id+"' into a subtype of "+type); + } + + /** + * @since 2.5 + */ + public JsonMappingException unknownTypeException(JavaType type, String id, + String extraDesc) { + String msg = String.format("Could not resolve type id '%s' into a subtype of %s", + id, type); + if (extraDesc != null) { + msg = msg + ": "+extraDesc; + } + return JsonMappingException.from(_parser, msg); + } + + public JsonMappingException endOfInputException(Class instClass) { + return JsonMappingException.from(_parser, "Unexpected end-of-input when trying to deserialize a " + +instClass.getName()); + } + + /* + /********************************************************** + /* Overridable internal methods + /********************************************************** + */ + + protected DateFormat getDateFormat() + { + if (_dateFormat != null) { + return _dateFormat; + } + /* 24-Feb-2012, tatu: At this point, all timezone configuration + * should have occurred, with respect to default dateformat + * and timezone configuration. But we still better clone + * an instance as formatters may be stateful. + */ + DateFormat df = _config.getDateFormat(); + _dateFormat = df = (DateFormat) df.clone(); + return df; + } + + protected String determineClassName(Object instance) { + return ClassUtil.getClassDescription(instance); + } + + /* + /********************************************************** + /* Other internal methods + /********************************************************** + */ + + protected String _calcName(Class cls) { + if (cls.isArray()) { + return _calcName(cls.getComponentType())+"[]"; + } + return cls.getName(); + } + + protected String _valueDesc() { + try { + return _desc(_parser.getText()); + } catch (Exception e) { + return "[N/A]"; + } + } + + protected String _desc(String desc) { + if (desc == null) { + return "[N/A]"; + } + // !!! should we quote it? (in case there are control chars, linefeeds) + if (desc.length() > MAX_ERROR_STR_LEN) { + desc = desc.substring(0, MAX_ERROR_STR_LEN) + "]...[" + desc.substring(desc.length() - MAX_ERROR_STR_LEN); + } + return desc; + } + + // @since 2.7 + protected String _quotedString(String desc) { + if (desc == null) { + return "[N/A]"; + } + // !!! should we quote it? (in case there are control chars, linefeeds) + if (desc.length() > MAX_ERROR_STR_LEN) { + return String.format("\"%s]...[%s\"", + desc.substring(0, MAX_ERROR_STR_LEN), + desc.substring(desc.length() - MAX_ERROR_STR_LEN)); + } + return "\"" + desc + "\""; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationFeature.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationFeature.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/DeserializationFeature.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,425 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.databind.cfg.ConfigFeature; + +/** + * Enumeration that defines simple on/off features that affect + * the way Java objects are deserialized from JSON + *

+ * Note that features can be set both through + * {@link ObjectMapper} (as sort of defaults) and through + * {@link ObjectReader}. + * In first case these defaults must follow "config-then-use" patterns + * (i.e. defined once, not changed afterwards); all per-call + * changes must be done using {@link ObjectReader}. + *

+ * Note that features that do not indicate version of inclusion + * were available in Jackson 2.0 (or earlier); only later additions + * indicate version of inclusion. + */ +public enum DeserializationFeature implements ConfigFeature +{ + /* + /****************************************************** + /* Type conversion features + /****************************************************** + */ + + /** + * Feature that determines whether JSON floating point numbers + * are to be deserialized into {@link java.math.BigDecimal}s + * if only generic type description (either {@link Object} or + * {@link Number}, or within untyped {@link java.util.Map} + * or {@link java.util.Collection} context) is available. + * If enabled such values will be deserialized as {@link java.math.BigDecimal}s; + * if disabled, will be deserialized as {@link Double}s. + *

+ * Feature is disabled by default, meaning that "untyped" floating + * point numbers will by default be deserialized as {@link Double}s + * (choice is for performance reason -- BigDecimals are slower than + * Doubles). + */ + USE_BIG_DECIMAL_FOR_FLOATS(false), + + /** + * Feature that determines whether JSON integral (non-floating-point) + * numbers are to be deserialized into {@link java.math.BigInteger}s + * if only generic type description (either {@link Object} or + * {@link Number}, or within untyped {@link java.util.Map} + * or {@link java.util.Collection} context) is available. + * If enabled such values will be deserialized as + * {@link java.math.BigInteger}s; + * if disabled, will be deserialized as "smallest" available type, + * which is either {@link Integer}, {@link Long} or + * {@link java.math.BigInteger}, depending on number of digits. + *

+ * Feature is disabled by default, meaning that "untyped" integral + * numbers will by default be deserialized using whatever + * is the most compact integral type, to optimize efficiency. + */ + USE_BIG_INTEGER_FOR_INTS(false), + + /** + * Feature that determines how "small" JSON integral (non-floating-point) + * numbers -- ones that fit in 32-bit signed integer (`int`) -- are bound + * when target type is loosely typed as {@link Object} or {@link Number} + * (or within untyped {@link java.util.Map} or {@link java.util.Collection} context). + * If enabled, such values will be deserialized as {@link java.lang.Long}; + * if disabled, they will be deserialized as "smallest" available type, + * {@link Integer}. + * In addition, if enabled, trying to bind values that do not fit in {@link java.lang.Long} + * will throw a {@link com.fasterxml.jackson.core.JsonProcessingException}. + *

+ * Note: if {@link #USE_BIG_INTEGER_FOR_INTS} is enabled, it has precedence + * over this setting, forcing use of {@link java.math.BigInteger} for all + * integral values. + *

+ * Feature is disabled by default, meaning that "untyped" integral + * numbers will by default be deserialized using {@link java.lang.Integer} + * if value fits. + * + * @since 2.6 + */ + USE_LONG_FOR_INTS(false), + + /** + * Feature that determines whether JSON Array is mapped to + * Object[] or List<Object> when binding + * "untyped" objects (ones with nominal type of java.lang.Object). + * If true, binds as Object[]; if false, as List<Object>. + *

+ * Feature is disabled by default, meaning that JSON arrays are bound as + * {@link java.util.List}s. + */ + USE_JAVA_ARRAY_FOR_JSON_ARRAY(false), + + /** + * Feature that determines standard deserialization mechanism used for + * Enum values: if enabled, Enums are assumed to have been serialized using + * return value of Enum.toString(); + * if disabled, return value of Enum.name() is assumed to have been used. + *

+ * Note: this feature should usually have same value + * as {@link SerializationFeature#WRITE_ENUMS_USING_TO_STRING}. + *

+ * Feature is disabled by default. + */ + READ_ENUMS_USING_TO_STRING(false), + + /* + /****************************************************** + * Error handling features + /****************************************************** + */ + + /** + * Feature that determines whether encountering of unknown + * properties (ones that do not map to a property, and there is + * no "any setter" or handler that can handle it) + * should result in a failure (by throwing a + * {@link JsonMappingException}) or not. + * This setting only takes effect after all other handling + * methods for unknown properties have been tried, and + * property remains unhandled. + *

+ * Feature is enabled by default (meaning that a + * {@link JsonMappingException} will be thrown if an unknown property + * is encountered). + */ + FAIL_ON_UNKNOWN_PROPERTIES(true), + + /** + * Feature that determines whether encountering of JSON null + * is an error when deserializing into Java primitive types + * (like 'int' or 'double'). If it is, a JsonProcessingException + * is thrown to indicate this; if not, default value is used + * (0 for 'int', 0.0 for double, same defaulting as what JVM uses). + *

+ * Feature is disabled by default. + */ + FAIL_ON_NULL_FOR_PRIMITIVES(false), + + /** + * Feature that determines whether JSON integer numbers are valid + * values to be used for deserializing Java enum values. + * If set to 'false' numbers are acceptable and are used to map to + * ordinal() of matching enumeration value; if 'true', numbers are + * not allowed and a {@link JsonMappingException} will be thrown. + * Latter behavior makes sense if there is concern that accidental + * mapping from integer values to enums might happen (and when enums + * are always serialized as JSON Strings) + *

+ * Feature is disabled by default. + */ + FAIL_ON_NUMBERS_FOR_ENUMS(false), + + /** + * Feature that determines what happens when type of a polymorphic + * value (indicated for example by {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) + * can not be found (missing) or resolved (invalid class name, unmappable id); + * if enabled, an exception ir thrown; if false, null value is used instead. + *

+ * Feature is enabled by default so that exception is thrown for missing or invalid + * type information. + * + * @since 2.2 + */ + FAIL_ON_INVALID_SUBTYPE(true), + + /** + * Feature that determines what happens when reading JSON content into tree + * ({@link com.fasterxml.jackson.core.TreeNode}) and a duplicate key + * is encountered (property name that was already seen for the JSON Object). + * If enabled, {@link JsonMappingException} will be thrown; if disabled, no exception + * is thrown and the new (later) value overwrites the earlier value. + *

+ * Note that this property does NOT affect other aspects of data-binding; that is, + * no detection is done with respect to POJO properties or {@link java.util.Map} + * keys. New features may be added to control additional cases. + *

+ * Feature is disabled by default so that no exception is thrown. + * + * @since 2.3 + */ + FAIL_ON_READING_DUP_TREE_KEY(false), + + /** + * Feature that determines what happens when a property that has been explicitly + * marked as ignorable is encountered in input: if feature is enabled, + * {@link JsonMappingException} is thrown; if false, property is quietly skipped. + *

+ * Feature is disabled by default so that no exception is thrown. + * + * @since 2.3 + */ + FAIL_ON_IGNORED_PROPERTIES(false), + + /** + * Feature that determines what happens if an Object Id reference is encountered + * that does not refer to an actual Object with that id ("unresolved Object Id"): + * either an exception is thrown (true), or a null object is used + * instead (false). + * Note that if this is set to false, no further processing is done; + * specifically, if reference is defined via setter method, that method will NOT + * be called. + *

+ * Feature is enabled by default, so that unknown Object Ids will result in an + * exception being thrown, at the end of deserialization. + * + * @since 2.5 + */ + FAIL_ON_UNRESOLVED_OBJECT_IDS(true), + + /** + * Feature that determines what happens if one or more Creator properties (properties + * bound to parameters of Creator method (constructor or static factory method)) + * are missing value to bind to from content. + * If enabled, such missing values result in a {@link JsonMappingException} being + * thrown with information on the first one (by index) of missing properties. + * If disabled, and if property is NOT marked as required, + * missing Creator properties are filled + * with null values provided by deserializer for the type of parameter + * (usually null for Object types, and default value for primitives; but redefinable + * via custom deserializers). + *

+ * Note that having an injectable value counts as "not missing". + *

+ * Feature is disabled by default, so that no exception is thrown for missing creator + * property values, unless they are explicitly marked as `required`. + * + * @since 2.6 + */ + FAIL_ON_MISSING_CREATOR_PROPERTIES(false), + + /** + * Feature that determines whether Jackson code should catch + * and wrap {@link Exception}s (but never {@link Error}s!) + * to add additional information about + * location (within input) of problem or not. If enabled, + * most exceptions will be caught and re-thrown (exception + * specifically being that {@link java.io.IOException}s may be passed + * as is, since they are declared as throwable); this can be + * convenient both in that all exceptions will be checked and + * declared, and so there is more contextual information. + * However, sometimes calling application may just want "raw" + * unchecked exceptions passed as is. + *

+ * Feature is enabled by default. + */ + WRAP_EXCEPTIONS(true), + + /* + /****************************************************** + /* Structural conversion features + /****************************************************** + */ + + /** + * Feature that determines whether it is acceptable to coerce non-array + * (in JSON) values to work with Java collection (arrays, java.util.Collection) + * types. If enabled, collection deserializers will try to handle non-array + * values as if they had "implicit" surrounding JSON array. + * This feature is meant to be used for compatibility/interoperability reasons, + * to work with packages (such as XML-to-JSON converters) that leave out JSON + * array in cases where there is just a single element in array. + *

+ * Feature is disabled by default. + */ + ACCEPT_SINGLE_VALUE_AS_ARRAY(false), + + /** + * Feature that determines whether it is acceptable to coerce single value array (in JSON) + * values to the corresponding value type. This is basically the opposite of the {@link #ACCEPT_SINGLE_VALUE_AS_ARRAY} + * feature. If more than one value is found in the array, a JsonMappingException is thrown. + *

+ * + * Feature is disabled by default + * @since 2.4 + */ + UNWRAP_SINGLE_VALUE_ARRAYS(false), + + /** + * Feature to allow "unwrapping" root-level JSON value, to match setting of + * {@link SerializationFeature#WRAP_ROOT_VALUE} used for serialization. + * Will verify that the root JSON value is a JSON Object, and that it has + * a single property with expected root name. If not, a + * {@link JsonMappingException} is thrown; otherwise value of the wrapped property + * will be deserialized as if it was the root value. + *

+ * Feature is disabled by default. + */ + UNWRAP_ROOT_VALUE(false), + + /* + /****************************************************** + /* Value conversion features + /****************************************************** + */ + + /** + * Feature that can be enabled to allow JSON empty String + * value ("") to be bound to POJOs as null. + * If disabled, standard POJOs can only be bound from JSON null or + * JSON Object (standard meaning that no custom deserializers or + * constructors are defined; both of which can add support for other + * kinds of JSON values); if enabled, empty JSON String can be taken + * to be equivalent of JSON null. + *

+ * Feature is disabled by default. + */ + ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false), + + /** + * Feature that can be enabled to allow empty JSON Array + * value (that is, [ ]) to be bound to POJOs as null. + * If disabled, standard POJOs can only be bound from JSON null or + * JSON Object (standard meaning that no custom deserializers or + * constructors are defined; both of which can add support for other + * kinds of JSON values); if enabled, empty JSON Array will be taken + * to be equivalent of JSON null. + *

+ * Feature is disabled by default. + * + * @since 2.5 + */ + ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT(false), + + /** + * Feature that determines whether coercion from JSON floating point + * number (anything with command (`.`) or exponent portion (`e` / `E')) + * to an expected integral number (`int`, `long`, `java.lang.Integer`, `java.lang.Long`, + * `java.math.BigDecimal`) is allowed or not. + * If enabled, coercion truncates value; if disabled, a {@link JsonMappingException} + * will be thrown. + *

+ * Feature is enabled by default. + * + * @since 2.6 + */ + ACCEPT_FLOAT_AS_INT(true), + + /** + * Feature that allows unknown Enum values to be parsed as null values. + * If disabled, unknown Enum values will throw exceptions. + *

+ * Note that in some cases this will basically ignore unknown Enum values; + * this is the keys for keys of {@link java.util.EnumMap} and values + * of {@link java.util.EnumSet} (because nulls are not accepted in these + * cases). + *

+ * Feature is disabled by default. + * + * @since 2.0 + */ + READ_UNKNOWN_ENUM_VALUES_AS_NULL(false), + + /** + * Feature that controls whether numeric timestamp values are expected + * to be written using nanosecond timestamps (enabled) or not (disabled), + * if and only if datatype supports such resolution. + * Only newer datatypes (such as Java8 Date/Time) support such resolution -- + * older types (pre-Java8 java.util.Date etc) and Joda do not -- + * and this setting has no effect on such types. + *

+ * If disabled, standard millisecond timestamps are assumed. + * This is the counterpart to {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}. + *

+ * Feature is enabled by default, to support most accurate time values possible. + * + * @since 2.2 + */ + READ_DATE_TIMESTAMPS_AS_NANOSECONDS(true), + + /** + * Feature that specifies whether context provided {@link java.util.TimeZone} + * ({@link DeserializationContext#getTimeZone()} should be used to adjust Date/Time + * values on deserialization, even if value itself contains timezone information. + * If enabled, contextual TimeZone will essentially override any other + * TimeZone information; if disabled, it will only be used if value itself does not + * contain any TimeZone information. + * + * @since 2.2 + */ + ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true), + + /* + /****************************************************** + /* Other + /****************************************************** + */ + + /** + * Feature that determines whether {@link ObjectReader} should + * try to eagerly fetch necessary {@link JsonDeserializer} when + * possible. This improves performance in cases where similarly + * configured {@link ObjectReader} instance is used multiple + * times; and should not significantly affect single-use cases. + *

+ * Note that there should not be any need to normally disable this + * feature: only consider that if there are actual perceived problems. + *

+ * Feature is enabled by default. + * + * @since 2.1 + */ + EAGER_DESERIALIZER_FETCH(true) + + ; + + private final boolean _defaultState; + private final int _mask; + + private DeserializationFeature(boolean defaultState) { + _defaultState = defaultState; + _mask = (1 << ordinal()); + } + + @Override + public boolean enabledByDefault() { return _defaultState; } + + @Override + public int getMask() { return _mask; } + + @Override + public boolean enabledIn(int flags) { return (flags & _mask) != 0; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/InjectableValues.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/InjectableValues.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/InjectableValues.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,80 @@ +package com.fasterxml.jackson.databind; + +import java.util.*; + +/** + * Abstract class that defines API for objects that provide value to + * "inject" during deserialization. An instance of this object + */ +public abstract class InjectableValues +{ + /** + * Method called to find value identified by id valueId to + * inject as value of specified property during deserialization, passing + * POJO instance in which value will be injected if it is available + * (will be available when injected via field or setter; not available + * when injected via constructor or factory method argument). + * + * @param valueId Object that identifies value to inject; may be a simple + * name or more complex identifier object, whatever provider needs + * @param ctxt Deserialization context + * @param forProperty Bean property in which value is to be injected + * @param beanInstance Bean instance that contains property to inject, + * if available; null if bean has not yet been constructed. + */ + public abstract Object findInjectableValue(Object valueId, DeserializationContext ctxt, + BeanProperty forProperty, Object beanInstance); + + /* + /********************************************************** + /* Standard implementation + /********************************************************** + */ + + /** + * Simple standard implementation which uses a simple Map to + * store values to inject, identified by simple String keys. + */ + public static class Std + extends InjectableValues + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + protected final Map _values; + + public Std() { + this(new HashMap()); + } + + public Std(Map values) { + _values = values; + } + + public Std addValue(String key, Object value) { + _values.put(key, value); + return this; + } + + public Std addValue(Class classKey, Object value) { + _values.put(classKey.getName(), value); + return this; + } + + @Override + public Object findInjectableValue(Object valueId, DeserializationContext ctxt, + BeanProperty forProperty, Object beanInstance) + { + if (!(valueId instanceof String)) { + String type = (valueId == null) ? "[null]" : valueId.getClass().getName(); + throw new IllegalArgumentException("Unrecognized inject value id type ("+type+"), expecting String"); + } + String key = (String) valueId; + Object ob = _values.get(key); + if (ob == null && !_values.containsKey(key)) { + throw new IllegalArgumentException("No injectable id with value '"+key+"' found (for property '"+forProperty.getName()+"')"); + } + return ob; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JavaType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JavaType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JavaType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,520 @@ +package com.fasterxml.jackson.databind; + +import java.lang.reflect.Modifier; +import java.util.List; + +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.databind.type.TypeBindings; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Base class for type token classes used both to contain information + * and as keys for deserializers. + *

+ * Instances can (only) be constructed by + * com.fasterxml.jackson.databind.type.TypeFactory. + *

+ * Since 2.2 this implements {@link java.lang.reflect.Type} to allow + * it to be pushed through interfaces that only expose that type. + */ +public abstract class JavaType + extends ResolvedType + implements java.io.Serializable, // 2.1 + java.lang.reflect.Type // 2.2 +{ + private static final long serialVersionUID = 1; + + /** + * This is the nominal type-erased Class that would be close to the + * type represented (but not exactly type, due to type erasure: type + * instance may have more information on this). + * May be an interface or abstract class, so instantiation + * may not be possible. + */ + protected final Class _class; + + protected final int _hash; + + /** + * Optional handler (codec) that can be attached to indicate + * what to use for handling (serializing, deserializing) values of + * this specific type. + *

+ * Note: untyped (i.e. caller has to cast) because it is used for + * different kinds of handlers, with unrelated types. + */ + protected final Object _valueHandler; + + /** + * Optional handler that can be attached to indicate how to handle + * additional type metadata associated with this type. + *

+ * Note: untyped (i.e. caller has to cast) because it is used for + * different kinds of handlers, with unrelated types. + */ + protected final Object _typeHandler; + + /** + * Whether entities defined with this type should be handled using + * static typing (as opposed to dynamic runtime type) or not. + * + * @since 2.2 + */ + protected final boolean _asStatic; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * @param raw "Raw" (type-erased) class for this type + * @param additionalHash Additional hash code to use, in addition + * to hash code of the class name + */ + protected JavaType(Class raw, int additionalHash, + Object valueHandler, Object typeHandler, boolean asStatic) + { + _class = raw; + _hash = raw.getName().hashCode() + additionalHash; + _valueHandler = valueHandler; + _typeHandler = typeHandler; + _asStatic = asStatic; + } + + /** + * Copy-constructor used when refining/upgrading type instances. + * + * @since 2.7 + */ + protected JavaType(JavaType base) + { + _class = base._class; + _hash = base._hash; + _valueHandler = base._valueHandler; + _typeHandler = base._typeHandler; + _asStatic = base._asStatic; + } + + /** + * "Copy method" that will construct a new instance that is identical to + * this instance, except that it will have specified type handler assigned. + * + * @return Newly created type instance + */ + public abstract JavaType withTypeHandler(Object h); + + /** + * Mutant factory method that will construct a new instance that is identical to + * this instance, except that it will have specified content type (element type + * for arrays, value type for Maps and so forth) handler assigned. + * + * @return Newly created type instance, with given + */ + public abstract JavaType withContentTypeHandler(Object h); + + /** + * Mutant factory method that will construct a new instance that is identical to + * this instance, except that it will have specified value handler assigned. + * + * @return Newly created type instance + */ + public abstract JavaType withValueHandler(Object h); + + /** + * Mutant factory method that will construct a new instance that is identical to + * this instance, except that it will have specified content value handler assigned. + * + * @return Newly created type instance + */ + public abstract JavaType withContentValueHandler(Object h); + + /** + * Mutant factory method that may be called on structured types + * that have a so-called content type (element of arrays, value type + * of Maps, referenced type of referential types), + * and will construct a new instance that is identical to + * this instance, except that it has specified content type, instead of current + * one. If content type is already set to given type, this is returned. + * If type does not have a content type (which is the case with + * SimpleType), {@link IllegalArgumentException} + * will be thrown. + * + * @return Newly created type instance + * + * @since 2.7 + */ + public abstract JavaType withContentType(JavaType contentType); + + /** + * Method that can be called to get a type instance that indicates + * that values of the type should be handled using "static typing" for purposes + * of serialization (as opposed to "dynamic" aka runtime typing): + * meaning that no runtime information is needed for determining serializers to use. + * The main use case is to allow forcing of specific root value serialization type, + * and specifically in resolving serializers for contained types (element types + * for arrays, Collections and Maps). + * + * @since 2.2 + */ + public abstract JavaType withStaticTyping(); + + /* + /********************************************************** + /* Type coercion fluent factory methods + /********************************************************** + */ + + /** + * Mutant factory method that will try to create and return a sub-type instance + * for known parameterized types; for other types will return `null` to indicate + * that no just refinement makes necessary sense, without trying to detect + * special status through implemented interfaces. + * + * @since 2.7 + */ + public abstract JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces); + + /** + * Legacy method used for forcing sub-typing of this type into + * type specified by specific type erasure. + * Deprecated as of 2.7 as such specializations really ought to + * go through {@link TypeFactory}, not directly via {@link JavaType}. + * + * @since 2.7 + */ + @Deprecated + public JavaType forcedNarrowBy(Class subclass) + { + if (subclass == _class) { // can still optimize for simple case + return this; + } + JavaType result = _narrow(subclass); + // TODO: these checks should NOT actually be needed; above should suffice: + if (_valueHandler != result.getValueHandler()) { + result = result.withValueHandler(_valueHandler); + } + if (_typeHandler != result.getTypeHandler()) { + result = result.withTypeHandler(_typeHandler); + } + return result; + } + + @Deprecated // since 2.7 + protected abstract JavaType _narrow(Class subclass); + + /* + /********************************************************** + /* Implementation of ResolvedType API + /********************************************************** + */ + + @Override + public final Class getRawClass() { return _class; } + + /** + * Method that can be used to check whether this type has + * specified Class as its type erasure. Put another way, returns + * true if instantiation of this Type is given (type-erased) Class. + */ + @Override + public final boolean hasRawClass(Class clz) { return _class == clz; } + + /** + * @since 2.6 + */ + public final boolean isTypeOrSubTypeOf(Class clz) { + return (_class == clz) || (clz.isAssignableFrom(_class)); + } + + @Override + public boolean isAbstract() { + return Modifier.isAbstract(_class.getModifiers()); + } + + /** + * Convenience method for checking whether underlying Java type + * is a concrete class or not: abstract classes and interfaces + * are not. + */ + @Override + public boolean isConcrete() { + int mod = _class.getModifiers(); + if ((mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0) { + return true; + } + /* 19-Feb-2010, tatus: Holy mackarel; primitive types + * have 'abstract' flag set... + */ + return _class.isPrimitive(); + } + + @Override + public boolean isThrowable() { return Throwable.class.isAssignableFrom(_class); } + + @Override + public boolean isArrayType() { return false; } + + @Override + public final boolean isEnumType() { return _class.isEnum(); } + + @Override + public final boolean isInterface() { return _class.isInterface(); } + + @Override + public final boolean isPrimitive() { return _class.isPrimitive(); } + + @Override + public final boolean isFinal() { return Modifier.isFinal(_class.getModifiers()); } + + /** + * @return True if type represented is a container type; this includes + * array, Map and Collection types. + */ + @Override + public abstract boolean isContainerType(); + + /** + * @return True if type is either true {@link java.util.Collection} type, + * or something similar (meaning it has at least one type parameter, + * which describes type of contents) + */ + @Override + public boolean isCollectionLikeType() { return false; } + + /** + * @return True if type is either true {@link java.util.Map} type, + * or something similar (meaning it has at least two type parameter; + * first one describing key type, second value type) + */ + @Override + public boolean isMapLikeType() { return false; } + + /** + * Convenience method, short-hand for + * + * getRawClass() == Object.class + * + * and used to figure if we basically have "untyped" type object. + * + * @since 2.5 + */ + public final boolean isJavaLangObject() { return _class == Object.class; } + + /** + * Accessor for checking whether handlers for dealing with values of + * this type should use static typing (as opposed to dynamic typing). + * Note that while value of 'true' does mean that static typing is to + * be used, value of 'false' may still be overridden by other settings. + * + * @since 2.2 + */ + public final boolean useStaticType() { return _asStatic; } + + /* + /********************************************************** + /* Public API, type parameter access; pass-through + /********************************************************** + */ + + @Override + public boolean hasGenericTypes() { return containedTypeCount() > 0; } + + @Override + public JavaType getKeyType() { return null; } + + @Override + public JavaType getContentType() { return null; } + + @Override // since 2.6 + public JavaType getReferencedType() { return null; } + + @Override + public abstract int containedTypeCount(); + + @Override + public abstract JavaType containedType(int index); + + @Deprecated // since 2.7 + @Override + public abstract String containedTypeName(int index); + + @Deprecated // since 2.7 + @Override + public Class getParameterSource() { + return null; + } + + /* + /********************************************************** + /* Extended API beyond ResolvedType + /********************************************************** + */ + + // NOTE: not defined in Resolved type + /** + * Convenience method that is functionally same as: + * + * JavaType t = containedType(index); + * if (t == null) { + * t = TypeFactory.unknownType(); + * } + * + * and typically used to eliminate need for null checks for common case + * where we just want to check if containedType is available first; and + * if not, use "unknown type" (which translates to java.lang.Object + * basically). + * + * @since 2.5 + */ + public JavaType containedTypeOrUnknown(int index) { + JavaType t = containedType(index); + return (t == null) ? TypeFactory.unknownType() : t; + } + + /** + * @since 2.7 + */ + public abstract TypeBindings getBindings(); + + /** + * Method that may be called to find representation of given type + * within type hierarchy of this type: either this type (if this + * type has given erased type), one of its supertypes that has the + * erased types, or null if target is neither this type or any of its + * supertypes. + * + * @since 2.7 + */ + public abstract JavaType findSuperType(Class erasedTarget); + + /** + * Accessor for finding fully resolved parent class of this type, + * if it has one; null if not. + * + * @since 2.7 + */ + public abstract JavaType getSuperClass(); + + /** + * Accessor for finding fully resolved interfaces this type implements, + * if any; empty array if none. + * + * @since 2.7 + */ + public abstract List getInterfaces(); + + /** + * Method that may be used to find paramaterization this type has for + * given type-erased generic target type. + * + * @since 2.7 + */ + public abstract JavaType[] findTypeParameters(Class expType); + + /* + /********************************************************** + /* Semi-public API, accessing handlers + /********************************************************** + */ + + /** + * Method for accessing value handler associated with this type, if any + */ + @SuppressWarnings("unchecked") + public T getValueHandler() { return (T) _valueHandler; } + + /** + * Method for accessing type handler associated with this type, if any + */ + @SuppressWarnings("unchecked") + public T getTypeHandler() { return (T) _typeHandler; } + + /** + * @since 2.7 + */ + public Object getContentValueHandler() { return null; } + + /** + * @since 2.7 + */ + public Object getContentTypeHandler() { return null; } + + /** + * @since 2.6 + */ + public boolean hasValueHandler() { return _valueHandler != null; } + + /* + /********************************************************** + /* Support for producing signatures + /********************************************************** + */ + + //public abstract String toCanonical(); + + /** + * Method for accessing signature that contains generic + * type information, in form compatible with JVM 1.5 + * as per JLS. It is a superset of {@link #getErasedSignature}, + * in that generic information can be automatically removed + * if necessary (just remove outermost + * angle brackets along with content inside) + */ + public String getGenericSignature() { + StringBuilder sb = new StringBuilder(40); + getGenericSignature(sb); + return sb.toString(); + } + + /** + * + * @param sb StringBuilder to append signature to + * + * @return StringBuilder that was passed in; returned to allow + * call chaining + */ + public abstract StringBuilder getGenericSignature(StringBuilder sb); + + /** + * Method for accessing signature without generic + * type information, in form compatible with all versions + * of JVM, and specifically used for type descriptions + * when generating byte code. + */ + public String getErasedSignature() { + StringBuilder sb = new StringBuilder(40); + getErasedSignature(sb); + return sb.toString(); + } + + /** + * Method for accessing signature without generic + * type information, in form compatible with all versions + * of JVM, and specifically used for type descriptions + * when generating byte code. + * + * @param sb StringBuilder to append signature to + * + * @return StringBuilder that was passed in; returned to allow + * call chaining + */ + public abstract StringBuilder getErasedSignature(StringBuilder sb); + + /* + /********************************************************** + /* Standard methods; let's make them abstract to force override + /********************************************************** + */ + + @Override + public abstract String toString(); + + @Override + public abstract boolean equals(Object o); + + @Override + public final int hashCode() { return _hash; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,365 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Abstract class that defines API used by {@link ObjectMapper} (and + * other chained {@link JsonDeserializer}s too) to deserialize Objects of + * arbitrary types from JSON, using provided {@link JsonParser}. + *

+ * Custom deserializers should usually not directly extend this class, + * but instead extend {@link com.fasterxml.jackson.databind.deser.std.StdDeserializer} + * (or its subtypes like {@link com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer}). + *

+ * If deserializer is an aggregate one -- meaning it delegates handling of some + * of its contents by using other deserializer(s) -- it typically also needs + * to implement {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer}, + * which can locate dependant deserializers. This is important to allow dynamic + * overrides of deserializers; separate call interface is needed to separate + * resolution of dependant deserializers (which may have cyclic link back + * to deserializer itself, directly or indirectly). + *

+ * In addition, to support per-property annotations (to configure aspects + * of deserialization on per-property basis), deserializers may want + * to implement + * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}, + * which allows specialization of deserializers: call to + * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual} + * is passed information on property, and can create a newly configured + * deserializer for handling that particular property. + *

+ * If both + * {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer} and + * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer} + * are implemented, resolution of deserializers occurs before + * contextualization. + */ +public abstract class JsonDeserializer +{ + /* + /********************************************************** + /* Main deserialization methods + /********************************************************** + */ + + /** + * Method that can be called to ask implementation to deserialize + * JSON content into the value type this serializer handles. + * Returned instance is to be constructed by method itself. + *

+ * Pre-condition for this method is that the parser points to the + * first event that is part of value to deserializer (and which + * is never JSON 'null' literal, more on this below): for simple + * types it may be the only value; and for structured types the + * Object start marker or a FIELD_NAME. + *

+ *

+ * The two possible input conditions for structured types result + * from polymorphism via fields. In the ordinary case, Jackson + * calls this method when it has encountered an OBJECT_START, + * and the method implementation must advance to the next token to + * see the first field name. If the application configures + * polymorphism via a field, then the object looks like the following. + *

+     *      {
+     *          "@class": "class name",
+     *          ...
+     *      }
+     *  
+ * Jackson consumes the two tokens (the @class field name + * and its value) in order to learn the class and select the deserializer. + * Thus, the stream is pointing to the FIELD_NAME for the first field + * after the @class. Thus, if you want your method to work correctly + * both with and without polymorphism, you must begin your method with: + *
+     *       if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
+     *         jp.nextToken();
+     *       }
+     *  
+ * This results in the stream pointing to the field name, so that + * the two conditions align. + *

+ * Post-condition is that the parser will point to the last + * event that is part of deserialized value (or in case deserialization + * fails, event that was not recognized or usable, which may be + * the same event as the one it pointed to upon call). + *

+ * Note that this method is never called for JSON null literal, + * and thus deserializers need (and should) not check for it. + * + * @param p Parsed used for reading JSON content + * @param ctxt Context that can be used to access information about + * this deserialization activity. + * + * @return Deserialized value + */ + public abstract T deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException; + + /** + * Alternate deserialization method (compared to the most commonly + * used, {@link #deserialize(JsonParser, DeserializationContext)}), + * which takes in initialized value instance, to be + * configured and/or populated by deserializer. + * Method is not necessarily used (or supported) by all types + * (it will not work for immutable types, for obvious reasons): + * most commonly it is used for Collections and Maps. + * It may be used both with "updating readers" (for POJOs) and + * when Collections and Maps use "getter as setter". + *

+ * Default implementation just throws + * {@link UnsupportedOperationException}, to indicate that types + * that do not explicitly add support do not necessarily support + * update-existing-value operation (esp. immutable types) + */ + public T deserialize(JsonParser p, DeserializationContext ctxt, T intoValue) + throws IOException, JsonProcessingException + { + throw new UnsupportedOperationException("Can not update object of type " + +intoValue.getClass().getName()+" (by deserializer of type "+getClass().getName()+")"); + } + + /** + * Deserialization called when type being deserialized is defined to + * contain additional type identifier, to allow for correctly + * instantiating correct subtype. This can be due to annotation on + * type (or its supertype), or due to global settings without + * annotations. + *

+ * Default implementation may work for some types, but ideally subclasses + * should not rely on current default implementation. + * Implementation is mostly provided to avoid compilation errors with older + * code. + * + * @param typeDeserializer Deserializer to use for handling type information + */ + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + // We could try calling + return typeDeserializer.deserializeTypedFromAny(p, ctxt); + } + + /* + /********************************************************** + /* Fluent factory methods for constructing decorated versions + /********************************************************** + */ + + /** + * Method that will return deserializer instance that is able + * to handle "unwrapped" value instances + * If no unwrapped instance can be constructed, will simply + * return this object as-is. + *

+ * Default implementation just returns 'this' + * indicating that no unwrapped variant exists + */ + public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) { + return this; + } + + /** + * Method that can be called to try to replace deserializer this deserializer + * delegates calls to. If not supported (either this deserializer does not + * delegate anything; or it does not want any changes), should either + * throw {@link UnsupportedOperationException} (if operation does not + * make sense or is not allowed); or return this deserializer as is. + * + * @since 2.1 + */ + public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) { + throw new UnsupportedOperationException(); + } + + /* + /********************************************************** + /* Introspection methods for figuring out configuration/setup + /* of this deserializer instance and/or type it handles + /********************************************************** + */ + + /** + * Method for accessing type of values this deserializer produces. + * Note that this information is not guaranteed to be exact -- it + * may be a more generic (super-type) -- but it should not be + * incorrect (return a non-related type). + *

+ * Default implementation will return null, which means almost same + * same as returning Object.class would; that is, that + * nothing is known about handled type. + *

+ * @since 2.3 + */ + public Class handledType() { return null; } + + /** + * Method called to see if deserializer instance is cachable and + * usable for other properties of same type (type for which instance + * was created). + *

+ * Note that cached instances are still resolved on per-property basis, + * if instance implements {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer}: + * cached instance is just as the base. This means that in most cases it is safe to + * cache instances; however, it only makes sense to cache instances + * if instantiation is expensive, or if instances are heavy-weight. + *

+ * Default implementation returns false, to indicate that no caching + * is done. + */ + public boolean isCachable() { return false; } + + /** + * Accessor that can be used to determine if this deserializer uses + * another deserializer for actual deserialization, by delegating + * calls. If so, will return immediate delegate (which itself may + * delegate to further deserializers); otherwise will return null. + * + * @return Deserializer this deserializer delegates calls to, if null; + * null otherwise. + * + * @since 2.1 + */ + public JsonDeserializer getDelegatee() { + return null; + } + + /** + * Method that will + * either return null to indicate that type being deserializers + * has no concept of properties; or a collection of identifiers + * for which toString will give external property + * name. + * This is only to be used for error reporting and diagnostics + * purposes (most commonly, to accompany "unknown property" + * exception). + * + * @since 2.0 + */ + public Collection getKnownPropertyNames() { + return null; + } + + /* + /********************************************************** + /* Other accessors + /********************************************************** + */ + + /** + * Method that can be called to determine value to be used for + * representing null values (values deserialized when JSON token + * is {@link JsonToken#VALUE_NULL}). Usually this is simply + * Java null, but for some types (especially primitives) it may be + * necessary to use non-null values. + *

+ * Since version 2.6 (in which the context argument was added), call is + * expected to be made each and every time a null token needs to + * be handled. + *

+ * Default implementation simply returns null. + * + * @since 2.6 Added to replace earlier no-arguments variant + */ + public T getNullValue(DeserializationContext ctxt) throws JsonMappingException { + // Change the direction in 2.7 + return getNullValue(); + } + + /** + * Method called to determine value to be used for "empty" values + * (most commonly when deserializing from empty JSON Strings). + * Usually this is same as {@link #getNullValue} (which in turn + * is usually simply Java null), but it can be overridden + * for types. Or, if type should never be converted from empty + * String, method can also throw an exception. + *

+ * Since version 2.6 (in which the context argument was added), call is + * expected to be made each and every time an empty value is needed. + *

+ * Default implementation simple calls {@link #getNullValue} and + * returns value. + * + * @since 2.6 Added to replace earlier no-arguments variant + */ + public T getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { + // Change the direction in 2.7 + return getEmptyValue(); + } + + /** + * Accessor that can be used to check whether this deserializer + * is expecting to possibly get an Object Identifier value instead of full value + * serialization, and if so, should be able to resolve it to actual + * Object instance to return as deserialized value. + *

+ * Default implementation returns null, as support can not be implemented + * generically. Some standard deserializers (most notably + * {@link com.fasterxml.jackson.databind.deser.BeanDeserializer}) + * do implement this feature, and may return reader instance, depending on exact + * configuration of instance (which is based on type, and referring property). + * + * @return ObjectIdReader used for resolving possible Object Identifier + * value, instead of full value serialization, if deserializer can do that; + * null if no Object Id is expected. + * + * @since 2.0 + */ + public ObjectIdReader getObjectIdReader() { return null; } + + /** + * Method needed by {@link BeanDeserializerFactory} to properly link + * managed- and back-reference pairs. + * + * @since 2.2 (was moved out of BeanDeserializerBase) + */ + public SettableBeanProperty findBackReference(String refName) + { + throw new IllegalArgumentException("Can not handle managed/back reference '"+refName + +"': type: value deserializer of type "+getClass().getName()+" does not support them"); + } + + /* + /********************************************************** + /* Deprecated methods + /********************************************************** + */ + + /** + * @deprecated Since 2.6 Use overloaded variant that takes context argument + */ + @Deprecated + public T getNullValue() { return null; } + + /** + * @deprecated Since 2.6 Use overloaded variant that takes context argument + */ + @Deprecated + public T getEmptyValue() { return getNullValue(); } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * This marker class is only to be used with annotations, to + * indicate that no deserializer is configured. + *

+ * Specifically, this class is to be used as the marker for + * annotation {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize} + */ + public abstract static class None extends JsonDeserializer { + private None() { } // not to be instantiated + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonMappingException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonMappingException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonMappingException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,516 @@ +package com.fasterxml.jackson.databind; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Checked exception used to signal fatal problems with mapping of + * content, distinct from low-level I/O problems (signaled using + * simple {@link java.io.IOException}s) or data encoding/decoding + * problems (signaled with {@link com.fasterxml.jackson.core.JsonParseException}, + * {@link com.fasterxml.jackson.core.JsonGenerationException}). + *

+ * One additional feature is the ability to denote relevant path + * of references (during serialization/deserialization) to help in + * troubleshooting. + */ +public class JsonMappingException + extends JsonProcessingException +{ + private static final long serialVersionUID = 1L; + + /** + * Let's limit length of reference chain, to limit damage in cases + * of infinite recursion. + */ + final static int MAX_REFS_TO_LIST = 1000; + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Simple bean class used to contain references. References + * can be added to indicate execution/reference path that + * lead to the problem that caused this exception to be + * thrown. + */ + public static class Reference implements Serializable + { + private static final long serialVersionUID = 1L; + + /** + * Object through which reference was resolved. Can be either + * actual instance (usually the case for serialization), or + * Class (usually the case for deserialization). + */ + protected Object _from; + + /** + * Name of field (for beans) or key (for Maps) that is part + * of the reference. May be null for Collection types (which + * generally have {@link #_index} defined), or when resolving + * Map classes without (yet) having an instance to operate on. + */ + protected String _fieldName; + + /** + * Index within a {@link Collection} instance that contained + * the reference; used if index is relevant and available. + * If either not applicable, or not available, -1 is used to + * denote "not known". + */ + protected int _index = -1; + + /** + * Lazily-constructed description of this instance; needed mostly to + * allow JDK serialization to work in case where {@link #_from} is + * non-serializable (and has to be dropped) but we still want to pass + * actual description along. + * + * @since 2.7.4 + */ + protected String _asString; + + /** + * Default constructor for deserialization/sub-classing purposes + */ + protected Reference() { } + + public Reference(Object from) { _from = from; } + + public Reference(Object from, String fieldName) { + _from = from; + if (fieldName == null) { + throw new NullPointerException("Can not pass null fieldName"); + } + _fieldName = fieldName; + } + + public Reference(Object from, int index) { + _from = from; + _index = index; + } + + private Reference(Reference src, String asString, Object newFrom) { + _asString = asString; + _from = newFrom; + _fieldName = src._fieldName; + _index = src._index; + } + + public void setFrom(Object o) { _from = o; } + public void setFieldName(String n) { _fieldName = n; } + public void setIndex(int ix) { _index = ix; } + + public Object getFrom() { return _from; } + public String getFieldName() { return _fieldName; } + public int getIndex() { return _index; } + + @Override public String toString() { + if (_asString == null) { + StringBuilder sb = new StringBuilder(); + + if (_from == null) { // can this ever occur? + sb.append("UNKNOWN"); + } else { + Class cls = (_from instanceof Class) ? (Class)_from : _from.getClass(); + /* Hmmh. Although Class.getName() is mostly ok, it does look + * butt-ugly for arrays. So let's use getSimpleName() instead; + * but have to prepend package name too. + */ + String pkgName = ClassUtil.getPackageName(cls); + if (pkgName != null) { + sb.append(pkgName); + sb.append('.'); + } + sb.append(cls.getSimpleName()); + } + sb.append('['); + if (_fieldName != null) { + sb.append('"'); + sb.append(_fieldName); + sb.append('"'); + } else if (_index >= 0) { + sb.append(_index); + } else { + sb.append('?'); + } + sb.append(']'); + _asString = sb.toString(); + } + return _asString; + } + + /** + * May need some cleaning here, given that `from` may or may not be serializable. + * + * since 2.7.4 + */ + Object writeReplace() { + // as per [databind#1195], reference may cause trouble, if non-serializable + // instance (either directly or transitively); and even use of Class would often + // be problematic. Because of this, clear up `_from` always, but ensure that + // description is preserved + return new Reference(this, toString(), null); + } + } + + /* + /********************************************************** + /* State/configuration + /********************************************************** + */ + + /** + * Path through which problem that triggering throwing of + * this exception was reached. + */ + protected LinkedList _path; + + /** + * Underlying processor ({@link JsonParser} or {@link JsonGenerator}), + * if known. + *

+ * NOTE: typically not serializable hence transient + * + * @since 2.7 + */ + protected transient Closeable _processor; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead + */ + @Deprecated // since 2.7 + public JsonMappingException(String msg) { super(msg); } + + /** + * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead + */ + @Deprecated // since 2.7 + public JsonMappingException(String msg, Throwable rootCause) { super(msg, rootCause); } + + /** + * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead + */ + @Deprecated // since 2.7 + public JsonMappingException(String msg, JsonLocation loc) { super(msg, loc); } + + /** + * @deprecated Since 2.7 Use variant that takes {@link JsonParser} instead + */ + @Deprecated // since 2.7 + public JsonMappingException(String msg, JsonLocation loc, Throwable rootCause) { super(msg, loc, rootCause); } + + /** + * @since 2.7 + */ + public JsonMappingException(Closeable processor, String msg) { + super(msg); + _processor = processor; + if (processor instanceof JsonParser) { + // 17-Aug-2015, tatu: Use of token location makes some sense from databinding, + // since actual parsing (current) location is typically only needed for low-level + // parsing exceptions. + _location = ((JsonParser) processor).getTokenLocation(); + } + } + + /** + * @since 2.7 + */ + public JsonMappingException(Closeable processor, String msg, Throwable problem) { + super(msg, problem); + _processor = processor; + if (processor instanceof JsonParser) { + _location = ((JsonParser) processor).getTokenLocation(); + } + } + + /** + * @since 2.7 + */ + public JsonMappingException(Closeable processor, String msg, JsonLocation loc) { + super(msg, loc); + _processor = processor; + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(JsonParser p, String msg) { + return new JsonMappingException(p, msg); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(JsonParser p, String msg, Throwable problem) { + return new JsonMappingException(p, msg, problem); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(JsonGenerator g, String msg) { + return new JsonMappingException(g, msg, (Throwable) null); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(JsonGenerator g, String msg, Throwable problem) { + return new JsonMappingException(g, msg, problem); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(DeserializationContext ctxt, String msg) { + return new JsonMappingException(ctxt.getParser(), msg); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(DeserializationContext ctxt, String msg, Throwable t) { + return new JsonMappingException(ctxt.getParser(), msg, t); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(SerializerProvider ctxt, String msg) { + /* 17-Aug-2015, tatu: As per [databind#903] this is bit problematic as + * SerializerProvider instance does not currently hold on to generator... + */ + JsonGenerator g = null; + return new JsonMappingException(g, msg); + } + + /** + * @since 2.7 + */ + public static JsonMappingException from(SerializerProvider ctxt, String msg, Throwable problem) { + /* 17-Aug-2015, tatu: As per [databind#903] this is bit problematic as + * SerializerProvider instance does not currently hold on to generator... + */ + JsonGenerator g = null; + return new JsonMappingException(g, msg, problem); + } + + /** + * Factory method used when "upgrading" an {@link IOException} into + * {@link JsonMappingException}: usually only needed to comply with + * a signature. + * + * @since 2.1 + */ + public static JsonMappingException fromUnexpectedIOE(IOException src) { + return new JsonMappingException(null, + String.format("Unexpected IOException (of type %s): %s", + src.getClass().getName(), src.getMessage())); + } + + /** + * Method that can be called to either create a new JsonMappingException + * (if underlying exception is not a JsonMappingException), or augment + * given exception with given path/reference information. + * + * This version of method is called when the reference is through a + * non-indexed object, such as a Map or POJO/bean. + */ + public static JsonMappingException wrapWithPath(Throwable src, Object refFrom, + String refFieldName) { + return wrapWithPath(src, new Reference(refFrom, refFieldName)); + } + + /** + * Method that can be called to either create a new JsonMappingException + * (if underlying exception is not a JsonMappingException), or augment + * given exception with given path/reference information. + * + * This version of method is called when the reference is through an + * index, which happens with arrays and Collections. + */ + public static JsonMappingException wrapWithPath(Throwable src, Object refFrom, int index) { + return wrapWithPath(src, new Reference(refFrom, index)); + } + + /** + * Method that can be called to either create a new JsonMappingException + * (if underlying exception is not a JsonMappingException), or augment + * given exception with given path/reference information. + */ + @SuppressWarnings("resource") + public static JsonMappingException wrapWithPath(Throwable src, Reference ref) + { + JsonMappingException jme; + if (src instanceof JsonMappingException) { + jme = (JsonMappingException) src; + } else { + String msg = src.getMessage(); + // Let's use a more meaningful placeholder if all we have is null + if (msg == null || msg.length() == 0) { + msg = "(was "+src.getClass().getName()+")"; + } + // 17-Aug-2015, tatu: Let's also pass the processor (parser/generator) along + Closeable proc = null; + if (src instanceof JsonProcessingException) { + Object proc0 = ((JsonProcessingException) src).getProcessor(); + if (proc0 instanceof Closeable) { + proc = (Closeable) proc0; + } + } + jme = new JsonMappingException(proc, msg, src); + } + jme.prependPath(ref); + return jme; + } + + /* + /********************************************************** + /* Accessors/mutators + /********************************************************** + */ + + /** + * Method for accessing full structural path within type hierarchy + * down to problematic property. + */ + public List getPath() + { + if (_path == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(_path); + } + + /** + * Method for accesing description of path that lead to the + * problem that triggered this exception + */ + public String getPathReference() + { + return getPathReference(new StringBuilder()).toString(); + } + + public StringBuilder getPathReference(StringBuilder sb) + { + _appendPathDesc(sb); + return sb; + } + + /** + * Method called to prepend a reference information in front of + * current path + */ + public void prependPath(Object referrer, String fieldName) + { + Reference ref = new Reference(referrer, fieldName); + prependPath(ref); + } + /** + * Method called to prepend a reference information in front of + * current path + */ + public void prependPath(Object referrer, int index) + { + Reference ref = new Reference(referrer, index); + prependPath(ref); + } + + public void prependPath(Reference r) + { + if (_path == null) { + _path = new LinkedList(); + } + /* Also: let's not increase without bounds. Could choose either + * head or tail; tail is easier (no need to ever remove), as + * well as potentially more useful so let's use it: + */ + if (_path.size() < MAX_REFS_TO_LIST) { + _path.addFirst(r); + } + } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public String getLocalizedMessage() { + return _buildMessage(); + } + + /** + * Method is overridden so that we can properly inject description + * of problem path, if such is defined. + */ + @Override + public String getMessage() { + return _buildMessage(); + } + + protected String _buildMessage() + { + /* First: if we have no path info, let's just use parent's + * definition as is + */ + String msg = super.getMessage(); + if (_path == null) { + return msg; + } + StringBuilder sb = (msg == null) ? new StringBuilder() : new StringBuilder(msg); + /* 18-Feb-2009, tatu: initially there was a linefeed between + * message and path reference; but unfortunately many systems + * (loggers, junit) seem to assume linefeeds are only added to + * separate stack trace. + */ + sb.append(" (through reference chain: "); + sb = getPathReference(sb); + sb.append(')'); + return sb.toString(); + } + + @Override + public String toString() + { + return getClass().getName()+": "+getMessage(); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected void _appendPathDesc(StringBuilder sb) + { + if (_path == null) { + return; + } + Iterator it = _path.iterator(); + while (it.hasNext()) { + sb.append(it.next().toString()); + if (it.hasNext()) { + sb.append("->"); + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,963 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import com.fasterxml.jackson.databind.node.MissingNode; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Base class for all JSON nodes, which form the basis of JSON + * Tree Model that Jackson implements. + * One way to think of these nodes is to consider them + * similar to DOM nodes in XML DOM trees. + *

+ * As a general design rule, most accessors ("getters") are included + * in this base class, to allow for traversing structure without + * type casts. Most mutators, however, need to be accessed through + * specific sub-classes (such as ObjectNode + * and ArrayNode). + * This seems sensible because proper type + * information is generally available when building or modifying + * trees, but less often when reading a tree (newly built from + * parsed JSON content). + *

+ * Actual concrete sub-classes can be found from package + * {@link com.fasterxml.jackson.databind.node}. + *

+ * Note that it is possible to "read" from nodes, using + * method {@link TreeNode#traverse(ObjectCodec)}, which will result in + * a {@link JsonParser} being constructed. This can be used for (relatively) + * efficient conversations between different representations; and it is what + * core databind uses for methods like {@link ObjectMapper#treeToValue(TreeNode, Class)} + * and {@link ObjectMapper#treeAsTokens(TreeNode)} + */ +public abstract class JsonNode + extends JsonSerializable.Base // i.e. implements JsonSerializable + implements TreeNode, Iterable +{ + /* + /********************************************************** + /* Construction, related + /********************************************************** + */ + + protected JsonNode() { } + + /** + * Method that can be called to get a node that is guaranteed + * not to allow changing of this node through mutators on + * this node or any of its children. + * This means it can either make a copy of this node (and all + * mutable children and grand children nodes), or node itself + * if it is immutable. + *

+ * Note: return type is guaranteed to have same type as the + * node method is called on; which is why method is declared + * with local generic type. + * + * @since 2.0 + * + * @return Node that is either a copy of this node (and all non-leaf + * children); or, for immutable leaf nodes, node itself. + */ + public abstract T deepCopy(); + + /* + /********************************************************** + /* TreeNode implementation + /********************************************************** + */ + +// public abstract JsonToken asToken(); +// public abstract JsonToken traverse(); +// public abstract JsonToken traverse(ObjectCodec codec); +// public abstract JsonParser.NumberType numberType(); + + @Override + public int size() { return 0; } + + @Override + public final boolean isValueNode() + { + switch (getNodeType()) { + case ARRAY: case OBJECT: case MISSING: + return false; + default: + return true; + } + } + + @Override + public final boolean isContainerNode() { + final JsonNodeType type = getNodeType(); + return type == JsonNodeType.OBJECT || type == JsonNodeType.ARRAY; + } + + @Override + public final boolean isMissingNode() { + return getNodeType() == JsonNodeType.MISSING; + } + + @Override + public final boolean isArray() { + return getNodeType() == JsonNodeType.ARRAY; + } + + @Override + public final boolean isObject() { + return getNodeType() == JsonNodeType.OBJECT; + } + + /** + * Method for accessing value of the specified element of + * an array node. For other nodes, null is always returned. + *

+ * For array nodes, index specifies + * exact location within array and allows for efficient iteration + * over child elements (underlying storage is guaranteed to + * be efficiently indexable, i.e. has random-access to elements). + * If index is less than 0, or equal-or-greater than + * node.size(), null is returned; no exception is + * thrown for any index. + *

+ * NOTE: if the element value has been explicitly set as null + * (which is different from removal!), + * a {@link com.fasterxml.jackson.databind.node.NullNode} will be returned, + * not null. + * + * @return Node that represent value of the specified element, + * if this node is an array and has specified element. + * Null otherwise. + */ + @Override + public abstract JsonNode get(int index); + + /** + * 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: if the property value has been explicitly set as null + * (which is different from removal!), + * a {@link com.fasterxml.jackson.databind.node.NullNode} will be returned, + * not null. + * + * @return Node that represent value of the specified field, + * if this node is an object and has value for the specified + * field. Null otherwise. + */ + @Override + public JsonNode get(String fieldName) { return null; } + /** + * This method is similar to {@link #get(String)}, except + * that instead of returning null if no such value exists (due + * to this node not being an object, or object not having value + * for the specified field), + * a "missing node" (node that returns true for + * {@link #isMissingNode}) will be returned. This allows for + * convenient and safe chained access via path calls. + */ + + @Override + public abstract JsonNode path(String fieldName); + + /** + * This method is similar to {@link #get(int)}, except + * that instead of returning null if no such element exists (due + * to index being out of range, or this node not being an array), + * a "missing node" (node that returns true for + * {@link #isMissingNode}) will be returned. This allows for + * convenient and safe chained access via path calls. + */ + @Override + public abstract JsonNode path(int index); + + @Override + public Iterator fieldNames() { + return ClassUtil.emptyIterator(); + } + + /** + * Method for locating node specified by given JSON pointer instances. + * Method will never return null; if no matching node exists, + * will return a node for which {@link #isMissingNode()} returns true. + * + * @return Node that matches given JSON Pointer: if no match exists, + * will return a node for which {@link #isMissingNode()} returns true. + * + * @since 2.3 + */ + @Override + public final JsonNode at(JsonPointer ptr) + { + // Basically: value nodes only match if we have "empty" path left + if (ptr.matches()) { + return this; + } + JsonNode n = _at(ptr); + if (n == null) { + return MissingNode.getInstance(); + } + return n.at(ptr.tail()); + } + + /** + * Convenience method that is functionally equivalent to: + *

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

+ * Note that if the same expression is used often, it is preferable to construct + * {@link JsonPointer} instance once and reuse it: this method will not perform + * any caching of compiled expressions. + * + * @param jsonPtrExpr 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 + */ + @Override + public final JsonNode at(String jsonPtrExpr) { + return at(JsonPointer.compile(jsonPtrExpr)); + } + + protected abstract JsonNode _at(JsonPointer ptr); + + /* + /********************************************************** + /* Public API, type introspection + /********************************************************** + */ + + // // First high-level division between values, containers and "missing" + + /** + * Return the type of this node + * + * @return the node type as a {@link JsonNodeType} enum value + * + * @since 2.2 + */ + public abstract JsonNodeType getNodeType(); + + /** + * Method that can be used to check if the node is a wrapper + * for a POJO ("Plain Old Java Object" aka "bean". + * Returns true only for + * instances of POJONode. + * + * @return True if this node wraps a POJO + */ + public final boolean isPojo() { + return getNodeType() == JsonNodeType.POJO; + } + + /** + * @return True if this node represents a numeric JSON value + */ + public final boolean isNumber() { + return getNodeType() == JsonNodeType.NUMBER; + } + + /** + * + * @return True if this node represents an integral (integer) + * numeric JSON value + */ + public boolean isIntegralNumber() { return false; } + + /** + * @return True if this node represents a non-integral + * numeric JSON value + */ + public boolean isFloatingPointNumber() { return false; } + + /** + * Method that can be used to check whether contained value + * is a number represented as Java short. + * Note, however, that even if this method returns false, it + * is possible that conversion would be possible from other numeric + * types -- to check if this is possible, use + * {@link #canConvertToInt()} instead. + * + * @return True if the value contained by this node is stored as Java short + */ + public boolean isShort() { return false; } + + /** + * Method that can be used to check whether contained value + * is a number represented as Java int. + * Note, however, that even if this method returns false, it + * is possible that conversion would be possible from other numeric + * types -- to check if this is possible, use + * {@link #canConvertToInt()} instead. + * + * @return True if the value contained by this node is stored as Java int + */ + public boolean isInt() { return false; } + + /** + * Method that can be used to check whether contained value + * is a number represented as Java long. + * Note, however, that even if this method returns false, it + * is possible that conversion would be possible from other numeric + * types -- to check if this is possible, use + * {@link #canConvertToInt()} instead. + * + * @return True if the value contained by this node is stored as Java long + */ + public boolean isLong() { return false; } + + /** + * @since 2.2 + */ + public boolean isFloat() { return false; } + + public boolean isDouble() { return false; } + public boolean isBigDecimal() { return false; } + public boolean isBigInteger() { return false; } + + /** + * Method that checks whether this node represents basic JSON String + * value. + */ + public final boolean isTextual() { + return getNodeType() == JsonNodeType.STRING; + } + + /** + * Method that can be used to check if this node was created from + * JSON boolean value (literals "true" and "false"). + */ + public final boolean isBoolean() { + return getNodeType() == JsonNodeType.BOOLEAN; + } + + /** + * Method that can be used to check if this node was created from + * JSON literal null value. + */ + public final boolean isNull() { + return getNodeType() == JsonNodeType.NULL; + } + + /** + * Method that can be used to check if this node represents + * binary data (Base64 encoded). Although this will be externally + * written as JSON String value, {@link #isTextual} will + * return false if this method returns true. + * + * @return True if this node represents base64 encoded binary data + */ + public final boolean isBinary() { + return getNodeType() == JsonNodeType.BINARY; + } + + /** + * Method that can be used to check whether this node is a numeric + * node ({@link #isNumber} would return true) AND its value fits + * within Java's 32-bit signed integer type, int. + * Note that floating-point numbers are convertible if the integral + * part fits without overflow (as per standard Java coercion rules) + *

+ * NOTE: this method does not consider possible value type conversion + * from JSON String into Number; so even if this method returns false, + * it is possible that {@link #asInt} could still succeed + * if node is a JSON String representing integral number, or boolean. + * + * @since 2.0 + */ + public boolean canConvertToInt() { return false; } + + /** + * Method that can be used to check whether this node is a numeric + * node ({@link #isNumber} would return true) AND its value fits + * within Java's 64-bit signed integer type, long. + * Note that floating-point numbers are convertible if the integral + * part fits without overflow (as per standard Java coercion rules) + *

+ * NOTE: this method does not consider possible value type conversion + * from JSON String into Number; so even if this method returns false, + * it is possible that {@link #asLong} could still succeed + * if node is a JSON String representing integral number, or boolean. + * + * @since 2.0 + */ + public boolean canConvertToLong() { return false; } + + /* + /********************************************************** + /* Public API, straight value access + /********************************************************** + */ + + /** + * Method to use for accessing String values. + * Does NOT do any conversions for non-String value nodes; + * for non-String values (ones for which {@link #isTextual} returns + * false) null will be returned. + * For String values, null is never returned (but empty Strings may be) + * + * @return Textual value this node contains, iff it is a textual + * JSON node (comes from JSON String value entry) + */ + public String textValue() { return null; } + + /** + * Method to use for accessing binary content of binary nodes (nodes + * for which {@link #isBinary} returns true); or for Text Nodes + * (ones for which {@link #textValue} returns non-null value), + * to read decoded base64 data. + * For other types of nodes, returns null. + * + * @return Binary data this node contains, iff it is a binary + * node; null otherwise + */ + public byte[] binaryValue() throws IOException { + return null; + } + + /** + * Method to use for accessing JSON boolean values (value + * literals 'true' and 'false'). + * For other types, always returns false. + * + * @return Textual value this node contains, iff it is a textual + * json node (comes from JSON String value entry) + */ + public boolean booleanValue() { return false; } + + /** + * Returns numeric value for this node, if and only if + * this node is numeric ({@link #isNumber} returns true); otherwise + * returns null + * + * @return Number value this node contains, if any (null for non-number + * nodes). + */ + public Number numberValue() { return null; } + + /** + * Returns 16-bit short value for this node, if and only if + * this node is numeric ({@link #isNumber} returns true). For other + * types returns 0. + * For floating-point numbers, value is truncated using default + * Java coercion, similar to how cast from double to short operates. + * + * @return Short value this node contains, if any; 0 for non-number + * nodes. + */ + public short shortValue() { return 0; } + + /** + * Returns integer value for this node, if and only if + * this node is numeric ({@link #isNumber} returns true). For other + * types returns 0. + * For floating-point numbers, value is truncated using default + * Java coercion, similar to how cast from double to int operates. + * + * @return Integer value this node contains, if any; 0 for non-number + * nodes. + */ + public int intValue() { return 0; } + + /** + * Returns 64-bit long value for this node, if and only if + * this node is numeric ({@link #isNumber} returns true). For other + * types returns 0. + * For floating-point numbers, value is truncated using default + * Java coercion, similar to how cast from double to long operates. + * + * @return Long value this node contains, if any; 0 for non-number + * nodes. + */ + public long longValue() { return 0L; } + + /** + * Returns 32-bit floating value for this node, if and only if + * this node is numeric ({@link #isNumber} returns true). For other + * types returns 0.0. + * For integer values, conversion is done using coercion; this means + * that an overflow is possible for `long` values + * + * @return 32-bit float value this node contains, if any; 0.0 for non-number nodes. + * + * @since 2.2 + */ + public float floatValue() { return 0.0f; } + + /** + * Returns 64-bit floating point (double) value for this node, if and only if + * this node is numeric ({@link #isNumber} returns true). For other + * types returns 0.0. + * For integer values, conversion is done using coercion; this may result + * in overflows with {@link BigInteger} values. + * + * @return 64-bit double value this node contains, if any; 0.0 for non-number nodes. + * + * @since 2.2 + */ + public double doubleValue() { return 0.0; } + + public BigDecimal decimalValue() { return BigDecimal.ZERO; } + public BigInteger bigIntegerValue() { return BigInteger.ZERO; } + + /* + /********************************************************** + /* Public API, value access with conversion(s)/coercion(s) + /********************************************************** + */ + + /** + * Method that will return a valid String representation of + * the container value, if the node is a value node + * (method {@link #isValueNode} returns true), + * otherwise empty String. + */ + public abstract String asText(); + + /** + * Method similar to {@link #asText()}, except that it will return + * defaultValue in cases where null value would be returned; + * either for missing nodes (trying to access missing property, or element + * at invalid item for array) or explicit nulls. + * + * @since 2.4 + */ + public String asText(String defaultValue) { + String str = asText(); + return (str == null) ? defaultValue : str; + } + + /** + * Method that will try to convert value of this node to a Java 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 types + * like Objects and Arrays), + * default value of 0 will be returned; no exceptions are thrown. + */ + public int asInt() { + return asInt(0); + } + + /** + * Method that will try to convert value of this node to a Java 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 types + * like Objects and Arrays), + * specified defaultValue will be returned; no exceptions are thrown. + */ + public int asInt(int defaultValue) { + return defaultValue; + } + + /** + * Method that will try to convert value of this node to a Java long. + * Numbers are coerced using default Java rules; booleans convert to 0 (false) + * and 1 (true), and Strings are parsed using default Java language integer + * parsing rules. + *

+ * If representation can not be converted to an long (including structured types + * like Objects and Arrays), + * default value of 0 will be returned; no exceptions are thrown. + */ + public long asLong() { + return asLong(0L); + } + + /** + * Method that will try to convert value of this node to a Java long. + * Numbers are coerced using default Java rules; booleans convert to 0 (false) + * and 1 (true), and Strings are parsed using default Java language integer + * parsing rules. + *

+ * If representation can not be converted to an long (including structured types + * like Objects and Arrays), + * specified defaultValue will be returned; no exceptions are thrown. + */ + public long asLong(long defaultValue) { + return defaultValue; + } + + /** + * Method that will try to convert value of this node to a Java double. + * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) + * and 1.0 (true), and Strings are parsed using default Java language integer + * parsing rules. + *

+ * If representation can not be converted to an int (including structured types + * like Objects and Arrays), + * default value of 0.0 will be returned; no exceptions are thrown. + */ + public double asDouble() { + return asDouble(0.0); + } + + /** + * Method that will try to convert value of this node to a Java double. + * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) + * and 1.0 (true), and Strings are parsed using default Java language integer + * parsing rules. + *

+ * If representation can not be converted to an int (including structured types + * like Objects and Arrays), + * specified defaultValue will be returned; no exceptions are thrown. + */ + public double asDouble(double defaultValue) { + return defaultValue; + } + + /** + * Method that will try to convert value of this node to a Java 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 asBoolean() { + return asBoolean(false); + } + + /** + * Method that will try to convert value of this node to a Java boolean. + * JSON booleans map naturally; integer numbers other than 0 map to true, and + * 0 maps to false + * and Strings 'true' and 'false' map to corresponding values. + *

+ * If representation can not be converted to a boolean value (including structured types + * like Objects and Arrays), + * specified defaultValue will be returned; no exceptions are thrown. + */ + public boolean asBoolean(boolean defaultValue) { + return defaultValue; + } + + /* + /********************************************************** + /* Public API, value find / existence check methods + /********************************************************** + */ + + /** + * Method that allows checking whether this node is JSON Object node + * and contains value for specified property. If this is the case + * (including properties with explicit null values), returns true; + * otherwise returns false. + *

+ * This method is equivalent to: + *

+     *   node.get(fieldName) != null
+     *
+ * (since return value of get() is node, not value node contains) + *

+ * NOTE: when explicit null values are added, this + * method will return true for such properties. + * + * @param fieldName Name of element to check + * + * @return True if this node is a JSON Object node, and has a property + * entry with specified name (with any value, including null value) + */ + public boolean has(String fieldName) { + return get(fieldName) != null; + } + + /** + * Method that allows checking whether this node is JSON Array node + * and contains a value for specified index + * If this is the case + * (including case of specified indexing having null as value), returns true; + * otherwise returns false. + *

+ * Note: array element indexes are 0-based. + *

+ * This method is equivalent to: + *

+     *   node.get(index) != null
+     *
+ *

+ * NOTE: this method will return true for explicitly added + * null values. + * + * @param index Index to check + * + * @return True if this node is a JSON Object node, and has a property + * entry with specified name (with any value, including null value) + */ + public boolean has(int index) { + return get(index) != null; + } + + /** + * Method that is similar to {@link #has(String)}, but that will + * return false for explicitly added nulls. + *

+ * This method is functionally equivalent to: + *

+     *   node.get(fieldName) != null << !node.get(fieldName).isNull()
+     *
+ * + * @since 2.1 + */ + public boolean hasNonNull(String fieldName) { + JsonNode n = get(fieldName); + return (n != null) && !n.isNull(); + } + + /** + * Method that is similar to {@link #has(int)}, but that will + * return false for explicitly added nulls. + *

+ * This method is equivalent to: + *

+     *   node.get(index) != null << !node.get(index).isNull()
+     *
+ * + * @since 2.1 + */ + public boolean hasNonNull(int index) { + JsonNode n = get(index); + return (n != null) && !n.isNull(); + } + + /* + /********************************************************** + /* Public API, container access + /********************************************************** + */ + + /** + * Same as calling {@link #elements}; implemented so that + * convenience "for-each" loop can be used for looping over elements + * of JSON Array constructs. + */ + @Override + public final Iterator iterator() { return elements(); } + + /** + * Method for accessing all value nodes of this Node, iff + * this node is a JSON Array or Object node. In case of Object node, + * field names (keys) are not included, only values. + * For other types of nodes, returns empty iterator. + */ + public Iterator elements() { + return ClassUtil.emptyIterator(); + } + + /** + * @return Iterator that can be used to traverse all key/value pairs for + * object nodes; empty iterator (no contents) for other types + */ + public Iterator> fields() { + return ClassUtil.emptyIterator(); + } + + /* + /********************************************************** + /* Public API, find methods + /********************************************************** + */ + + /** + * Method for finding a JSON Object field with specified name in this + * node or its child nodes, and returning value it has. + * If no matching field is found in this node or its descendants, returns null. + * + * @param fieldName Name of field to look for + * + * @return Value of first matching node found, if any; null if none + */ + public abstract JsonNode findValue(String fieldName); + + /** + * Method for finding JSON Object fields with specified name, and returning + * found ones as a List. Note that sub-tree search ends if a field is found, + * so possible children of result nodes are not included. + * If no matching fields are found in this node or its descendants, returns + * an empty List. + * + * @param fieldName Name of field to look for + */ + public final List findValues(String fieldName) + { + List result = findValues(fieldName, null); + if (result == null) { + return Collections.emptyList(); + } + return result; + } + + /** + * Similar to {@link #findValues}, but will additionally convert + * values into Strings, calling {@link #asText}. + */ + public final List findValuesAsText(String fieldName) + { + List result = findValuesAsText(fieldName, null); + if (result == null) { + return Collections.emptyList(); + } + return result; + } + + /** + * Method similar to {@link #findValue}, but that will return a + * "missing node" instead of null if no field is found. Missing node + * is a specific kind of node for which {@link #isMissingNode} + * returns true; and all value access methods return empty or + * missing value. + * + * @param fieldName Name of field to look for + * + * @return Value of first matching node found; or if not found, a + * "missing node" (non-null instance that has no value) + */ + public abstract JsonNode findPath(String fieldName); + + /** + * Method for finding a JSON Object that contains specified field, + * within this node or its descendants. + * If no matching field is found in this node or its descendants, returns null. + * + * @param fieldName Name of field to look for + * + * @return Value of first matching node found, if any; null if none + */ + public abstract JsonNode findParent(String fieldName); + + /** + * Method for finding a JSON Object that contains specified field, + * within this node or its descendants. + * If no matching field is found in this node or its descendants, returns null. + * + * @param fieldName Name of field to look for + * + * @return Value of first matching node found, if any; null if none + */ + public final List findParents(String fieldName) + { + List result = findParents(fieldName, null); + if (result == null) { + return Collections.emptyList(); + } + return result; + } + + public abstract List findValues(String fieldName, List foundSoFar); + public abstract List findValuesAsText(String fieldName, List foundSoFar); + public abstract List findParents(String fieldName, List foundSoFar); + + /* + /********************************************************** + /* Public API, path handling + /********************************************************** + */ + + /** + * Method that can be called on Object nodes, to access a property + * that has Object value; or if no such property exists, to create, + * add and return such Object node. + * If the node method is called on is not Object node, + * or if property exists and has value that is not Object node, + * {@link UnsupportedOperationException} is thrown + */ + public JsonNode with(String propertyName) { + throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but " + +getClass().getName()+"), can not call with() on it"); + } + + /** + * Method that can be called on Object nodes, to access a property + * that has Array value; or if no such property exists, to create, + * add and return such Array node. + * If the node method is called on is not Object node, + * or if property exists and has value that is not Array node, + * {@link UnsupportedOperationException} is thrown + */ + public JsonNode withArray(String propertyName) { + throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but " + +getClass().getName()+"), can not call withArray() on it"); + } + + /* + /********************************************************** + /* Public API, comparison + /********************************************************** + */ + + /** + * Entry method for invoking customizable comparison, using passed-in + * {@link Comparator} object. Nodes will handle traversal of structured + * types (arrays, objects), but defer to comparator for scalar value + * comparisons. If a "natural" {@link Comparator} is passed -- one that + * simply calls equals() on one of arguments, passing the other + * -- implementation is the same as directly calling equals() + * on node. + *

+ * Default implementation simply delegates to passed in comparator, + * with this as the first argument, and other as + * the second argument. + * + * @param comparator Object called to compare two scalar {@link JsonNode} + * instances, and return either 0 (are equals) or non-zero (not equal) + * + * @since 2.6 + */ + public boolean equals(Comparator comparator, JsonNode other) { + return comparator.compare(this, other) == 0; + } + + /* + /********************************************************** + /* Overridden standard methods + /********************************************************** + */ + + /** + * Method that will produce developer-readable representation of the + * node; which may or may not be as valid JSON. + * If you want valid JSON output (or output formatted using one of + * other Jackson supported data formats) make sure to use + * {@link ObjectMapper} or {@link ObjectWriter} to serialize an + * instance, for example: + *

+     *   String json = objectMapper.writeValueAsString(rootNode);
+     *
+ *

+ * Note: method defined as abstract to ensure all implementation + * classes explicitly implement method, instead of relying + * on {@link Object#toString()} definition. + */ + @Override + public abstract String toString(); + + /** + * Equality for node objects is defined as full (deep) value + * equality. This means that it is possible to compare complete + * JSON trees for equality by comparing equality of root nodes. + *

+ * Note: marked as abstract to ensure all implementation + * classes define it properly and not rely on definition + * from {@link java.lang.Object}. + */ + @Override + public abstract boolean equals(Object o); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonSerializable.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonSerializable.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonSerializable.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Interface that can be implemented by objects that know how to + * serialize themselves to JSON, using {@link JsonGenerator} + * (and {@link SerializerProvider} if necessary). + *

+ * Note that implementing this interface binds implementing object + * closely to Jackson API, and that it is often not necessary to do + * so -- if class is a bean, it can be serialized without + * implementing this interface. + *

+ * Note that while it is possible to just directly implement {@link JsonSerializable}, + * actual implementations are strongly recommended to instead extend + * {@link JsonSerializable.Base}. + */ +public interface JsonSerializable +{ + /** + * Serialization method called when no additional type information is + * to be included in serialization. + */ + public void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException; + + /** + * Serialization method called when additional type information is + * expected to be included in serialization, for deserialization to use. + *

+ * Usually implementation consists of a call to one of methods + * in {@link TypeSerializer} (such as {@link TypeSerializer#writeTypePrefixForObject(Object, JsonGenerator)}) + * followed by serialization of contents, + * followed by another call to {@link TypeSerializer} + * (such as {@link TypeSerializer#writeTypeSuffixForObject(Object, JsonGenerator)}). + * Exact methods to call in {@link TypeSerializer} depend on shape of JSON Object used + * (Array, Object or scalar like String/Number/Boolean). + *

+ * Note that some types (most notably, "natural" types: String, Integer, + * Double and Boolean) never include type information. + */ + public void serializeWithType(JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) throws IOException; + + /** + * Base class with minimal implementation, as well as couple of extension methods + * that core Jackson databinding makes use of. + * Use of this base class is strongly recommended over directly implementing + * {@link JsonSerializable}. + * + * @since 2.6 + */ + public abstract static class Base implements JsonSerializable + { + /** + * Method that may be called on instance to determine if it is considered + * "empty" for purposes of serialization filtering or not. + */ + public boolean isEmpty(SerializerProvider serializers) { + return false; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/JsonSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,295 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; +import java.util.Iterator; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.PropertyWriter; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Abstract class that defines API used by {@link ObjectMapper} (and + * other chained {@link JsonSerializer}s too) to serialize Objects of + * arbitrary types into JSON, using provided {@link JsonGenerator}. + * {@link com.fasterxml.jackson.databind.ser.std.StdSerializer} instead + * of this class, since it will implement many of optional + * methods of this class. + *

+ * NOTE: various serialize methods are never (to be) called + * with null values -- caller must handle null values, usually + * by calling {@link SerializerProvider#findNullValueSerializer} to obtain + * serializer to use. + * This also means that custom serializers can not be directly used to change + * the output to produce when serializing null values. + *

+ * If serializer is an aggregate one -- meaning it delegates handling of some + * of its contents by using other serializer(s) -- it typically also needs + * to implement {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer}, + * which can locate secondary serializers needed. This is important to allow dynamic + * overrides of serializers; separate call interface is needed to separate + * resolution of secondary serializers (which may have cyclic link back + * to serializer itself, directly or indirectly). + *

+ * In addition, to support per-property annotations (to configure aspects + * of serialization on per-property basis), serializers may want + * to implement + * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}, + * which allows specialization of serializers: call to + * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual} + * is passed information on property, and can create a newly configured + * serializer for handling that particular property. + *

+ * If both + * {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer} and + * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer} + * are implemented, resolution of serializers occurs before + * contextualization. + */ +public abstract class JsonSerializer + implements JsonFormatVisitable // since 2.1 +{ + /* + /********************************************************** + /* Fluent factory methods for constructing decorated versions + /********************************************************** + */ + + /** + * Method that will return serializer instance that produces + * "unwrapped" serialization, if applicable for type being + * serialized (which is the case for some serializers + * that produce JSON Objects as output). + * If no unwrapped serializer can be constructed, will simply + * return serializer as-is. + *

+ * Default implementation just returns serializer as-is, + * indicating that no unwrapped variant exists + * + * @param unwrapper Name transformation to use to convert between names + * of unwrapper properties + */ + public JsonSerializer unwrappingSerializer(NameTransformer unwrapper) { + return this; + } + + /** + * Method that can be called to try to replace serializer this serializer + * delegates calls to. If not supported (either this serializer does not + * delegate anything; or it does not want any changes), should either + * throw {@link UnsupportedOperationException} (if operation does not + * make sense or is not allowed); or return this serializer as is. + * + * @since 2.1 + */ + public JsonSerializer replaceDelegatee(JsonSerializer delegatee) { + throw new UnsupportedOperationException(); + } + + /** + * Mutant factory method that is called if contextual configuration indicates that + * a specific filter (as specified by filterId) is to be used for + * serialization. + *

+ * Default implementation simply returns this; sub-classes that do support + * filtering will need to create and return new instance if filter changes. + * + * @since 2.6 + */ + public JsonSerializer withFilterId(Object filterId) { + return this; + } + + /* + /********************************************************** + /* Serialization methods + /********************************************************** + */ + + /** + * Method that can be called to ask implementation to serialize + * values of type this serializer handles. + * + * @param value Value to serialize; can not be null. + * @param gen Generator used to output resulting Json content + * @param serializers Provider that can be used to get serializers for + * serializing Objects value contains, if any. + */ + public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers) + throws IOException, JsonProcessingException; + + /** + * Method that can be called to ask implementation to serialize + * values of type this serializer handles, using specified type serializer + * for embedding necessary type information. + *

+ * Default implementation will throw {@link UnsupportedOperationException} + * to indicate that proper type handling needs to be implemented. + *

+ * For simple datatypes written as a single scalar value (JSON String, Number, Boolean), + * implementation would look like: + *

+     *  // note: method to call depends on whether this type is serialized as JSON scalar, object or Array!
+     *  typeSer.writeTypePrefixForScalar(value, gen);
+     *  serialize(value, gen, provider);
+     *  typeSer.writeTypeSuffixForScalar(value, gen);
+     *
+ * and implementations for type serialized as JSON Arrays or Objects would differ slightly, + * as START-ARRAY/END-ARRAY and + * START-OBJECT/END-OBJECT pairs + * need to be properly handled with respect to serializing of contents. + * + * @param value Value to serialize; can not be null. + * @param gen Generator used to output resulting Json content + * @param serializers Provider that can be used to get serializers for + * serializing Objects value contains, if any. + * @param typeSer Type serializer to use for including type information + */ + public void serializeWithType(T value, JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) + throws IOException + { + Class clz = handledType(); + if (clz == null) { + clz = value.getClass(); + } + throw serializers.mappingException("Type id handling not implemented for type %s (by serializer of type %s)", + clz.getName(), getClass().getName()); + } + + /* + /********************************************************** + /* Other accessors + /********************************************************** + */ + + /** + * Method for accessing type of Objects this serializer can handle. + * Note that this information is not guaranteed to be exact -- it + * may be a more generic (super-type) -- but it should not be + * incorrect (return a non-related type). + *

+ * Default implementation will return null, which essentially means + * same as returning Object.class would; that is, that + * nothing is known about handled type. + *

+ */ + public Class handledType() { return null; } + + /** + * Method called to check whether given serializable value is + * considered "empty" value (for purposes of suppressing serialization + * of empty values). + *

+ * Default implementation will consider only null values to be empty. + * + * @since 2.0 + * + * @deprecated Since 2.5 Use {@link #isEmpty(SerializerProvider, Object)} instead; + * will be removed from 2.8 + */ + @Deprecated + public boolean isEmpty(T value) { + return isEmpty(null, value); + } + + /** + * Method called to check whether given serializable value is + * considered "empty" value (for purposes of suppressing serialization + * of empty values). + *

+ * Default implementation will consider only null values to be empty. + *

+ * NOTE: replaces {@link #isEmpty(Object)}, deprecated in 2.5 + * + * @since 2.5 + */ + public boolean isEmpty(SerializerProvider provider, T value) { + return (value == null); + } + + /** + * Method that can be called to see whether this serializer instance + * will use Object Id to handle cyclic references. + */ + public boolean usesObjectId() { + return false; + } + + /** + * Accessor for checking whether this serializer is an + * "unwrapping" serializer; this is necessary to know since + * it may also require caller to suppress writing of the + * leading property name. + */ + public boolean isUnwrappingSerializer() { + return false; + } + + /** + * Accessor that can be used to determine if this serializer uses + * another serializer for actual serialization, by delegating + * calls. If so, will return immediate delegate (which itself may + * delegate to further serializers); otherwise will return null. + * + * @return Serializer this serializer delegates calls to, if null; + * null otherwise. + * + * @since 2.1 + */ + public JsonSerializer getDelegatee() { + return null; + } + + /** + * Accessor for iterating over logical properties that the type + * handled by this serializer has, from serialization perspective. + * Actual type of properties, if any, will be + * {@link com.fasterxml.jackson.databind.ser.BeanPropertyWriter}. + * Of standard Jackson serializers, only {@link com.fasterxml.jackson.databind.ser.BeanSerializer} + * exposes properties. + * + * @since 2.6 + */ + public Iterator properties() { + return ClassUtil.emptyIterator(); + } + + /* + /********************************************************** + /* Default JsonFormatVisitable implementation + /********************************************************** + */ + + /** + * Default implementation simply calls {@link JsonFormatVisitorWrapper#expectAnyFormat(JavaType)}. + * + * @since 2.1 + */ + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType type) + throws JsonMappingException + { + if (visitor != null) visitor.expectAnyFormat(type); + } + + /* + /********************************************************** + /* Helper class(es) + /********************************************************** + */ + + /** + * This marker class is only to be used with annotations, to + * indicate that no serializer is configured. + *

+ * Specifically, this class is to be used as the marker for + * annotation {@link com.fasterxml.jackson.databind.annotation.JsonSerialize}. + */ + public abstract static class None + extends JsonSerializer { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/KeyDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/KeyDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/KeyDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,29 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; + +/** + * Abstract class that defines API used for deserializing JSON content + * field names into Java Map keys. These deserializers are only used + * if the Map key class is not String or Object. + */ +public abstract class KeyDeserializer +{ + /** + * Method called to deserialize a {@link java.util.Map} key from JSON property name. + */ + public abstract Object deserializeKey(String key, DeserializationContext ctxt) + throws IOException, JsonProcessingException; + + /** + * This marker class is only to be used with annotations, to + * indicate that no deserializer is configured. + *

+ * Specifically, this class is to be used as the marker for + * annotation {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}. + */ + public abstract static class None + extends KeyDeserializer { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MapperFeature.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MapperFeature.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MapperFeature.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,408 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.cfg.ConfigFeature; + +/** + * Enumeration that defines simple on/off features to set + * for {@link ObjectMapper}, and accessible (but not changeable) + * via {@link ObjectReader} and {@link ObjectWriter} (as well as + * through various convenience methods through context objects). + *

+ * Note that in addition to being only mutable via {@link ObjectMapper}, + * changes only take effect when done before any serialization or + * deserialization calls -- that is, caller must follow + * "configure-then-use" pattern. + */ +public enum MapperFeature implements ConfigFeature +{ + /* + /****************************************************** + /* Introspection features + /****************************************************** + */ + + /** + * Feature that determines whether annotation introspection + * is used for configuration; if enabled, configured + * {@link AnnotationIntrospector} will be used: if disabled, + * no annotations are considered. + *

+ * Feature is enabled by default. + */ + USE_ANNOTATIONS(true), + + /** + * Feature that determines whether "creator" methods are + * automatically detected by consider public constructors, + * and static single argument methods with name "valueOf". + * If disabled, only methods explicitly annotated are considered + * creator methods (except for the no-arg default constructor which + * is always considered a factory method). + *

+ * Note that this feature has lower precedence than per-class + * annotations, and is only used if there isn't more granular + * configuration available. + *

+ * Feature is enabled by default. + */ + AUTO_DETECT_CREATORS(true), + + /** + * Feature that determines whether non-static fields are recognized as + * properties. + * If yes, then all public member fields + * are considered as properties. If disabled, only fields explicitly + * annotated are considered property fields. + *

+ * Note that this feature has lower precedence than per-class + * annotations, and is only used if there isn't more granular + * configuration available. + *

+ * Feature is enabled by default. + */ + AUTO_DETECT_FIELDS(true), + + /** + * Feature that determines whether regular "getter" methods are + * automatically detected based on standard Bean naming convention + * or not. If yes, then all public zero-argument methods that + * start with prefix "get" + * are considered as getters. + * If disabled, only methods explicitly annotated are considered getters. + *

+ * Note that since version 1.3, this does NOT include + * "is getters" (see {@link #AUTO_DETECT_IS_GETTERS} for details) + *

+ * Note that this feature has lower precedence than per-class + * annotations, and is only used if there isn't more granular + * configuration available. + *

+ * Feature is enabled by default. + */ + AUTO_DETECT_GETTERS(true), + + /** + * Feature that determines whether "is getter" methods are + * automatically detected based on standard Bean naming convention + * or not. If yes, then all public zero-argument methods that + * start with prefix "is", and whose return type is boolean + * are considered as "is getters". + * If disabled, only methods explicitly annotated are considered getters. + *

+ * Note that this feature has lower precedence than per-class + * annotations, and is only used if there isn't more granular + * configuration available. + *

+ * Feature is enabled by default. + */ + AUTO_DETECT_IS_GETTERS(true), + + /** + * Feature that determines whether "setter" methods are + * automatically detected based on standard Bean naming convention + * or not. If yes, then all public one-argument methods that + * start with prefix "set" + * are considered setters. If disabled, only methods explicitly + * annotated are considered setters. + *

+ * Note that this feature has lower precedence than per-class + * annotations, and is only used if there isn't more granular + * configuration available. + *

+ * Feature is enabled by default. + */ + AUTO_DETECT_SETTERS(true), + + /** + * Feature that determines whether getters (getter methods) + * can be auto-detected if there is no matching mutator (setter, + * constructor parameter or field) or not: if set to true, + * only getters that match a mutator are auto-discovered; if + * false, all auto-detectable getters can be discovered. + *

+ * Feature is disabled by default. + */ + REQUIRE_SETTERS_FOR_GETTERS(false), + + /** + * Feature that determines whether otherwise regular "getter" + * methods (but only ones that handle Collections and Maps, + * not getters of other type) + * can be used for purpose of getting a reference to a Collection + * and Map to modify the property, without requiring a setter + * method. + * This is similar to how JAXB framework sets Collections and + * Maps: no setter is involved, just setter. + *

+ * Note that such getters-as-setters methods have lower + * precedence than setters, so they are only used if no + * setter is found for the Map/Collection property. + *

+ * Feature is enabled by default. + */ + USE_GETTERS_AS_SETTERS(true), + + /** + * Feature that determines whether method and field access + * modifier settings can be overridden when accessing + * properties. If enabled, method + * {@link java.lang.reflect.AccessibleObject#setAccessible} + * may be called to enable access to otherwise unaccessible objects. + *

+ * Note that this setting may have significant performance implications, + * since access override helps remove costly access checks on each + * and every Reflection access. If you are considering disabling + * this feature, be sure to verify performance consequences if usage + * is performance sensitive. + * Also note that performance effects vary between Java platforms + * (JavaSE vs Android, for example), as well as JDK versions: older + * versions seemed to have more significant performance difference. + *

+ * Conversely, on some platforms, it may be necessary to disable this feature + * as platform does not allow such calls. For example, when developing + * Applets (or other Java code that runs on tightly restricted sandbox), + * it may be necessary to disable the feature regardless of performance effects. + *

+ * Feature is enabled by default. + */ + CAN_OVERRIDE_ACCESS_MODIFIERS(true), + + /** + * Feature that determines that forces call to + * {@link java.lang.reflect.AccessibleObject#setAccessible} even for + * public accessors -- that is, even if no such call is + * needed from functionality perspective -- if call is allowed + * (that is, {@link #CAN_OVERRIDE_ACCESS_MODIFIERS} is set to true). + * The main reason to enable this feature is possible performance + * improvement as JDK does not have to perform access checks; these + * checks are otherwise made for all accessors, including public ones, + * and may result in slower Reflection calls. Exact impact (if any) + * depends on Java platform (Java SE, Android) as well as JDK version. + *

+ * Feature is enabled by default, for legacy reasons (it was the behavior + * until 2.6) + * + * @since 2.7 + */ + OVERRIDE_PUBLIC_ACCESS_MODIFIERS(true), + + /** + * Feature that determines whether member mutators (fields and + * setters) may be "pulled in" even if they are not visible, + * as long as there is a visible accessor (getter or field) with same name. + * For example: field "value" may be inferred as mutator, + * if there is visible or explicitly marked getter "getValue()". + * If enabled, inferring is enabled; otherwise (disabled) only visible and + * explicitly annotated accessors are ever used. + *

+ * Note that 'getters' are never inferred and need to be either visible (including + * bean-style naming) or explicitly annotated. + *

+ * Feature is enabled by default. + * + * @since 2.2 + */ + INFER_PROPERTY_MUTATORS(true), + + /** + * Feature that determines whether member fields declared as 'final' may + * be auto-detected to be used mutators (used to change value of the logical + * property) or not. If enabled, 'final' access modifier has no effect, and + * such fields may be detected according to usual visibility and inference + * rules; if disabled, such fields are NOT used as mutators except if + * explicitly annotated for such use. + *

+ * Feature is enabled by default, for backwards compatibility reasons. + * + * @since 2.2 + */ + ALLOW_FINAL_FIELDS_AS_MUTATORS(true), + + /** + * Feature that determines for transient modifier for fields + * is handled: if disabled, it is only taken to mean exclusion of + *

+ * Feature is disabled by default, meaning that existence of `transient` + * for a field does not necessarily lead to ignoral of getters or setters. + * + * @since 2.6 + */ + PROPAGATE_TRANSIENT_MARKER(false), + + /* + /****************************************************** + /* Type-handling features + /****************************************************** + */ + + /** + * Feature that determines whether the type detection for + * serialization should be using actual dynamic runtime type, + * or declared static type. + * Note that deserialization always uses declared static types + * since no runtime types are available (as we are creating + * instances after using type information). + *

+ * This global default value can be overridden at class, method + * or field level by using {@link JsonSerialize#typing} annotation + * property. + *

+ * Feature is disabled by default which means that dynamic runtime types + * are used (instead of declared static types) for serialization. + */ + USE_STATIC_TYPING(false), + + /* + /****************************************************** + /* View-related features + /****************************************************** + */ + + /** + * Feature that determines whether properties that have no view + * annotations are included in JSON serialization views (see + * {@link com.fasterxml.jackson.annotation.JsonView} for more + * details on JSON Views). + * If enabled, non-annotated properties will be included; + * when disabled, they will be excluded. So this feature + * changes between "opt-in" (feature disabled) and + * "opt-out" (feature enabled) modes. + *

+ * Default value is enabled, meaning that non-annotated + * properties are included in all views if there is no + * {@link com.fasterxml.jackson.annotation.JsonView} annotation. + *

+ * Feature is enabled by default. + */ + DEFAULT_VIEW_INCLUSION(true), + + /* + /****************************************************** + /* Generic output features + /****************************************************** + */ + + /** + * Feature that defines default property serialization order used + * for POJO fields (note: does not apply to {@link java.util.Map} + * serialization!): + * if enabled, default ordering is alphabetic (similar to + * how {@link com.fasterxml.jackson.annotation.JsonPropertyOrder#alphabetic()} + * works); if disabled, order is unspecified (based on what JDK gives + * us, which may be declaration order, but is not guaranteed). + *

+ * Note that this is just the default behavior, and can be overridden by + * explicit overrides in classes (for example with + * {@link com.fasterxml.jackson.annotation.JsonPropertyOrder} annotation) + *

+ * Feature is disabled by default. + */ + SORT_PROPERTIES_ALPHABETICALLY(false), + + /* + /****************************************************** + /* Name-related features + /****************************************************** + */ + /** + * Feature that will allow for more forgiving deserialization of incoming JSON. + * If enabled, the bean properties will be matched using their lower-case equivalents, + * meaning that any case-combination (incoming and matching names are canonicalized + * by lower-casing) should work. + *

+ * Note that there is additional performance overhead since incoming property + * names need to be lower-cased before comparison, for cases where there are upper-case + * letters. Overhead for names that are already lower-case should be negligible however. + *

+ * Feature is disabled by default. + * + * @since 2.5 + */ + ACCEPT_CASE_INSENSITIVE_PROPERTIES(false), + + /** + * Feature that can be enabled to make property names be + * overridden by wrapper name (usually detected with annotations + * as defined by {@link AnnotationIntrospector#findWrapperName}. + * If enabled, all properties that have associated non-empty Wrapper + * name will use that wrapper name instead of property name. + * If disabled, wrapper name is only used for wrapping (if anything). + *

+ * Feature is disabled by default. + * + * @since 2.1 + */ + USE_WRAPPER_NAME_AS_PROPERTY_NAME(false), + + /** + * Feature that may be enabled to enforce strict compatibility with + * Bean name introspection, instead of slightly different mechanism + * Jackson defaults to. + * Specific difference is that Jackson always lower cases leading upper-case + * letters, so "getURL()" becomes "url" property; whereas standard Bean + * naming only lower-cases the first letter if it is NOT followed by + * another upper-case letter (so "getURL()" would result in "URL" property). + *

+ * Feature is disabled by default for backwards compatibility purposes: earlier + * Jackson versions used Jackson's own mechanism. + * + * @since 2.5 + */ + USE_STD_BEAN_NAMING(false), + + /** + * Feature that when enabled will allow explicitly named properties (i.e., fields or methods + * annotated with {@link com.fasterxml.jackson.annotation.JsonProperty}("explicitName")) to + * be re-named by a {@link PropertyNamingStrategy}, if one is configured. + *

+ * Feature is disabled by default. + * + * @since 2.7 + */ + ALLOW_EXPLICIT_PROPERTY_RENAMING(false), + + /* + /****************************************************** + /* Other features + /****************************************************** + */ + + /** + * Feature that determines whether multiple registrations of same module + * should be ignored or not; if enabled, only the first registration call + * results in module being called, and possible duplicate calls are silently + * ignored; if disabled, no checking is done and all registration calls are + * dispatched to module. + *

+ * Definition of "same module" is based on using {@link Module#getTypeId()}; + * modules with same non-null type id are considered same for + * purposes of duplicate registration. This also avoids having to keep track + * of actual module instances; only ids will be kept track of (and only if + * this feature is enabled). + *

+ * Feature is enabled by default. + * + * @since 2.5 + */ + IGNORE_DUPLICATE_MODULE_REGISTRATIONS(true) + + ; + + private final boolean _defaultState; + private final int _mask; + + private MapperFeature(boolean defaultState) { + _defaultState = defaultState; + _mask = (1 << ordinal()); + } + + @Override + public boolean enabledByDefault() { return _defaultState; } + + @Override + public int getMask() { return _mask; } + + @Override + public boolean enabledIn(int flags) { return (flags & _mask) != 0; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MappingIterator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MappingIterator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MappingIterator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,416 @@ +package com.fasterxml.jackson.databind; + +import java.io.Closeable; +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; + +/** + * Iterator exposed by {@link ObjectMapper} when binding sequence of + * objects. Extension is done to allow more convenient exposing of + * {@link IOException} (which basic {@link Iterator} does not expose) + */ +public class MappingIterator implements Iterator, Closeable +{ + protected final static MappingIterator EMPTY_ITERATOR = + new MappingIterator(null, null, null, null, false, null); + + /* + /********************************************************** + /* State constants + /********************************************************** + */ + + /** + * State in which iterator is closed + */ + protected final static int STATE_CLOSED = 0; + + /** + * State in which value read failed + */ + protected final static int STATE_NEED_RESYNC = 1; + + /** + * State in which no recovery is needed, but "hasNextValue()" needs + * to be called first + */ + protected final static int STATE_MAY_HAVE_VALUE = 2; + + /** + * State in which "hasNextValue()" has been succesfully called + * and deserializer can be called to fetch value + */ + protected final static int STATE_HAS_VALUE = 3; + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Type to bind individual elements to. + */ + protected final JavaType _type; + + /** + * Context for deserialization, needed to pass through to deserializer + */ + protected final DeserializationContext _context; + + /** + * Deserializer for individual element values. + */ + protected final JsonDeserializer _deserializer; + + /** + * Underlying parser used for reading content to bind. Initialized + * as not null but set as null when + * iterator is closed, to denote closing. + */ + protected final JsonParser _parser; + + /** + * Context to resynchronize to, in case an exception is encountered + * but caller wants to try to read more elements. + */ + protected final JsonStreamContext _seqContext; + + /** + * If not null, "value to update" instead of creating a new instance + * for each call. + */ + protected final T _updatedValue; + + /** + * Flag that indicates whether input {@link JsonParser} should be closed + * when we are done or not; generally only called when caller did not + * pass JsonParser. + */ + protected final boolean _closeParser; + + /* + /********************************************************** + /* Parsing state + /********************************************************** + */ + + /** + * State of the iterator + */ + protected int _state; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + /** + * @param managedParser Whether we "own" the {@link JsonParser} passed or not: + * if true, it was created by {@link ObjectReader} and code here needs to + * close it; if false, it was passed by calling code and should not be + * closed by iterator. + */ + @SuppressWarnings("unchecked") + protected MappingIterator(JavaType type, JsonParser p, DeserializationContext ctxt, + JsonDeserializer deser, + boolean managedParser, Object valueToUpdate) + { + _type = type; + _parser = p; + _context = ctxt; + _deserializer = (JsonDeserializer) deser; + _closeParser = managedParser; + if (valueToUpdate == null) { + _updatedValue = null; + } else { + _updatedValue = (T) valueToUpdate; + } + + /* Ok: one more thing; we may have to skip START_ARRAY, assuming + * "wrapped" sequence; but this is ONLY done for 'managed' parsers + * and never if JsonParser was directly passed by caller (if it + * was, caller must have either positioned it over first token of + * the first element, or cleared the START_ARRAY token explicitly). + * Note, however, that we do not try to guess whether this could be + * an unwrapped sequence of arrays/Lists: we just assume it is wrapped; + * and if not, caller needs to hand us JsonParser instead, pointing to + * the first token of the first element. + */ + if (p == null) { // can this occur? + _seqContext = null; + _state = STATE_CLOSED; + } else { + JsonStreamContext sctxt = p.getParsingContext(); + if (managedParser && p.isExpectedStartArrayToken()) { + // If pointing to START_ARRAY, context should be that ARRAY + p.clearCurrentToken(); + } else { + // regardless, recovery context should be whatever context we have now, + // with sole exception of pointing to a start marker, in which case it's + // the parent + JsonToken t = p.getCurrentToken(); + if ((t == JsonToken.START_OBJECT) || (t == JsonToken.START_ARRAY)) { + sctxt = sctxt.getParent(); + } + } + _seqContext = sctxt; + _state = STATE_MAY_HAVE_VALUE; + } + } + + @SuppressWarnings("unchecked") + protected static MappingIterator emptyIterator() { + return (MappingIterator) EMPTY_ITERATOR; + } + + /* + /********************************************************** + /* Basic iterator impl + /********************************************************** + */ + + @Override + public boolean hasNext() + { + try { + return hasNextValue(); + } catch (JsonMappingException e) { + return (Boolean) _handleMappingException(e); + } catch (IOException e) { + return (Boolean) _handleIOException(e); + } + } + + @Override + public T next() + { + try { + return nextValue(); + } catch (JsonMappingException e) { + throw new RuntimeJsonMappingException(e.getMessage(), e); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + if (_state != STATE_CLOSED) { + _state = STATE_CLOSED; + if (_parser != null) { + _parser.close(); + } + } + } + + /* + /********************************************************** + /* Extended API, iteration + /********************************************************** + */ + + + /* + */ + + /** + * Equivalent of {@link #next} but one that may throw checked + * exceptions from Jackson due to invalid input. + */ + public boolean hasNextValue() throws IOException + { + switch (_state) { + case STATE_CLOSED: + return false; + case STATE_NEED_RESYNC: + _resync(); + // fall-through + case STATE_MAY_HAVE_VALUE: + JsonToken t = _parser.getCurrentToken(); + if (t == null) { // un-initialized or cleared; find next + t = _parser.nextToken(); + // If EOF, no more, or if we hit END_ARRAY (although we don't clear the token). + if (t == null || t == JsonToken.END_ARRAY) { + _state = STATE_CLOSED; + if (_closeParser && (_parser != null)) { + _parser.close(); + } + return false; + } + } + _state = STATE_HAS_VALUE; + return true; + case STATE_HAS_VALUE: + // fall through + } + return true; + } + + public T nextValue() throws IOException + { + switch (_state) { + case STATE_CLOSED: + return _throwNoSuchElement(); + case STATE_NEED_RESYNC: // fall-through, will do re-sync + case STATE_MAY_HAVE_VALUE: + if (!hasNextValue()) { + return _throwNoSuchElement(); + } + break; + case STATE_HAS_VALUE: + break; + } + + int nextState = STATE_NEED_RESYNC; + try { + T value; + if (_updatedValue == null) { + value = _deserializer.deserialize(_parser, _context); + } else{ + _deserializer.deserialize(_parser, _context, _updatedValue); + value = _updatedValue; + } + nextState = STATE_MAY_HAVE_VALUE; + return value; + } finally { + _state = nextState; + /* 24-Mar-2015, tatu: As per [#733], need to mark token consumed no + * matter what, to avoid infinite loop for certain failure cases. + * For 2.6 need to improve further. + */ + _parser.clearCurrentToken(); + } + } + + /** + * Convenience method for reading all entries accessible via + * this iterator; resulting container will be a {@link java.util.ArrayList}. + * + * @return List of entries read + * + * @since 2.2 + */ + public List readAll() throws IOException { + return readAll(new ArrayList()); + } + + /** + * Convenience method for reading all entries accessible via + * this iterator + * + * @return List of entries read (same as passed-in argument) + * + * @since 2.2 + */ + public > L readAll(L resultList) throws IOException + { + while (hasNextValue()) { + resultList.add(nextValue()); + } + return resultList; + } + + /** + * Convenience method for reading all entries accessible via + * this iterator + * + * @since 2.5 + */ + public > C readAll(C results) throws IOException + { + while (hasNextValue()) { + results.add(nextValue()); + } + return results; + } + + /* + /********************************************************** + /* Extended API, accessors + /********************************************************** + */ + + /** + * Accessor for getting underlying parser this iterator uses. + * + * @since 2.2 + */ + public JsonParser getParser() { + return _parser; + } + + /** + * Accessor for accessing {@link FormatSchema} that the underlying parser + * (as per {@link #getParser}) is using, if any; only parser of schema-aware + * formats use schemas. + * + * @since 2.2 + */ + public FormatSchema getParserSchema() { + return _parser.getSchema(); + } + + /** + * Convenience method, functionally equivalent to: + * + * iterator.getParser().getCurrentLocation() + * + * + * @return Location of the input stream of the underlying parser + * + * @since 2.2.1 + */ + public JsonLocation getCurrentLocation() { + return _parser.getCurrentLocation(); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected void _resync() throws IOException + { + final JsonParser p = _parser; + // First, a quick check to see if we might have been lucky and no re-sync needed + if (p.getParsingContext() == _seqContext) { + return; + } + + while (true) { + JsonToken t = p.nextToken(); + if ((t == JsonToken.END_ARRAY) || (t == JsonToken.END_OBJECT)) { + if (p.getParsingContext() == _seqContext) { + p.clearCurrentToken(); + return; + } + } else if ((t == JsonToken.START_ARRAY) || (t == JsonToken.START_OBJECT)) { + p.skipChildren(); + } else if (t == null) { + return; + } + } + } + + protected R _throwNoSuchElement() { + throw new NoSuchElementException(); + } + + protected R _handleMappingException(JsonMappingException e) { + throw new RuntimeJsonMappingException(e.getMessage(), e); + } + + protected R _handleIOException(IOException e) { + throw new RuntimeException(e.getMessage(), e); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MappingJsonFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MappingJsonFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/MappingJsonFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,88 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.format.InputAccessor; +import com.fasterxml.jackson.core.format.MatchStrength; + +/** + * Sub-class of {@link JsonFactory} that will create a proper + * {@link ObjectCodec} to allow seam-less conversions between + * JSON content and Java objects (POJOs). + * The only addition to regular {@link JsonFactory} currently + * is that {@link ObjectMapper} is constructed and passed as + * the codec to use. + */ +public class MappingJsonFactory + extends JsonFactory +{ + private static final long serialVersionUID = -1; // since 2.7 + + public MappingJsonFactory() + { + this(null); + } + + public MappingJsonFactory(ObjectMapper mapper) + { + super(mapper); + if (mapper == null) { + setCodec(new ObjectMapper(this)); + } + } + + public MappingJsonFactory(JsonFactory src, ObjectMapper mapper) + { + super(src, mapper); + if (mapper == null) { + setCodec(new ObjectMapper(this)); + } + } + + /** + * We'll override the method to return more specific type; co-variance + * helps here + */ + @Override + public final ObjectMapper getCodec() { return (ObjectMapper) _objectCodec; } + + // @since 2.1 + @Override + public JsonFactory copy() + { + _checkInvalidCopy(MappingJsonFactory.class); + // note: as with base class, must NOT copy mapper reference + return new MappingJsonFactory(this, null); + } + + /* + /********************************************************** + /* Format detection functionality (since 1.8) + /********************************************************** + */ + + /** + * Sub-classes need to override this method + */ + @Override + public String getFormatName() + { + /* since non-JSON factories typically should not extend this class, + * let's just always return JSON as name. + */ + return FORMAT_NAME_JSON; + } + + /** + * Sub-classes need to override this method + */ + @Override + public MatchStrength hasFormat(InputAccessor acc) throws IOException + { + if (getClass() == MappingJsonFactory.class) { + return hasJSONFormat(acc); + } + return null; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/Module.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/Module.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/Module.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,310 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.deser.Deserializers; +import com.fasterxml.jackson.databind.deser.KeyDeserializers; +import com.fasterxml.jackson.databind.deser.ValueInstantiators; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.type.TypeModifier; + +/** + * Simple interface for extensions that can be registered with {@link ObjectMapper} + * to provide a well-defined set of extensions to default functionality; such as + * support for new data types. + */ +public abstract class Module + implements Versioned +{ + /* + /********************************************************** + /* Simple accessors + /********************************************************** + */ + + /** + * Method that returns a display that can be used by Jackson + * for informational purposes, as well as in associating extensions with + * module that provides them. + */ + public abstract String getModuleName(); + + /** + * Method that returns version of this module. Can be used by Jackson for + * informational purposes. + */ + @Override + public abstract Version version(); + + /** + * Method that returns an id that may be used to determine if two {@link Module} + * instances are considered to be of same type, for purpose of preventing + * multiple registrations of "same type of" module + * (see {@link com.fasterxml.jackson.databind.MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS}) + * If `null` is returned, every instance is considered unique. + * If non-null value is returned, equality of id Objects is used to check whether + * modules should be considered to be "of same type" + *

+ * Default implementation returns value of class name ({@link Class#getName}). + * + * @since 2.5 + */ + public Object getTypeId() { + return getClass().getName(); + } + + /* + /********************************************************** + /* Life-cycle: registration + /********************************************************** + */ + + /** + * Method called by {@link ObjectMapper} when module is registered. + * It is called to let module register functionality it provides, + * using callback methods passed-in context object exposes. + */ + public abstract void setupModule(SetupContext context); + + /* + /********************************************************** + /* Helper types + /********************************************************** + */ + + /** + * Interface Jackson exposes to modules for purpose of registering + * extended functionality. + * Usually implemented by {@link ObjectMapper}, but modules should + * NOT rely on this -- if they do require access to mapper instance, + * they need to call {@link SetupContext#getOwner} method. + */ + public static interface SetupContext + { + /* + /********************************************************** + /* Simple accessors + /********************************************************** + */ + + /** + * Method that returns version information about {@link ObjectMapper} + * that implements this context. Modules can use this to choose + * different settings or initialization order; or even decide to fail + * set up completely if version is compatible with module. + */ + public Version getMapperVersion(); + + /** + * Fallback access method that allows modules to refer to the + * {@link ObjectMapper} that provided this context. + * It should NOT be needed by most modules; and ideally should + * not be used -- however, there may be cases where this may + * be necessary due to various design constraints. + *

+ * NOTE: use of this method is discouraged, as it allows access to + * things Modules typically should not modify. It is included, however, + * to allow access to new features in cases where Module API + * has not yet been extended, or there are oversights. + *

+ * Return value is chosen to not leak dependency to {@link ObjectMapper}; + * however, instance will always be of that type. + * This is why return value is declared generic, to allow caller to + * specify context to often avoid casting. + * + * @since 2.0 + */ + public C getOwner(); + + /** + * Accessor for finding {@link TypeFactory} that is currently configured + * by the context. + *

+ * NOTE: since it is possible that other modules might change or replace + * TypeFactory, use of this method adds order-dependency for registrations. + * + * @since 2.0 + */ + public TypeFactory getTypeFactory(); + + public boolean isEnabled(MapperFeature f); + + public boolean isEnabled(DeserializationFeature f); + + public boolean isEnabled(SerializationFeature f); + + public boolean isEnabled(JsonFactory.Feature f); + + public boolean isEnabled(JsonParser.Feature f); + + public boolean isEnabled(JsonGenerator.Feature f); + + /* + /********************************************************** + /* Handler registration; serializers/deserializers + /********************************************************** + */ + + /** + * Method that module can use to register additional deserializers to use for + * handling types. + * + * @param d Object that can be called to find deserializer for types supported + * by module (null returned for non-supported types) + */ + public void addDeserializers(Deserializers d); + + /** + * Method that module can use to register additional deserializers to use for + * handling Map key values (which are separate from value deserializers because + * they are always serialized from String values) + */ + public void addKeyDeserializers(KeyDeserializers s); + + /** + * Method that module can use to register additional serializers to use for + * handling types. + * + * @param s Object that can be called to find serializer for types supported + * by module (null returned for non-supported types) + */ + public void addSerializers(Serializers s); + + /** + * Method that module can use to register additional serializers to use for + * handling Map key values (which are separate from value serializers because + * they must write JsonToken.FIELD_NAME instead of String value). + */ + public void addKeySerializers(Serializers s); + + /* + /********************************************************** + /* Handler registration; other + /********************************************************** + */ + + /** + * Method that module can use to register additional modifier objects to + * customize configuration and construction of bean deserializers. + * + * @param mod Modifier to register + */ + public void addBeanDeserializerModifier(BeanDeserializerModifier mod); + + /** + * Method that module can use to register additional modifier objects to + * customize configuration and construction of bean serializers. + * + * @param mod Modifier to register + */ + public void addBeanSerializerModifier(BeanSerializerModifier mod); + + /** + * Method that module can use to register additional + * {@link AbstractTypeResolver} instance, to handle resolution of + * abstract to concrete types (either by defaulting, or by materializing). + * + * @param resolver Resolver to add. + */ + public void addAbstractTypeResolver(AbstractTypeResolver resolver); + + /** + * Method that module can use to register additional + * {@link TypeModifier} instance, which can augment {@link com.fasterxml.jackson.databind.JavaType} + * instances constructed by {@link com.fasterxml.jackson.databind.type.TypeFactory}. + * + * @param modifier to add + */ + public void addTypeModifier(TypeModifier modifier); + + /** + * Method that module can use to register additional {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s, + * by adding {@link ValueInstantiators} object that gets called when + * instantatiator is needed by a deserializer. + * + * @param instantiators Object that can provide {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s for + * constructing POJO values during deserialization + */ + public void addValueInstantiators(ValueInstantiators instantiators); + + /** + * Method for replacing the default class introspector with a derived class that + * overrides specific behavior. + * + * @param ci Derived class of ClassIntrospector with overriden behavior + * + * @since 2.2 + */ + public void setClassIntrospector(ClassIntrospector ci); + + /** + * Method for registering specified {@link AnnotationIntrospector} as the highest + * priority introspector (will be chained with existing introspector(s) which + * will be used as fallbacks for cases this introspector does not handle) + * + * @param ai Annotation introspector to register. + */ + public void insertAnnotationIntrospector(AnnotationIntrospector ai); + + /** + * Method for registering specified {@link AnnotationIntrospector} as the lowest + * priority introspector, chained with existing introspector(s) and called + * as fallback for cases not otherwise handled. + * + * @param ai Annotation introspector to register. + */ + public void appendAnnotationIntrospector(AnnotationIntrospector ai); + + /** + * Method for registering specified classes as subtypes (of supertype(s) + * they have) + */ + public void registerSubtypes(Class... subtypes); + + /** + * Method for registering specified classes as subtypes (of supertype(s) + * they have), using specified type names. + */ + public void registerSubtypes(NamedType... subtypes); + + /** + * Method used for defining mix-in annotations to use for augmenting + * specified class or interface. + * All annotations from + * mixinSource are taken to override annotations + * that target (or its supertypes) has. + *

+ * Note: mix-ins are registered both for serialization and deserialization + * (which can be different internally). + *

+ * Note: currently only one set of mix-in annotations can be defined for + * a single class; so if multiple modules register mix-ins, highest + * priority one (last one registered) will have priority over other modules. + * + * @param target Class (or interface) whose annotations to effectively override + * @param mixinSource Class (or interface) whose annotations are to + * be "added" to target's annotations, overriding as necessary + */ + public void setMixInAnnotations(Class target, Class mixinSource); + + /** + * Add a deserialization problem handler + * + * @param handler The deserialization problem handler + */ + public void addDeserializationProblemHandler(DeserializationProblemHandler handler); + + /** + * Method that may be used to override naming strategy that is used + * by {@link ObjectMapper}. + * + * @since 2.3 + */ + public void setNamingStrategy(PropertyNamingStrategy naming); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectMapper.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectMapper.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectMapper.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,3929 @@ +package com.fasterxml.jackson.databind; + +import java.io.*; +import java.lang.reflect.Type; +import java.net.URL; +import java.text.DateFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.io.SegmentedStringWriter; +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.core.util.*; +import com.fasterxml.jackson.databind.cfg.BaseSettings; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.*; +import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; +import com.fasterxml.jackson.databind.node.*; +import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.type.*; +import com.fasterxml.jackson.databind.util.RootNameLookup; +import com.fasterxml.jackson.databind.util.StdDateFormat; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * ObjectMapper provides functionality for reading and writing JSON, + * either to and from basic POJOs (Plain Old Java Objects), or to and from + * a general-purpose JSON Tree Model ({@link JsonNode}), as well as + * related functionality for performing conversions. + * It is also highly customizable to work both with different styles of JSON + * content, and to support more advanced Object concepts such as + * polymorphism and Object identity. + * ObjectMapper also acts as a factory for more advanced {@link ObjectReader} + * and {@link ObjectWriter} classes. + * Mapper (and {@link ObjectReader}s, {@link ObjectWriter}s it constructs) will + * use instances of {@link JsonParser} and {@link JsonGenerator} + * for implementing actual reading/writing of JSON. + * Note that although most read and write methods are exposed through this class, + * some of the functionality is only exposed via {@link ObjectReader} and + * {@link ObjectWriter}: specifically, reading/writing of longer sequences of + * values is only available through {@link ObjectReader#readValues(InputStream)} + * and {@link ObjectWriter#writeValues(OutputStream)}. + *

+Simplest usage is of form: +

+  final ObjectMapper mapper = new ObjectMapper(); // can use static singleton, inject: just make sure to reuse!
+  MyValue value = new MyValue();
+  // ... and configure
+  File newState = new File("my-stuff.json");
+  mapper.writeValue(newState, value); // writes JSON serialization of MyValue instance
+  // or, read
+  MyValue older = mapper.readValue(new File("my-older-stuff.json"), MyValue.class);
+
+  // Or if you prefer JSON Tree representation:
+  JsonNode root = mapper.readTree(newState);
+  // and find values by, for example, using a {@link com.fasterxml.jackson.core.JsonPointer} expression:
+  int age = root.at("/personal/age").getValueAsInt(); 
+
+ *

+ * The main conversion API is defined in {@link ObjectCodec}, so that + * implementation details of this class need not be exposed to + * streaming parser and generator classes. Usage via {@link ObjectCodec} is, + * however, usually only for cases where dependency to {@link ObjectMapper} is + * either not possible (from Streaming API), or undesireable (when only relying + * on Streaming API). + *

+ * Mapper instances are fully thread-safe provided that ALL configuration of the + * instance occurs before ANY read or write calls. If configuration of a mapper + * is modified after first usage, changes may or may not take effect, and configuration + * calls themselves may fail. + * If you need to use different configuration, you have two main possibilities: + *

    + *
  • Construct and use {@link ObjectReader} for reading, {@link ObjectWriter} for writing. + * Both types are fully immutable and you can freely create new instances with different + * configuration using either factory methods of {@link ObjectMapper}, or readers/writers + * themselves. Construction of new {@link ObjectReader}s and {@link ObjectWriter}s is + * a very light-weight operation so it is usually appropriate to create these on per-call + * basis, as needed, for configuring things like optional indentation of JSON. + *
  • + *
  • If the specific kind of configurability is not available via {@link ObjectReader} and + * {@link ObjectWriter}, you may need to use multiple {@link ObjectMapper} instead (for example: + * you can not change mix-in annotations on-the-fly; or, set of custom (de)serializers). + * To help with this usage, you may want to use method {@link #copy()} which creates a clone + * of the mapper with specific configuration, and allows configuration of the copied instance + * before it gets used. Note that {@link #copy} operation is as expensive as constructing + * a new {@link ObjectMapper} instance: if possible, you should still pool and reuse mappers + * if you intend to use them for multiple operations. + *
  • + *
+ *

+ * Note on caching: root-level deserializers are always cached, and accessed + * using full (generics-aware) type information. This is different from + * caching of referenced types, which is more limited and is done only + * for a subset of all deserializer types. The main reason for difference + * is that at root-level there is no incoming reference (and hence no + * referencing property, no referral information or annotations to + * produce differing deserializers), and that the performance impact + * greatest at root level (since it'll essentially cache the full + * graph of deserializers involved). + */ +public class ObjectMapper + extends ObjectCodec + implements Versioned, + java.io.Serializable // as of 2.1 +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Helper classes, enums + /********************************************************** + */ + + /** + * Enumeration used with {@link ObjectMapper#enableDefaultTyping()} + * to specify what kind of types (classes) default typing should + * be used for. It will only be used if no explicit type information + * is found, but this enumeration further limits subset of those types. + *

+ * Since 2.4 there are special exceptions for JSON Tree model + * types (sub-types of {@link TreeNode}: default typing is never + * applied to them + * (see Issue#88 for details) + */ + public enum DefaultTyping { + /** + * This value means that only properties that have + * {@link java.lang.Object} as declared type (including + * generic types without explicit type) will use default + * typing. + */ + JAVA_LANG_OBJECT, + + /** + * Value that means that default typing will be used for + * properties with declared type of {@link java.lang.Object} + * or an abstract type (abstract class or interface). + * Note that this does not include array types. + *

+ * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes. + */ + OBJECT_AND_NON_CONCRETE, + + /** + * Value that means that default typing will be used for + * all types covered by {@link #OBJECT_AND_NON_CONCRETE} + * plus all array types for them. + *

+ * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes. + */ + NON_CONCRETE_AND_ARRAYS, + + /** + * Value that means that default typing will be used for + * all non-final types, with exception of small number of + * "natural" types (String, Boolean, Integer, Double), which + * can be correctly inferred from JSON; as well as for + * all arrays of non-final types. + *

+ * Since 2.4, this does NOT apply to {@link TreeNode} and its subtypes. + */ + NON_FINAL + } + + /** + * Customized {@link TypeResolverBuilder} that provides type resolver builders + * used with so-called "default typing" + * (see {@link ObjectMapper#enableDefaultTyping()} for details). + *

+ * Type resolver construction is based on configuration: implementation takes care + * of only providing builders in cases where type information should be applied. + * This is important since build calls may be sent for any and all types, and + * type information should NOT be applied to all of them. + */ + public static class DefaultTypeResolverBuilder + extends StdTypeResolverBuilder + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + /** + * Definition of what types is this default typer valid for. + */ + protected final DefaultTyping _appliesFor; + + public DefaultTypeResolverBuilder(DefaultTyping t) { + _appliesFor = t; + } + + @Override + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, + JavaType baseType, Collection subtypes) + { + return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null; + } + + @Override + public TypeSerializer buildTypeSerializer(SerializationConfig config, + JavaType baseType, Collection subtypes) + { + return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null; + } + + /** + * Method called to check if the default type handler should be + * used for given type. + * Note: "natural types" (String, Boolean, Integer, Double) will never + * use typing; that is both due to them being concrete and final, + * and since actual serializers and deserializers will also ignore any + * attempts to enforce typing. + */ + public boolean useForType(JavaType t) + { + switch (_appliesFor) { + case NON_CONCRETE_AND_ARRAYS: + while (t.isArrayType()) { + t = t.getContentType(); + } + // fall through + case OBJECT_AND_NON_CONCRETE: + // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: + while (t.isReferenceType()) { + t = t.getReferencedType(); + } + return t.isJavaLangObject() + || (!t.isConcrete() + // [databind#88] Should not apply to JSON tree models: + && !TreeNode.class.isAssignableFrom(t.getRawClass())); + + case NON_FINAL: + while (t.isArrayType()) { + t = t.getContentType(); + } + // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: + while (t.isReferenceType()) { + t = t.getReferencedType(); + } + // [databind#88] Should not apply to JSON tree models: + return !t.isFinal() && !TreeNode.class.isAssignableFrom(t.getRawClass()); + default: + //case JAVA_LANG_OBJECT: + return t.isJavaLangObject(); + } + } + } + + /* + /********************************************************** + /* Internal constants, singletons + /********************************************************** + */ + + // Quick little shortcut, to avoid having to use global TypeFactory instance... + // 19-Oct-2015, tatu: Not sure if this is really safe to do; let's at least allow + // some amount of introspection + private final static JavaType JSON_NODE_TYPE = + SimpleType.constructUnsafe(JsonNode.class); +// TypeFactory.defaultInstance().constructType(JsonNode.class); + + // 16-May-2009, tatu: Ditto ^^^ + protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector(); + + protected final static VisibilityChecker STD_VISIBILITY_CHECKER = VisibilityChecker.Std.defaultInstance(); + + /** + * @deprecated Since 2.6, do not use: will be removed in 2.7 or later + */ + @Deprecated + protected final static PrettyPrinter _defaultPrettyPrinter = new DefaultPrettyPrinter(); + + /** + * Base settings contain defaults used for all {@link ObjectMapper} + * instances. + */ + protected final static BaseSettings DEFAULT_BASE = new BaseSettings( + null, // can not share global ClassIntrospector any more (2.5+) + DEFAULT_ANNOTATION_INTROSPECTOR, + STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(), + null, StdDateFormat.instance, null, + Locale.getDefault(), + null, // to indicate "use default TimeZone" + Base64Variants.getDefaultVariant() // 2.1 + ); + + /* + /********************************************************** + /* Configuration settings, shared + /********************************************************** + */ + + /** + * Factory used to create {@link JsonParser} and {@link JsonGenerator} + * instances as necessary. + */ + protected final JsonFactory _jsonFactory; + + /** + * Specific factory used for creating {@link JavaType} instances; + * needed to allow modules to add more custom type handling + * (mostly to support types of non-Java JVM languages) + */ + protected TypeFactory _typeFactory; + + /** + * Provider for values to inject in deserialized POJOs. + */ + protected InjectableValues _injectableValues; + + /** + * Thing used for registering sub-types, resolving them to + * super/sub-types as needed. + */ + protected SubtypeResolver _subtypeResolver; + + /* + /********************************************************** + /* Configuration settings: mix-in annotations + /********************************************************** + */ + + /** + * Mapping that defines how to apply mix-in annotations: key is + * the type to received additional annotations, and value is the + * type that has annotations to "mix in". + *

+ * Annotations associated with the value classes will be used to + * override annotations of the key class, associated with the + * same field or method. They can be further masked by sub-classes: + * you can think of it as injecting annotations between the target + * class and its sub-classes (or interfaces) + * + * @since 2.6 (earlier was a simple {@link java.util.Map} + */ + protected SimpleMixInResolver _mixIns; + + /* + /********************************************************** + /* Configuration settings, serialization + /********************************************************** + */ + + /** + * Configuration object that defines basic global + * settings for the serialization process + */ + protected SerializationConfig _serializationConfig; + + /** + * Object that manages access to serializers used for serialization, + * including caching. + * It is configured with {@link #_serializerFactory} to allow + * for constructing custom serializers. + *

+ * Note: while serializers are only exposed {@link SerializerProvider}, + * mappers and readers need to access additional API defined by + * {@link DefaultSerializerProvider} + */ + protected DefaultSerializerProvider _serializerProvider; + + /** + * Serializer factory used for constructing serializers. + */ + protected SerializerFactory _serializerFactory; + + /* + /********************************************************** + /* Configuration settings, deserialization + /********************************************************** + */ + + /** + * Configuration object that defines basic global + * settings for the serialization process + */ + protected DeserializationConfig _deserializationConfig; + + /** + * Blueprint context object; stored here to allow custom + * sub-classes. Contains references to objects needed for + * deserialization construction (cache, factory). + */ + protected DefaultDeserializationContext _deserializationContext; + + /* + /********************************************************** + /* Module-related + /********************************************************** + */ + + /** + * Set of module types (as per {@link Module#getTypeId()} that have been + * registered; kept track of iff {@link MapperFeature#IGNORE_DUPLICATE_MODULE_REGISTRATIONS} + * is enabled, so that duplicate registration calls can be ignored + * (to avoid adding same handlers multiple times, mostly). + * + * @since 2.5 + */ + protected Set _registeredModuleTypes; + + /* + /********************************************************** + /* Caching + /********************************************************** + */ + + /* Note: handling of serializers and deserializers is not symmetric; + * and as a result, only root-level deserializers can be cached here. + * This is mostly because typing and resolution for deserializers is + * fully static; whereas it is quite dynamic for serialization. + */ + + /** + * We will use a separate main-level Map for keeping track + * of root-level deserializers. This is where most successful + * cache lookups get resolved. + * Map will contain resolvers for all kinds of types, including + * container types: this is different from the component cache + * which will only cache bean deserializers. + *

+ * Given that we don't expect much concurrency for additions + * (should very quickly converge to zero after startup), let's + * explicitly define a low concurrency setting. + *

+ * Since version 1.5, these may are either "raw" deserializers (when + * no type information is needed for base type), or type-wrapped + * deserializers (if it is needed) + */ + final protected ConcurrentHashMap> _rootDeserializers + = new ConcurrentHashMap>(64, 0.6f, 2); + + /* + /********************************************************** + /* Life-cycle: constructing instance + /********************************************************** + */ + + /** + * Default constructor, which will construct the default + * {@link JsonFactory} as necessary, use + * {@link SerializerProvider} as its + * {@link SerializerProvider}, and + * {@link BeanSerializerFactory} as its + * {@link SerializerFactory}. + * This means that it + * can serialize all standard JDK types, as well as regular + * Java Beans (based on method names and Jackson-specific annotations), + * but does not support JAXB annotations. + */ + public ObjectMapper() { + this(null, null, null); + } + + /** + * Constructs instance that uses specified {@link JsonFactory} + * for constructing necessary {@link JsonParser}s and/or + * {@link JsonGenerator}s. + */ + public ObjectMapper(JsonFactory jf) { + this(jf, null, null); + } + + /** + * Copy-constructor, mostly used to support {@link #copy}. + * + * @since 2.1 + */ + protected ObjectMapper(ObjectMapper src) + { + _jsonFactory = src._jsonFactory.copy(); + _jsonFactory.setCodec(this); + _subtypeResolver = src._subtypeResolver; + _typeFactory = src._typeFactory; + _injectableValues = src._injectableValues; + + SimpleMixInResolver mixins = src._mixIns.copy(); + _mixIns = mixins; + RootNameLookup rootNames = new RootNameLookup(); + _serializationConfig = new SerializationConfig(src._serializationConfig, mixins, rootNames); + _deserializationConfig = new DeserializationConfig(src._deserializationConfig, mixins, rootNames); + _serializerProvider = src._serializerProvider.copy(); + _deserializationContext = src._deserializationContext.copy(); + + // Default serializer factory is stateless, can just assign + _serializerFactory = src._serializerFactory; + + // as per [databind#922], [databind#1078] make sure to copy registered modules as appropriate + Set reg = src._registeredModuleTypes; + if (reg == null) { + _registeredModuleTypes = null; + } else { + _registeredModuleTypes = new LinkedHashSet(reg); + } + } + + /** + * Constructs instance that uses specified {@link JsonFactory} + * for constructing necessary {@link JsonParser}s and/or + * {@link JsonGenerator}s, and uses given providers for accessing + * serializers and deserializers. + * + * @param jf JsonFactory to use: if null, a new {@link MappingJsonFactory} will be constructed + * @param sp SerializerProvider to use: if null, a {@link SerializerProvider} will be constructed + * @param dc Blueprint deserialization context instance to use for creating + * actual context objects; if null, will construct standard + * {@link DeserializationContext} + */ + public ObjectMapper(JsonFactory jf, + DefaultSerializerProvider sp, DefaultDeserializationContext dc) + { + /* 02-Mar-2009, tatu: Important: we MUST default to using + * the mapping factory, otherwise tree serialization will + * have problems with POJONodes. + * 03-Jan-2010, tatu: and obviously we also must pass 'this', + * to create actual linking. + */ + if (jf == null) { + _jsonFactory = new MappingJsonFactory(this); + } else { + _jsonFactory = jf; + if (jf.getCodec() == null) { // as per [JACKSON-741] + _jsonFactory.setCodec(this); + } + } + _subtypeResolver = new StdSubtypeResolver(); + RootNameLookup rootNames = new RootNameLookup(); + // and default type factory is shared one + _typeFactory = TypeFactory.defaultInstance(); + + SimpleMixInResolver mixins = new SimpleMixInResolver(null); + _mixIns = mixins; + + BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector()); + _serializationConfig = new SerializationConfig(base, + _subtypeResolver, mixins, rootNames); + _deserializationConfig = new DeserializationConfig(base, + _subtypeResolver, mixins, rootNames); + + // Some overrides we may need + final boolean needOrder = _jsonFactory.requiresPropertyOrdering(); + if (needOrder ^ _serializationConfig.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)) { + configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, needOrder); + } + + _serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp; + _deserializationContext = (dc == null) ? + new DefaultDeserializationContext.Impl(BeanDeserializerFactory.instance) : dc; + + // Default serializer factory is stateless, can just assign + _serializerFactory = BeanSerializerFactory.instance; + } + + /** + * Overridable helper method used to construct default {@link ClassIntrospector} + * to use. + * + * @since 2.5 + */ + protected ClassIntrospector defaultClassIntrospector() { + return new BasicClassIntrospector(); + } + + /* + /********************************************************** + /* Methods sub-classes MUST override + /********************************************************** + */ + + /** + * Method for creating a new {@link ObjectMapper} instance that + * has same initial configuration as this instance. Note that this + * also requires making a copy of the underlying {@link JsonFactory} + * instance. + *

+ * Method is typically + * used when multiple, differently configured mappers are needed. + * Although configuration is shared, cached serializers and deserializers + * are NOT shared, which means that the new instance may be re-configured + * before use; meaning that it behaves the same way as if an instance + * was constructed from scratch. + * + * @since 2.1 + */ + public ObjectMapper copy() { + _checkInvalidCopy(ObjectMapper.class); + return new ObjectMapper(this); + } + + /** + * @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"); + } + } + + /* + /********************************************************** + /* Methods sub-classes MUST override if providing custom + /* ObjectReader/ObjectWriter implementations + /********************************************************** + */ + + /** + * Factory method sub-classes must override, to produce {@link ObjectReader} + * instances of proper sub-type + * + * @since 2.5 + */ + protected ObjectReader _newReader(DeserializationConfig config) { + return new ObjectReader(this, config); + } + + /** + * Factory method sub-classes must override, to produce {@link ObjectReader} + * instances of proper sub-type + * + * @since 2.5 + */ + protected ObjectReader _newReader(DeserializationConfig config, + JavaType valueType, Object valueToUpdate, + FormatSchema schema, InjectableValues injectableValues) { + return new ObjectReader(this, config, valueType, valueToUpdate, schema, injectableValues); + } + + /** + * Factory method sub-classes must override, to produce {@link ObjectWriter} + * instances of proper sub-type + * + * @since 2.5 + */ + protected ObjectWriter _newWriter(SerializationConfig config) { + return new ObjectWriter(this, config); + } + + /** + * Factory method sub-classes must override, to produce {@link ObjectWriter} + * instances of proper sub-type + * + * @since 2.5 + */ + protected ObjectWriter _newWriter(SerializationConfig config, FormatSchema schema) { + return new ObjectWriter(this, config, schema); + } + + /** + * Factory method sub-classes must override, to produce {@link ObjectWriter} + * instances of proper sub-type + * + * @since 2.5 + */ + protected ObjectWriter _newWriter(SerializationConfig config, + JavaType rootType, PrettyPrinter pp) { + return new ObjectWriter(this, config, rootType, pp); + } + + /* + /********************************************************** + /* Versioned impl + /********************************************************** + */ + + /** + * Method that will return version information stored in and read from jar + * that contains this class. + */ + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Module registration, discovery + /********************************************************** + */ + + /** + * Method for registering a module that can extend functionality + * provided by this mapper; for example, by adding providers for + * custom serializers and deserializers. + * + * @param module Module to register + */ + public ObjectMapper registerModule(Module module) + { + if (isEnabled(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS)) { + Object typeId = module.getTypeId(); + if (typeId != null) { + if (_registeredModuleTypes == null) { + // plus let's keep them in order too, easier to debug or expose + // in registration order if that matter + _registeredModuleTypes = new LinkedHashSet(); + } + // try adding; if already had it, should skip + if (!_registeredModuleTypes.add(typeId)) { + return this; + } + } + } + + /* Let's ensure we have access to name and version information, + * even if we do not have immediate use for either. This way we know + * that they will be available from beginning + */ + String name = module.getModuleName(); + if (name == null) { + throw new IllegalArgumentException("Module without defined name"); + } + Version version = module.version(); + if (version == null) { + throw new IllegalArgumentException("Module without defined version"); + } + + final ObjectMapper mapper = this; + + // And then call registration + module.setupModule(new Module.SetupContext() + { + // // // Accessors + + @Override + public Version getMapperVersion() { + return version(); + } + + @SuppressWarnings("unchecked") + @Override + public C getOwner() { + // why do we need the cast here?!? + return (C) mapper; + } + + @Override + public TypeFactory getTypeFactory() { + return _typeFactory; + } + + @Override + public boolean isEnabled(MapperFeature f) { + return mapper.isEnabled(f); + } + + @Override + public boolean isEnabled(DeserializationFeature f) { + return mapper.isEnabled(f); + } + + @Override + public boolean isEnabled(SerializationFeature f) { + return mapper.isEnabled(f); + } + + @Override + public boolean isEnabled(JsonFactory.Feature f) { + return mapper.isEnabled(f); + } + + @Override + public boolean isEnabled(JsonParser.Feature f) { + return mapper.isEnabled(f); + } + + @Override + public boolean isEnabled(JsonGenerator.Feature f) { + return mapper.isEnabled(f); + } + + // // // Methods for registering handlers: deserializers + + @Override + public void addDeserializers(Deserializers d) { + DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalDeserializers(d); + mapper._deserializationContext = mapper._deserializationContext.with(df); + } + + @Override + public void addKeyDeserializers(KeyDeserializers d) { + DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalKeyDeserializers(d); + mapper._deserializationContext = mapper._deserializationContext.with(df); + } + + @Override + public void addBeanDeserializerModifier(BeanDeserializerModifier modifier) { + DeserializerFactory df = mapper._deserializationContext._factory.withDeserializerModifier(modifier); + mapper._deserializationContext = mapper._deserializationContext.with(df); + } + + // // // Methods for registering handlers: serializers + + @Override + public void addSerializers(Serializers s) { + mapper._serializerFactory = mapper._serializerFactory.withAdditionalSerializers(s); + } + + @Override + public void addKeySerializers(Serializers s) { + mapper._serializerFactory = mapper._serializerFactory.withAdditionalKeySerializers(s); + } + + @Override + public void addBeanSerializerModifier(BeanSerializerModifier modifier) { + mapper._serializerFactory = mapper._serializerFactory.withSerializerModifier(modifier); + } + + // // // Methods for registering handlers: other + + @Override + public void addAbstractTypeResolver(AbstractTypeResolver resolver) { + DeserializerFactory df = mapper._deserializationContext._factory.withAbstractTypeResolver(resolver); + mapper._deserializationContext = mapper._deserializationContext.with(df); + } + + @Override + public void addTypeModifier(TypeModifier modifier) { + TypeFactory f = mapper._typeFactory; + f = f.withModifier(modifier); + mapper.setTypeFactory(f); + } + + @Override + public void addValueInstantiators(ValueInstantiators instantiators) { + DeserializerFactory df = mapper._deserializationContext._factory.withValueInstantiators(instantiators); + mapper._deserializationContext = mapper._deserializationContext.with(df); + } + + @Override + public void setClassIntrospector(ClassIntrospector ci) { + mapper._deserializationConfig = mapper._deserializationConfig.with(ci); + mapper._serializationConfig = mapper._serializationConfig.with(ci); + } + + @Override + public void insertAnnotationIntrospector(AnnotationIntrospector ai) { + mapper._deserializationConfig = mapper._deserializationConfig.withInsertedAnnotationIntrospector(ai); + mapper._serializationConfig = mapper._serializationConfig.withInsertedAnnotationIntrospector(ai); + } + + @Override + public void appendAnnotationIntrospector(AnnotationIntrospector ai) { + mapper._deserializationConfig = mapper._deserializationConfig.withAppendedAnnotationIntrospector(ai); + mapper._serializationConfig = mapper._serializationConfig.withAppendedAnnotationIntrospector(ai); + } + + @Override + public void registerSubtypes(Class... subtypes) { + mapper.registerSubtypes(subtypes); + } + + @Override + public void registerSubtypes(NamedType... subtypes) { + mapper.registerSubtypes(subtypes); + } + + @Override + public void setMixInAnnotations(Class target, Class mixinSource) { + mapper.addMixIn(target, mixinSource); + } + + @Override + public void addDeserializationProblemHandler(DeserializationProblemHandler handler) { + mapper.addHandler(handler); + } + + @Override + public void setNamingStrategy(PropertyNamingStrategy naming) { + mapper.setPropertyNamingStrategy(naming); + } + }); + return this; + } + + /** + * Convenience method for registering specified modules in order; + * functionally equivalent to: + *
+     *   for (Module module : modules) {
+     *      registerModule(module);
+     *   }
+     *
+ * + * @since 2.2 + */ + public ObjectMapper registerModules(Module... modules) + { + for (Module module : modules) { + registerModule(module); + } + return this; + } + + /** + * Convenience method for registering specified modules in order; + * functionally equivalent to: + *
+     *   for (Module module : modules) {
+     *      registerModule(module);
+     *   }
+     *
+ * + * @since 2.2 + */ + public ObjectMapper registerModules(Iterable modules) + { + for (Module module : modules) { + registerModule(module); + } + return this; + } + + /** + * Method for locating available methods, using JDK {@link ServiceLoader} + * facility, along with module-provided SPI. + *

+ * Note that method does not do any caching, so calls should be considered + * potentially expensive. + * + * @since 2.2 + */ + public static List findModules() { + return findModules(null); + } + + /** + * Method for locating available methods, using JDK {@link ServiceLoader} + * facility, along with module-provided SPI. + *

+ * Note that method does not do any caching, so calls should be considered + * potentially expensive. + * + * @since 2.2 + */ + public static List findModules(ClassLoader classLoader) + { + ArrayList modules = new ArrayList(); + ServiceLoader loader = (classLoader == null) ? + ServiceLoader.load(Module.class) : ServiceLoader.load(Module.class, classLoader); + for (Module module : loader) { + modules.add(module); + } + return modules; + } + + /** + * Convenience method that is functionally equivalent to: + * + * mapper.registerModules(mapper.findModules()); + * + *

+ * As with {@link #findModules()}, no caching is done for modules, so care + * needs to be taken to either create and share a single mapper instance; + * or to cache introspected set of modules. + * + * @since 2.2 + */ + public ObjectMapper findAndRegisterModules() { + return registerModules(findModules()); + } + + /* + /********************************************************** + /* Configuration: main config object access + /********************************************************** + */ + + /** + * Method that returns the shared default {@link SerializationConfig} + * object that defines configuration settings for serialization. + *

+ * Note that since instances are immutable, you can NOT change settings + * by accessing an instance and calling methods: this will simply create + * new instance of config object. + */ + public SerializationConfig getSerializationConfig() { + return _serializationConfig; + } + + /** + * Method that returns + * the shared default {@link DeserializationConfig} object + * that defines configuration settings for deserialization. + *

+ * Note that since instances are immutable, you can NOT change settings + * by accessing an instance and calling methods: this will simply create + * new instance of config object. + */ + public DeserializationConfig getDeserializationConfig() { + return _deserializationConfig; + } + + /** + * Method for getting current {@link DeserializationContext}. + *

+ * Note that since instances are immutable, you can NOT change settings + * by accessing an instance and calling methods: this will simply create + * new instance of context object. + */ + public DeserializationContext getDeserializationContext() { + return _deserializationContext; + } + + /* + /********************************************************** + /* Configuration: ser/deser factory, provider access + /********************************************************** + */ + + /** + * Method for setting specific {@link SerializerFactory} to use + * for constructing (bean) serializers. + */ + public ObjectMapper setSerializerFactory(SerializerFactory f) { + _serializerFactory = f; + return this; + } + + /** + * Method for getting current {@link SerializerFactory}. + *

+ * Note that since instances are immutable, you can NOT change settings + * by accessing an instance and calling methods: this will simply create + * new instance of factory object. + */ + public SerializerFactory getSerializerFactory() { + return _serializerFactory; + } + + /** + * Method for setting "blueprint" {@link SerializerProvider} instance + * to use as the base for actual provider instances to use for handling + * caching of {@link JsonSerializer} instances. + */ + public ObjectMapper setSerializerProvider(DefaultSerializerProvider p) { + _serializerProvider = p; + return this; + } + + /** + * Accessor for the "blueprint" (or, factory) instance, from which instances + * are created by calling {@link DefaultSerializerProvider#createInstance}. + * Note that returned instance can not be directly used as it is not properly + * configured: to get a properly configured instance to call, use + * {@link #getSerializerProviderInstance()} instead. + */ + public SerializerProvider getSerializerProvider() { + return _serializerProvider; + } + + /** + * Accessor for constructing and returning a {@link SerializerProvider} + * instance that may be used for accessing serializers. This is same as + * calling {@link #getSerializerProvider}, and calling createInstance + * on it. + * + * @since 2.7 + */ + public SerializerProvider getSerializerProviderInstance() { + return _serializerProvider(_serializationConfig); + } + + /* + /********************************************************** + /* Configuration: mix-in annotations + /********************************************************** + */ + + /** + * Method to use for defining mix-in annotations to use for augmenting + * annotations that processable (serializable / deserializable) + * classes have. + * Mixing in is done when introspecting class annotations and properties. + * Map passed contains keys that are target classes (ones to augment + * with new annotation overrides), and values that are source classes + * (have annotations to use for augmentation). + * Annotations from source classes (and their supertypes) + * will override + * annotations that target classes (and their super-types) have. + *

+ * Note that this method will CLEAR any previously defined mix-ins + * for this mapper. + * + * @since 2.5 + */ + public ObjectMapper setMixIns(Map, Class> sourceMixins) + { + // NOTE: does NOT change possible externally configured resolver, just local defs + _mixIns.setLocalDefinitions(sourceMixins); + return this; + } + + /** + * Method to use for adding mix-in annotations to use for augmenting + * specified class or interface. All annotations from + * mixinSource are taken to override annotations + * that target (or its supertypes) has. + * + * @param target Class (or interface) whose annotations to effectively override + * @param mixinSource Class (or interface) whose annotations are to + * be "added" to target's annotations, overriding as necessary + * + * @since 2.5 + */ + public ObjectMapper addMixIn(Class target, Class mixinSource) + { + _mixIns.addLocalDefinition(target, mixinSource); + return this; + } + + /** + * Method that can be called to specify given resolver for locating + * mix-in classes to use, overriding directly added mappings. + * Note that direct mappings are not cleared, but they are only applied + * if resolver does not provide mix-in matches. + * + * @since 2.6 + */ + public ObjectMapper setMixInResolver(ClassIntrospector.MixInResolver resolver) + { + SimpleMixInResolver r = _mixIns.withOverrides(resolver); + if (r != _mixIns) { + _mixIns = r; + _deserializationConfig = new DeserializationConfig(_deserializationConfig, r); + _serializationConfig = new SerializationConfig(_serializationConfig, r); + } + return this; + } + + public Class findMixInClassFor(Class cls) { + return _mixIns.findMixInClassFor(cls); + } + + // For testing only: + public int mixInCount() { + return _mixIns.localSize(); + } + + /** + * @deprecated Since 2.5: replaced by a fluent form of the method; {@link #setMixIns}. + */ + @Deprecated + public void setMixInAnnotations(Map, Class> sourceMixins) { + setMixIns(sourceMixins); + } + + /** + * @deprecated Since 2.5: replaced by a fluent form of the method; {@link #addMixIn(Class, Class)}. + */ + @Deprecated + public final void addMixInAnnotations(Class target, Class mixinSource) { + addMixIn(target, mixinSource); + } + + /* + /********************************************************** + /* Configuration, introspection + /********************************************************** + */ + + /** + * Method for accessing currently configured visibility checker; + * object used for determining whether given property element + * (method, field, constructor) can be auto-detected or not. + */ + public VisibilityChecker getVisibilityChecker() { + return _serializationConfig.getDefaultVisibilityChecker(); + } + + /** + * @deprecated Since 2.6 use {@link #setVisibility(VisibilityChecker)} instead. + */ + @Deprecated + public void setVisibilityChecker(VisibilityChecker vc) { + setVisibility(vc); + } + + /** + * Method for setting currently configured {@link VisibilityChecker}, + * object used for determining whether given property element + * (method, field, constructor) can be auto-detected or not. + * This default checker is used if no per-class overrides + * are defined. + * + * @since 2.6 + */ + public ObjectMapper setVisibility(VisibilityChecker vc) { + _deserializationConfig = _deserializationConfig.with(vc); + _serializationConfig = _serializationConfig.with(vc); + return this; + } + + /** + * Convenience method that allows changing configuration for + * underlying {@link VisibilityChecker}s, to change details of what kinds of + * properties are auto-detected. + * Basically short cut for doing: + *

+     *  mapper.setVisibilityChecker(
+     *     mapper.getVisibilityChecker().withVisibility(forMethod, visibility)
+     *  );
+     *
+ * one common use case would be to do: + *
+     *  mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
+     *
+ * which would make all member fields serializable without further annotations, + * instead of just public fields (default setting). + * + * @param forMethod Type of property descriptor affected (field, getter/isGetter, + * setter, creator) + * @param visibility Minimum visibility to require for the property descriptors of type + * + * @return Modified mapper instance (that is, "this"), to allow chaining + * of configuration calls + */ + public ObjectMapper setVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) + { + _deserializationConfig = _deserializationConfig.withVisibility(forMethod, visibility); + _serializationConfig = _serializationConfig.withVisibility(forMethod, visibility); + return this; + } + + /** + * Method for accessing subtype resolver in use. + */ + public SubtypeResolver getSubtypeResolver() { + return _subtypeResolver; + } + + /** + * Method for setting custom subtype resolver to use. + */ + public ObjectMapper setSubtypeResolver(SubtypeResolver str) { + _subtypeResolver = str; + _deserializationConfig = _deserializationConfig.with(str); + _serializationConfig = _serializationConfig.with(str); + return this; + } + + /** + * Method for setting {@link AnnotationIntrospector} used by this + * mapper instance for both serialization and deserialization. + * Note that doing this will replace the current introspector, which + * may lead to unavailability of core Jackson annotations. + * If you want to combine handling of multiple introspectors, + * have a look at {@link com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair}. + * + * @see com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair + */ + public ObjectMapper setAnnotationIntrospector(AnnotationIntrospector ai) { + _serializationConfig = _serializationConfig.with(ai); + _deserializationConfig = _deserializationConfig.with(ai); + return this; + } + + /** + * Method for changing {@link AnnotationIntrospector} instances used + * by this mapper instance for serialization and deserialization, + * specifying them separately so that different introspection can be + * used for different aspects + * + * @since 2.1 + * + * @param serializerAI {@link AnnotationIntrospector} to use for configuring + * serialization + * @param deserializerAI {@link AnnotationIntrospector} to use for configuring + * deserialization + * + * @see com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair + */ + public ObjectMapper setAnnotationIntrospectors(AnnotationIntrospector serializerAI, + AnnotationIntrospector deserializerAI) { + _serializationConfig = _serializationConfig.with(serializerAI); + _deserializationConfig = _deserializationConfig.with(deserializerAI); + return this; + } + + /** + * Method for setting custom property naming strategy to use. + */ + public ObjectMapper setPropertyNamingStrategy(PropertyNamingStrategy s) { + _serializationConfig = _serializationConfig.with(s); + _deserializationConfig = _deserializationConfig.with(s); + return this; + } + + /** + * @since 2.5 + */ + public PropertyNamingStrategy getPropertyNamingStrategy() { + // arbitrary choice but let's do: + return _serializationConfig.getPropertyNamingStrategy(); + } + + /** + * Convenience method, equivalent to calling: + *
+     *  setPropertyInclusion(JsonInclude.Value.construct(incl, Include.ALWAYS));
+     *
+ */ + public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) { + setPropertyInclusion(JsonInclude.Value.construct(incl, JsonInclude.Include.USE_DEFAULTS)); + return this; + } + + /** + * Method for setting default POJO property inclusion strategy for serialization. + * + * @since 2.7 + */ + public ObjectMapper setPropertyInclusion(JsonInclude.Value incl) { + _serializationConfig = _serializationConfig.withPropertyInclusion(incl); + return this; + } + + /** + * Method for specifying {@link PrettyPrinter} to use when "default pretty-printing" + * is enabled (by enabling {@link SerializationFeature#INDENT_OUTPUT}) + * + * @param pp Pretty printer to use by default. + * + * @return This mapper, useful for call-chaining + * + * @since 2.6 + */ + public ObjectMapper setDefaultPrettyPrinter(PrettyPrinter pp) { + _serializationConfig = _serializationConfig.withDefaultPrettyPrinter(pp); + return this; + } + + /* + /********************************************************** + /* Type information configuration + /********************************************************** + */ + + /** + * Convenience method that is equivalent to calling + *
+     *  enableObjectTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
+     *
+ */ + public ObjectMapper enableDefaultTyping() { + return enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE); + } + + /** + * Convenience method that is equivalent to calling + *
+     *  enableObjectTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
+     *
+ */ + public ObjectMapper enableDefaultTyping(DefaultTyping dti) { + return enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY); + } + + /** + * Method for enabling automatic inclusion of type information, needed + * for proper deserialization of polymorphic types (unless types + * have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}). + *

+ * NOTE: use of JsonTypeInfo.As#EXTERNAL_PROPERTY NOT SUPPORTED; + * and attempts of do so will throw an {@link IllegalArgumentException} to make + * this limitation explicit. + * + * @param applicability Defines kinds of types for which additional type information + * is added; see {@link DefaultTyping} for more information. + */ + public ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs) + { + /* 18-Sep-2014, tatu: Let's add explicit check to ensure no one tries to + * use "As.EXTERNAL_PROPERTY", since that will not work (with 2.5+) + */ + if (includeAs == JsonTypeInfo.As.EXTERNAL_PROPERTY) { + throw new IllegalArgumentException("Can not use includeAs of "+includeAs); + } + + TypeResolverBuilder typer = new DefaultTypeResolverBuilder(applicability); + // we'll always use full class name, when using defaulting + typer = typer.init(JsonTypeInfo.Id.CLASS, null); + typer = typer.inclusion(includeAs); + return setDefaultTyping(typer); + } + + /** + * Method for enabling automatic inclusion of type information -- needed + * for proper deserialization of polymorphic types (unless types + * have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) -- + * using "As.PROPERTY" inclusion mechanism and specified property name + * to use for inclusion (default being "@class" since default type information + * always uses class name as type identifier) + */ + public ObjectMapper enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName) + { + TypeResolverBuilder typer = new DefaultTypeResolverBuilder(applicability); + // we'll always use full class name, when using defaulting + typer = typer.init(JsonTypeInfo.Id.CLASS, null); + typer = typer.inclusion(JsonTypeInfo.As.PROPERTY); + typer = typer.typeProperty(propertyName); + return setDefaultTyping(typer); + } + + /** + * Method for disabling automatic inclusion of type information; if so, only + * explicitly annotated types (ones with + * {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) will have + * additional embedded type information. + */ + public ObjectMapper disableDefaultTyping() { + return setDefaultTyping(null); + } + + /** + * Method for enabling automatic inclusion of type information, using + * specified handler object for determining which types this affects, + * as well as details of how information is embedded. + * + * @param typer Type information inclusion handler + */ + public ObjectMapper setDefaultTyping(TypeResolverBuilder typer) { + _deserializationConfig = _deserializationConfig.with(typer); + _serializationConfig = _serializationConfig.with(typer); + return this; + } + + /** + * Method for registering specified class as a subtype, so that + * typename-based resolution can link supertypes to subtypes + * (as an alternative to using annotations). + * Type for given class is determined from appropriate annotation; + * or if missing, default name (unqualified class name) + */ + public void registerSubtypes(Class... classes) { + getSubtypeResolver().registerSubtypes(classes); + } + + /** + * Method for registering specified class as a subtype, so that + * typename-based resolution can link supertypes to subtypes + * (as an alternative to using annotations). + * Name may be provided as part of argument, but if not will + * be based on annotations or use default name (unqualified + * class name). + */ + public void registerSubtypes(NamedType... types) { + getSubtypeResolver().registerSubtypes(types); + } + + /* + /********************************************************** + /* Configuration, basic type handling + /********************************************************** + */ + + /** + * Accessor for getting currently configured {@link TypeFactory} instance. + */ + public TypeFactory getTypeFactory() { + return _typeFactory; + } + + /** + * Method that can be used to override {@link TypeFactory} instance + * used by this mapper. + *

+ * Note: will also set {@link TypeFactory} that deserialization and + * serialization config objects use. + */ + public ObjectMapper setTypeFactory(TypeFactory f) + { + _typeFactory = f; + _deserializationConfig = _deserializationConfig.with(f); + _serializationConfig = _serializationConfig.with(f); + return this; + } + + /** + * Convenience method for constructing {@link JavaType} out of given + * type (typically java.lang.Class), but without explicit + * context. + */ + public JavaType constructType(Type t) { + return _typeFactory.constructType(t); + } + + /* + /********************************************************** + /* Configuration, deserialization + /********************************************************** + */ + + /** + * Method that can be used to get hold of {@link JsonNodeFactory} + * that this mapper will use when directly constructing + * root {@link JsonNode} instances for Trees. + *

+ * Note: this is just a shortcut for calling + *

+     *   getDeserializationConfig().getNodeFactory()
+     *
+ */ + public JsonNodeFactory getNodeFactory() { + return _deserializationConfig.getNodeFactory(); + } + + /** + * Method for specifying {@link JsonNodeFactory} to use for + * constructing root level tree nodes (via method + * {@link #createObjectNode} + */ + public ObjectMapper setNodeFactory(JsonNodeFactory f) { + _deserializationConfig = _deserializationConfig.with(f); + return this; + } + + /** + * Method for adding specified {@link DeserializationProblemHandler} + * to be used for handling specific problems during deserialization. + */ + public ObjectMapper addHandler(DeserializationProblemHandler h) { + _deserializationConfig = _deserializationConfig.withHandler(h); + return this; + } + + /** + * Method for removing all registered {@link DeserializationProblemHandler}s + * instances from this mapper. + */ + public ObjectMapper clearProblemHandlers() { + _deserializationConfig = _deserializationConfig.withNoProblemHandlers(); + return this; + } + + /** + * Method that allows overriding of the underlying {@link DeserializationConfig} + * object. + * It is added as a fallback method that may be used if no other configuration + * modifier method works: it should not be used if there are alternatives, + * and its use is generally discouraged. + *

+ * NOTE: only use this method if you know what you are doing -- it allows + * by-passing some of checks applied to other configuration methods. + * Also keep in mind that as with all configuration of {@link ObjectMapper}, + * this is only thread-safe if done before calling any deserialization methods. + * + * @since 2.4 + */ + public ObjectMapper setConfig(DeserializationConfig config) { + _deserializationConfig = config; + return this; + } + + /* + /********************************************************** + /* Configuration, serialization + /********************************************************** + */ + + /** + * @deprecated Since 2.6, use {@link #setFilterProvider} instead (allows chaining) + */ + @Deprecated + public void setFilters(FilterProvider filterProvider) { + _serializationConfig = _serializationConfig.withFilters(filterProvider); + } + + /** + * Method for configuring this mapper to use specified {@link FilterProvider} for + * mapping Filter Ids to actual filter instances. + *

+ * Note that usually it is better to use method {@link #writer(FilterProvider)}; + * however, sometimes + * this method is more convenient. For example, some frameworks only allow configuring + * of ObjectMapper instances and not {@link ObjectWriter}s. + * + * @since 2.6 + */ + public ObjectMapper setFilterProvider(FilterProvider filterProvider) { + _serializationConfig = _serializationConfig.withFilters(filterProvider); + return this; + } + + /** + * Method that will configure default {@link Base64Variant} that + * byte[] serializers and deserializers will use. + * + * @param v Base64 variant to use + * + * @return This mapper, for convenience to allow chaining + * + * @since 2.1 + */ + public ObjectMapper setBase64Variant(Base64Variant v) { + _serializationConfig = _serializationConfig.with(v); + _deserializationConfig = _deserializationConfig.with(v); + return this; + } + + /** + * Method that allows overriding of the underlying {@link SerializationConfig} + * object, which contains serialization-specific configuration settings. + * It is added as a fallback method that may be used if no other configuration + * modifier method works: it should not be used if there are alternatives, + * and its use is generally discouraged. + *

+ * NOTE: only use this method if you know what you are doing -- it allows + * by-passing some of checks applied to other configuration methods. + * Also keep in mind that as with all configuration of {@link ObjectMapper}, + * this is only thread-safe if done before calling any serialization methods. + * + * @since 2.4 + */ + public ObjectMapper setConfig(SerializationConfig config) { + _serializationConfig = config; + return this; + } + + /* + /********************************************************** + /* Configuration, other + /********************************************************** + */ + + /** + * Method that can be used to get hold of {@link JsonFactory} that this + * mapper uses if it needs to construct {@link JsonParser}s + * and/or {@link JsonGenerator}s. + * + * @return {@link JsonFactory} that this mapper uses when it needs to + * construct Json parser and generators + */ + @Override + public JsonFactory getFactory() { return _jsonFactory; } + + /** + * @deprecated Since 2.1: Use {@link #getFactory} instead + */ + @Deprecated + @Override + public JsonFactory getJsonFactory() { return getFactory(); } + + /** + * Method for configuring the default {@link DateFormat} to use when serializing time + * values as Strings, and deserializing from JSON Strings. + * This is preferably to directly modifying {@link SerializationConfig} and + * {@link DeserializationConfig} instances. + * If you need per-request configuration, use {@link #writer(DateFormat)} to + * create properly configured {@link ObjectWriter} and use that; this because + * {@link ObjectWriter}s are thread-safe whereas ObjectMapper itself is only + * thread-safe when configuring methods (such as this one) are NOT called. + */ + public ObjectMapper setDateFormat(DateFormat dateFormat) + { + _deserializationConfig = _deserializationConfig.with(dateFormat); + _serializationConfig = _serializationConfig.with(dateFormat); + return this; + } + + /** + * @since 2.5 + */ + public DateFormat getDateFormat() { + // arbitrary choice but let's do: + return _serializationConfig.getDateFormat(); + } + + /** + * Method for configuring {@link HandlerInstantiator} to use for creating + * instances of handlers (such as serializers, deserializers, type and type + * id resolvers), given a class. + * + * @param hi Instantiator to use; if null, use the default implementation + */ + public Object setHandlerInstantiator(HandlerInstantiator hi) + { + _deserializationConfig = _deserializationConfig.with(hi); + _serializationConfig = _serializationConfig.with(hi); + return this; + } + + /** + * Method for configuring {@link InjectableValues} which used to find + * values to inject. + */ + public ObjectMapper setInjectableValues(InjectableValues injectableValues) { + _injectableValues = injectableValues; + return this; + } + + /** + * @since 2.6 + */ + public InjectableValues getInjectableValues() { + return _injectableValues; + } + + /** + * Method for overriding default locale to use for formatting. + * Default value used is {@link Locale#getDefault()}. + */ + public ObjectMapper setLocale(Locale l) { + _deserializationConfig = _deserializationConfig.with(l); + _serializationConfig = _serializationConfig.with(l); + return this; + } + + /** + * Method for overriding default TimeZone to use for formatting. + * Default value used is UTC (NOT local timezone). + */ + public ObjectMapper setTimeZone(TimeZone tz) { + _deserializationConfig = _deserializationConfig.with(tz); + _serializationConfig = _serializationConfig.with(tz); + return this; + } + + /* + /********************************************************** + /* Configuration, simple features: MapperFeature + /********************************************************** + */ + + /** + * Method for checking whether given {@link MapperFeature} is enabled. + */ + public boolean isEnabled(MapperFeature f) { + // ok to use either one, should be kept in sync + return _serializationConfig.isEnabled(f); + } + + /** + * Method for changing state of an on/off mapper feature for + * this mapper instance. + */ + public ObjectMapper configure(MapperFeature f, boolean state) { + _serializationConfig = state ? + _serializationConfig.with(f) : _serializationConfig.without(f); + _deserializationConfig = state ? + _deserializationConfig.with(f) : _deserializationConfig.without(f); + return this; + } + + /** + * Method for enabling specified {@link MapperConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper enable(MapperFeature... f) { + _deserializationConfig = _deserializationConfig.with(f); + _serializationConfig = _serializationConfig.with(f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper disable(MapperFeature... f) { + _deserializationConfig = _deserializationConfig.without(f); + _serializationConfig = _serializationConfig.without(f); + return this; + } + + /* + /********************************************************** + /* Configuration, simple features: SerializationFeature + /********************************************************** + */ + + /** + * Method for checking whether given serialization-specific + * feature is enabled. + */ + public boolean isEnabled(SerializationFeature f) { + return _serializationConfig.isEnabled(f); + } + + /** + * Method for changing state of an on/off serialization feature for + * this object mapper. + */ + public ObjectMapper configure(SerializationFeature f, boolean state) { + _serializationConfig = state ? + _serializationConfig.with(f) : _serializationConfig.without(f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} feature. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper enable(SerializationFeature f) { + _serializationConfig = _serializationConfig.with(f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper enable(SerializationFeature first, + SerializationFeature... f) { + _serializationConfig = _serializationConfig.with(first, f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper disable(SerializationFeature f) { + _serializationConfig = _serializationConfig.without(f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper disable(SerializationFeature first, + SerializationFeature... f) { + _serializationConfig = _serializationConfig.without(first, f); + return this; + } + + /* + /********************************************************** + /* Configuration, simple features: DeserializationFeature + /********************************************************** + */ + + /** + * Method for checking whether given deserialization-specific + * feature is enabled. + */ + public boolean isEnabled(DeserializationFeature f) { + return _deserializationConfig.isEnabled(f); + } + + /** + * Method for changing state of an on/off deserialization feature for + * this object mapper. + */ + public ObjectMapper configure(DeserializationFeature f, boolean state) { + _deserializationConfig = state ? + _deserializationConfig.with(f) : _deserializationConfig.without(f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper enable(DeserializationFeature feature) { + _deserializationConfig = _deserializationConfig.with(feature); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper enable(DeserializationFeature first, + DeserializationFeature... f) { + _deserializationConfig = _deserializationConfig.with(first, f); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper disable(DeserializationFeature feature) { + _deserializationConfig = _deserializationConfig.without(feature); + return this; + } + + /** + * Method for enabling specified {@link DeserializationConfig} features. + * Modifies and returns this instance; no new object is created. + */ + public ObjectMapper disable(DeserializationFeature first, + DeserializationFeature... f) { + _deserializationConfig = _deserializationConfig.without(first, f); + return this; + } + + /* + /********************************************************** + /* Configuration, simple features: JsonParser.Feature + /********************************************************** + */ + + public boolean isEnabled(JsonParser.Feature f) { + return _deserializationConfig.isEnabled(f, _jsonFactory); + } + + /** + * Method for changing state of specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s + * for parser instances this object mapper creates. + *

+ * Note that this is equivalent to directly calling same method + * on {@link #getFactory}. + */ + public ObjectMapper configure(JsonParser.Feature f, boolean state) { + _jsonFactory.configure(f, state); + return this; + } + + /** + * Method for enabling specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s + * for parser instances this object mapper creates. + *

+ * Note that this is equivalent to directly calling same method on {@link #getFactory}. + * + * @since 2.5 + */ + public ObjectMapper enable(JsonParser.Feature... features) { + for (JsonParser.Feature f : features) { + _jsonFactory.enable(f); + } + return this; + } + + /** + * Method for disabling specified {@link com.fasterxml.jackson.core.JsonParser.Feature}s + * for parser instances this object mapper creates. + *

+ * Note that this is equivalent to directly calling same method on {@link #getFactory}. + * + * @since 2.5 + */ + public ObjectMapper disable(JsonParser.Feature... features) { + for (JsonParser.Feature f : features) { + _jsonFactory.disable(f); + } + return this; + } + + /* + /********************************************************** + /* Configuration, simple features: JsonGenerator.Feature + /********************************************************** + */ + + public boolean isEnabled(JsonGenerator.Feature f) { + return _serializationConfig.isEnabled(f, _jsonFactory); + } + + /** + * Method for changing state of an on/off {@link JsonGenerator} feature for + * generator instances this object mapper creates. + *

+ * Note that this is equivalent to directly calling same method + * on {@link #getFactory}. + */ + public ObjectMapper configure(JsonGenerator.Feature f, boolean state) { + _jsonFactory.configure(f, state); + return this; + } + + /** + * Method for enabling specified {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s + * for parser instances this object mapper creates. + *

+ * Note that this is equivalent to directly calling same method on {@link #getFactory}. + * + * @since 2.5 + */ + public ObjectMapper enable(JsonGenerator.Feature... features) { + for (JsonGenerator.Feature f : features) { + _jsonFactory.enable(f); + } + return this; + } + + /** + * Method for disabling specified {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s + * for parser instances this object mapper creates. + *

+ * Note that this is equivalent to directly calling same method on {@link #getFactory}. + * + * @since 2.5 + */ + public ObjectMapper disable(JsonGenerator.Feature... features) { + for (JsonGenerator.Feature f : features) { + _jsonFactory.disable(f); + } + return this; + } + + /* + /********************************************************** + /* Configuration, simple features: JsonFactory.Feature + /********************************************************** + */ + + /** + * Convenience method, equivalent to: + *

+     *  getJsonFactory().isEnabled(f);
+     *
+ */ + public boolean isEnabled(JsonFactory.Feature f) { + return _jsonFactory.isEnabled(f); + } + + /* + /********************************************************** + /* Public API (from ObjectCodec): deserialization + /* (mapping from JSON to Java types); + /* main methods + /********************************************************** + */ + + /** + * Method to deserialize JSON content into a non-container + * type (it can be an array type, however): typically a bean, array + * or a wrapper type (like {@link java.lang.Boolean}). + *

+ * Note: this method should NOT be used if the result type is a + * container ({@link java.util.Collection} or {@link java.util.Map}. + * The reason is that due to type erasure, key and value types + * can not be introspected when using this method. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @Override + @SuppressWarnings("unchecked") + public T readValue(JsonParser jp, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readValue(getDeserializationConfig(), jp, _typeFactory.constructType(valueType)); + } + + /** + * Method to deserialize JSON content into a Java type, reference + * to which is passed as argument. Type is passed using so-called + * "super type token" (see ) + * and specifically needs to be used if the root type is a + * parameterized (generic) container type. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @Override + @SuppressWarnings("unchecked") + public T readValue(JsonParser jp, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readValue(getDeserializationConfig(), jp, _typeFactory.constructType(valueTypeRef)); + } + + /** + * Method to deserialize JSON content into a Java type, reference + * to which is passed as argument. Type is passed using + * Jackson specific type; instance of which can be constructed using + * {@link TypeFactory}. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @Override + @SuppressWarnings("unchecked") + public final T readValue(JsonParser jp, ResolvedType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readValue(getDeserializationConfig(), jp, (JavaType) valueType); + } + + /** + * Type-safe overloaded method, basically alias for {@link #readValue(JsonParser, Class)}. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings("unchecked") + public T readValue(JsonParser jp, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readValue(getDeserializationConfig(), jp, valueType); + } + + /** + * Method to deserialize JSON content as tree expressed + * using set of {@link JsonNode} instances. Returns + * root of the resulting tree (where root can consist + * of just a single node if the current event is a + * value event, not container). + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + */ + @Override + public T readTree(JsonParser jp) + throws IOException, JsonProcessingException + { + /* 02-Mar-2009, tatu: One twist; deserialization provider + * will map JSON null straight into Java null. But what + * we want to return is the "null node" instead. + */ + /* 05-Aug-2011, tatu: Also, must check for EOF here before + * calling readValue(), since that'll choke on it otherwise + */ + DeserializationConfig cfg = getDeserializationConfig(); + JsonToken t = jp.getCurrentToken(); + if (t == null) { + t = jp.nextToken(); + if (t == null) { + return null; + } + } + JsonNode n = (JsonNode) _readValue(cfg, jp, JSON_NODE_TYPE); + if (n == null) { + n = getNodeFactory().nullNode(); + } + @SuppressWarnings("unchecked") + T result = (T) n; + return result; + } + + /** + * Convenience method, equivalent in function to: + *

+     *   readerFor(valueType).readValues(p);
+     *
+ *

+ * Method for reading sequence of Objects from parser stream. + * Sequence can be either root-level "unwrapped" sequence (without surrounding + * JSON array), or a sequence contained in a JSON Array. + * In either case {@link JsonParser} MUST point to the first token of + * the first element, OR not point to any token (in which case it is advanced + * to the next token). This means, specifically, that for wrapped sequences, + * parser MUST NOT point to the surrounding START_ARRAY (one that + * contains values to read) but rather to the token following it which is the first + * token of the first value to read. + *

+ * Note that {@link ObjectReader} has more complete set of variants. + */ + @Override + public MappingIterator readValues(JsonParser p, ResolvedType valueType) + throws IOException, JsonProcessingException + { + return readValues(p, (JavaType) valueType); + } + + /** + * Convenience method, equivalent in function to: + *

+     *   readerFor(valueType).readValues(p);
+     *
+ *

+ * Type-safe overload of {@link #readValues(JsonParser, ResolvedType)}. + */ + public MappingIterator readValues(JsonParser p, JavaType valueType) + throws IOException, JsonProcessingException + { + DeserializationConfig config = getDeserializationConfig(); + DeserializationContext ctxt = createDeserializationContext(p, config); + JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); + // false -> do NOT close JsonParser (since caller passed it) + return new MappingIterator(valueType, p, ctxt, deser, + false, null); + } + + /** + * Convenience method, equivalent in function to: + *

+     *   readerFor(valueType).readValues(p);
+     *
+ *

+ * Type-safe overload of {@link #readValues(JsonParser, ResolvedType)}. + */ + @Override + public MappingIterator readValues(JsonParser p, Class valueType) + throws IOException, JsonProcessingException + { + return readValues(p, _typeFactory.constructType(valueType)); + } + + /** + * Method for reading sequence of Objects from parser stream. + */ + @Override + public MappingIterator readValues(JsonParser p, TypeReference valueTypeRef) + throws IOException, JsonProcessingException + { + return readValues(p, _typeFactory.constructType(valueTypeRef)); + } + + /* + /********************************************************** + /* Public API not included in ObjectCodec: deserialization + /* (mapping from JSON to Java types) + /********************************************************** + */ + + /** + * Method to deserialize JSON content as tree expressed + * using set of {@link JsonNode} instances. + * Returns root of the resulting tree (where root can consist + * of just a single node if the current event is a + * value event, not container). + *

+ * If a low-level I/O problem (missing input, network error) occurs, + * a {@link IOException} will be thrown. + * If a parsing problem occurs (invalid JSON), + * {@link JsonParseException} will be thrown. + * If no content is found from input (end-of-input), Java + * null will be returned. + * + * @param in Input stream used to read JSON content + * for building the JSON tree. + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + * + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + */ + public JsonNode readTree(InputStream in) + throws IOException, JsonProcessingException + { + JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(in), JSON_NODE_TYPE); + return (n == null) ? NullNode.instance : n; + } + + /** + * Method to deserialize JSON content as tree expressed + * using set of {@link JsonNode} instances. + * Returns root of the resulting tree (where root can consist + * of just a single node if the current event is a + * value event, not container). + *

+ * If a low-level I/O problem (missing input, network error) occurs, + * a {@link IOException} will be thrown. + * If a parsing problem occurs (invalid JSON), + * {@link JsonParseException} will be thrown. + * If no content is found from input (end-of-input), Java + * null will be returned. + * + * @param r Reader used to read JSON content + * for building the JSON tree. + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + */ + public JsonNode readTree(Reader r) + throws IOException, JsonProcessingException + { + JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(r), JSON_NODE_TYPE); + return (n == null) ? NullNode.instance : n; + } + + /** + * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances. + * Returns root of the resulting tree (where root can consist of just a single node if the current + * event is a value event, not container). + *

+ * If a low-level I/O problem (missing input, network error) occurs, + * a {@link IOException} will be thrown. + * If a parsing problem occurs (invalid JSON), + * {@link JsonParseException} will be thrown. + * If no content is found from input (end-of-input), Java + * null will be returned. + * + * @param content JSON content to parse to build the JSON tree. + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + * + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + */ + public JsonNode readTree(String content) + throws IOException, JsonProcessingException + { + JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(content), JSON_NODE_TYPE); + return (n == null) ? NullNode.instance : n; + } + + /** + * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances. + * Returns root of the resulting tree (where root can consist of just a single node if the current + * event is a value event, not container). + * + * @param content JSON content to parse to build the JSON tree. + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + * + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + */ + public JsonNode readTree(byte[] content) + throws IOException, JsonProcessingException + { + JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(content), JSON_NODE_TYPE); + return (n == null) ? NullNode.instance : n; + } + + /** + * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances. + * Returns root of the resulting tree (where root can consist of just a single node if the current + * event is a value event, not container). + * + * @param file File of which contents to parse as JSON for building a tree instance + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + */ + public JsonNode readTree(File file) + throws IOException, JsonProcessingException + { + JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(file), JSON_NODE_TYPE); + return (n == null) ? NullNode.instance : n; + } + + /** + * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances. + * Returns root of the resulting tree (where root can consist of just a single node if the current + * event is a value event, not container). + * + * @param source URL to use for fetching contents to parse as JSON for building a tree instance + * + * @return a {@link JsonNode}, if valid JSON content found; null + * if input has no content to bind -- note, however, that if + * JSON null token is found, it will be represented + * as a non-null {@link JsonNode} (one that returns true + * for {@link JsonNode#isNull()} + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + */ + public JsonNode readTree(URL source) + throws IOException, JsonProcessingException + { + JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(source), JSON_NODE_TYPE); + return (n == null) ? NullNode.instance : n; + } + + /* + /********************************************************** + /* Public API (from ObjectCodec): serialization + /* (mapping from Java types to Json) + /********************************************************** + */ + + /** + * Method that can be used to serialize any Java value as + * JSON output, using provided {@link JsonGenerator}. + */ + @Override + public void writeValue(JsonGenerator g, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + SerializationConfig config = getSerializationConfig(); + + /* 12-May-2015/2.6, tatu: Looks like we do NOT want to call the usual + * 'config.initialize(g)` here, since it is assumed that generator + * has been configured by caller. But for some reason we don't + * trust indentation settings... + */ + // 10-Aug-2012, tatu: as per [Issue#12], must handle indentation: + if (config.isEnabled(SerializationFeature.INDENT_OUTPUT)) { + if (g.getPrettyPrinter() == null) { + g.setPrettyPrinter(config.constructDefaultPrettyPrinter()); + } + } + if (config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { + _writeCloseableValue(g, value, config); + } else { + _serializerProvider(config).serializeValue(g, value); + if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + g.flush(); + } + } + } + + /* + /********************************************************** + /* Public API (from TreeCodec via ObjectCodec): Tree Model support + /********************************************************** + */ + + @Override + public void writeTree(JsonGenerator jgen, TreeNode rootNode) + throws IOException, JsonProcessingException + { + SerializationConfig config = getSerializationConfig(); + _serializerProvider(config).serializeValue(jgen, rootNode); + if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + jgen.flush(); + } + } + + /** + * Method to serialize given JSON Tree, using generator + * provided. + */ + public void writeTree(JsonGenerator jgen, JsonNode rootNode) + throws IOException, JsonProcessingException + { + SerializationConfig config = getSerializationConfig(); + _serializerProvider(config).serializeValue(jgen, rootNode); + if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + jgen.flush(); + } + } + + /** + *

+ * Note: return type is co-variant, as basic ObjectCodec + * abstraction can not refer to concrete node types (as it's + * part of core package, whereas impls are part of mapper + * package) + */ + @Override + public ObjectNode createObjectNode() { + return _deserializationConfig.getNodeFactory().objectNode(); + } + + /** + *

+ * Note: return type is co-variant, as basic ObjectCodec + * abstraction can not refer to concrete node types (as it's + * part of core package, whereas impls are part of mapper + * package) + */ + @Override + public ArrayNode createArrayNode() { + return _deserializationConfig.getNodeFactory().arrayNode(); + } + + /** + * Method for constructing a {@link JsonParser} out of JSON tree + * representation. + * + * @param n Root node of the tree that resulting parser will read from + */ + @Override + public JsonParser treeAsTokens(TreeNode n) { + return new TreeTraversingParser((JsonNode) n, this); + } + + /** + * Convenience conversion method that will bind data given JSON tree + * contains into specific value (usually bean) type. + *

+ * Functionally equivalent to: + *

+     *   objectMapper.convertValue(n, valueClass);
+     *
+ */ + @SuppressWarnings("unchecked") + @Override + public T treeToValue(TreeNode n, Class valueType) + throws JsonProcessingException + { + try { + // Simple cast when we just want to cast to, say, ObjectNode + // ... one caveat; while everything is Object.class, let's not take shortcut + if (valueType != Object.class && valueType.isAssignableFrom(n.getClass())) { + return (T) n; + } + // 20-Apr-2016, tatu: Another thing: for VALUE_EMBEDDED_OBJECT, assume similar + // short-cut coercion + if (n.asToken() == JsonToken.VALUE_EMBEDDED_OBJECT) { + if (n instanceof POJONode) { + Object ob = ((POJONode) n).getPojo(); + if ((ob == null) || valueType.isInstance(ob)) { + return (T) ob; + } + } + } + return readValue(treeAsTokens(n), valueType); + } catch (JsonProcessingException e) { + throw e; + } catch (IOException e) { // should not occur, no real i/o... + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + /** + * Reverse of {@link #treeToValue}; given a value (usually bean), will + * construct equivalent JSON Tree representation. Functionally similar + * to serializing value into JSON and parsing JSON as tree, but + * more efficient. + *

+ * NOTE: while results are usually identical to that of serialization followed + * by deserialization, this is not always the case. In some cases serialization + * into intermediate representation will retain encapsulation of things like + * raw value ({@link com.fasterxml.jackson.databind.util.RawValue}) or basic + * node identity ({@link JsonNode}). If so, result is a valid tree, but values + * are not re-constructed through actual JSON representation. So if transformation + * requires actual materialization of JSON (or other data format that this mapper + * produces), it will be necessary to do actual serialization. + * + * @param Actual node type; usually either basic {@link JsonNode} or + * {@link com.fasterxml.jackson.databind.node.ObjectNode} + * @param fromValue Bean value to convert + * @return Root node of the resulting JSON tree + */ + @SuppressWarnings({ "unchecked", "resource" }) + public T valueToTree(Object fromValue) + throws IllegalArgumentException + { + if (fromValue == null) return null; + TokenBuffer buf = new TokenBuffer(this, false); + if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + buf = buf.forceUseOfBigDecimal(true); + } + JsonNode result; + try { + writeValue(buf, fromValue); + JsonParser jp = buf.asParser(); + result = readTree(jp); + jp.close(); + } catch (IOException e) { // should not occur, no real i/o... + throw new IllegalArgumentException(e.getMessage(), e); + } + return (T) result; + } + + /* + /********************************************************** + /* Extended Public API, accessors + /********************************************************** + */ + + /** + * Method that can be called to check whether mapper thinks + * it could serialize an instance of given Class. + * Check is done + * by checking whether a serializer can be found for the type. + *

+ * NOTE: since this method does NOT throw exceptions, but internal + * processing may, caller usually has little information as to why + * serialization would fail. If you want access to internal {@link Exception}, + * call {@link #canSerialize(Class, AtomicReference)} instead. + * + * @return True if mapper can find a serializer for instances of + * given class (potentially serializable), false otherwise (not + * serializable) + */ + public boolean canSerialize(Class type) { + return _serializerProvider(getSerializationConfig()).hasSerializerFor(type, null); + } + + /** + * Method similar to {@link #canSerialize(Class)} but that can return + * actual {@link Throwable} that was thrown when trying to construct + * serializer: this may be useful in figuring out what the actual problem is. + * + * @since 2.3 + */ + public boolean canSerialize(Class type, AtomicReference cause) { + return _serializerProvider(getSerializationConfig()).hasSerializerFor(type, cause); + } + + /** + * Method that can be called to check whether mapper thinks + * it could deserialize an Object of given type. + * Check is done by checking whether a registered deserializer can + * be found or built for the type; if not (either by no mapping being + * found, or through an Exception being thrown, false + * is returned. + *

+ * NOTE: in case an exception is thrown during course of trying + * co construct matching deserializer, it will be effectively swallowed. + * If you want access to that exception, call + * {@link #canDeserialize(JavaType, AtomicReference)} instead. + * + * @return True if mapper can find a serializer for instances of + * given class (potentially serializable), false otherwise (not + * serializable) + */ + public boolean canDeserialize(JavaType type) + { + return createDeserializationContext(null, + getDeserializationConfig()).hasValueDeserializerFor(type, null); + } + + /** + * Method similar to {@link #canDeserialize(JavaType)} but that can return + * actual {@link Throwable} that was thrown when trying to construct + * serializer: this may be useful in figuring out what the actual problem is. + * + * @since 2.3 + */ + public boolean canDeserialize(JavaType type, AtomicReference cause) + { + return createDeserializationContext(null, + getDeserializationConfig()).hasValueDeserializerFor(type, cause); + } + + /* + /********************************************************** + /* Extended Public API, deserialization, + /* convenience methods + /********************************************************** + */ + + /** + * Method to deserialize JSON content from given file into given Java type. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings("unchecked") + public T readValue(File src, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + } + + /** + * Method to deserialize JSON content from given file into given Java type. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(File src, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + } + + /** + * Method to deserialize JSON content from given file into given Java type. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings("unchecked") + public T readValue(File src, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + } + + /** + * Method to deserialize JSON content from given resource into given Java type. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings("unchecked") + public T readValue(URL src, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + // !!! TODO +// _setupClassLoaderForDeserialization(valueType); + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + } + + /** + * Method to deserialize JSON content from given resource into given Java type. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(URL src, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + } + + @SuppressWarnings("unchecked") + public T readValue(URL src, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + } + + /** + * Method to deserialize JSON content from given JSON content String. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings("unchecked") + public T readValue(String content, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + // !!! TODO +// _setupClassLoaderForDeserialization(valueType); + return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType)); + } + + /** + * Method to deserialize JSON content from given JSON content String. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(String content, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueTypeRef)); + } + + /** + * Method to deserialize JSON content from given JSON content String. + * + * @throws IOException if a low-level I/O problem (unexpected end-of-input, + * network error) occurs (passed through as-is without additional wrapping -- note + * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS} + * does NOT result in wrapping of exception even if enabled) + * @throws JsonParseException if underlying input contains invalid content + * of type {@link JsonParser} supports (JSON for default case) + * @throws JsonMappingException if the input JSON structure does not match structure + * expected for result type (or has other mismatch issues) + */ + @SuppressWarnings("unchecked") + public T readValue(String content, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(content), valueType); + } + + @SuppressWarnings("unchecked") + public T readValue(Reader src, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + // !!! TODO +// _setupClassLoaderForDeserialization(valueType); + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(Reader src, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + } + + @SuppressWarnings("unchecked") + public T readValue(Reader src, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + } + + @SuppressWarnings("unchecked") + public T readValue(InputStream src, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + // !!! TODO +// _setupClassLoaderForDeserialization(valueType); + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(InputStream src, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + } + + @SuppressWarnings("unchecked") + public T readValue(InputStream src, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + } + + @SuppressWarnings("unchecked") + public T readValue(byte[] src, Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + // !!! TODO +// _setupClassLoaderForDeserialization(valueType); + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); + } + + @SuppressWarnings("unchecked") + public T readValue(byte[] src, int offset, int len, + Class valueType) + throws IOException, JsonParseException, JsonMappingException + { + // !!! TODO +// _setupClassLoaderForDeserialization(valueType); + return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(byte[] src, TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T readValue(byte[] src, int offset, int len, + TypeReference valueTypeRef) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueTypeRef)); + } + + @SuppressWarnings("unchecked") + public T readValue(byte[] src, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType); + } + + @SuppressWarnings("unchecked") + public T readValue(byte[] src, int offset, int len, + JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), valueType); + } + + /* + /********************************************************** + /* Extended Public API: serialization + /* (mapping from Java types to JSON) + /********************************************************** + */ + + /** + * Method that can be used to serialize any Java value as + * JSON output, written to File provided. + */ + public void writeValue(File resultFile, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configAndWriteValue(_jsonFactory.createGenerator(resultFile, JsonEncoding.UTF8), value); + } + + /** + * Method that can be used to serialize any Java value as + * JSON output, using output stream provided (using encoding + * {@link JsonEncoding#UTF8}). + *

+ * Note: method does not close the underlying stream explicitly + * here; however, {@link JsonFactory} this mapper uses may choose + * to close the stream depending on its settings (by default, + * it will try to close it when {@link JsonGenerator} we construct + * is closed). + */ + public void writeValue(OutputStream out, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configAndWriteValue(_jsonFactory.createGenerator(out, JsonEncoding.UTF8), value); + } + + /** + * Method that can be used to serialize any Java value as + * JSON output, using Writer provided. + *

+ * Note: method does not close the underlying stream explicitly + * here; however, {@link JsonFactory} this mapper uses may choose + * to close the stream depending on its settings (by default, + * it will try to close it when {@link JsonGenerator} we construct + * is closed). + */ + public void writeValue(Writer w, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configAndWriteValue(_jsonFactory.createGenerator(w), value); + } + + /** + * Method that can be used to serialize any Java value as + * a String. Functionally equivalent to calling + * {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter} + * and constructing String, but more efficient. + *

+ * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. + */ + @SuppressWarnings("resource") + public String writeValueAsString(Object value) + throws JsonProcessingException + { + // alas, we have to pull the recycler directly here... + SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler()); + try { + _configAndWriteValue(_jsonFactory.createGenerator(sw), value); + } catch (JsonProcessingException e) { // to support [JACKSON-758] + throw e; + } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: + throw JsonMappingException.fromUnexpectedIOE(e); + } + return sw.getAndClear(); + } + + /** + * Method that can be used to serialize any Java value as + * a byte array. Functionally equivalent to calling + * {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream} + * and getting bytes, but more efficient. + * Encoding used will be UTF-8. + *

+ * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. + */ + @SuppressWarnings("resource") + public byte[] writeValueAsBytes(Object value) + throws JsonProcessingException + { + ByteArrayBuilder bb = new ByteArrayBuilder(_jsonFactory._getBufferRecycler()); + try { + _configAndWriteValue(_jsonFactory.createGenerator(bb, JsonEncoding.UTF8), value); + } catch (JsonProcessingException e) { // to support [JACKSON-758] + throw e; + } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: + throw JsonMappingException.fromUnexpectedIOE(e); + } + byte[] result = bb.toByteArray(); + bb.release(); + return result; + } + + /* + /********************************************************** + /* Extended Public API: constructing ObjectWriters + /* for more advanced configuration + /********************************************************** + */ + + /** + * Convenience method for constructing {@link ObjectWriter} + * with default settings. + */ + public ObjectWriter writer() { + return _newWriter(getSerializationConfig()); + } + + /** + * Factory method for constructing {@link ObjectWriter} with + * specified feature enabled (compared to settings that this + * mapper instance has). + */ + public ObjectWriter writer(SerializationFeature feature) { + return _newWriter(getSerializationConfig().with(feature)); + } + + /** + * Factory method for constructing {@link ObjectWriter} with + * specified features enabled (compared to settings that this + * mapper instance has). + */ + public ObjectWriter writer(SerializationFeature first, + SerializationFeature... other) { + return _newWriter(getSerializationConfig().with(first, other)); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified {@link DateFormat}; or, if + * null passed, using timestamp (64-bit number. + */ + public ObjectWriter writer(DateFormat df) { + return _newWriter(getSerializationConfig().with(df)); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified JSON View (filter). + */ + public ObjectWriter writerWithView(Class serializationView) { + return _newWriter(getSerializationConfig().withView(serializationView)); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified root type, instead of actual + * runtime type of value. Type must be a super-type of runtime type. + *

+ * Main reason for using this method is performance, as writer is able + * to pre-fetch serializer to use before write, and if writer is used + * more than once this avoids addition per-value serializer lookups. + * + * @since 2.5 + */ + public ObjectWriter writerFor(Class rootType) { + return _newWriter(getSerializationConfig(), + ((rootType == null) ? null :_typeFactory.constructType(rootType)), + /*PrettyPrinter*/null); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified root type, instead of actual + * runtime type of value. Type must be a super-type of runtime type. + *

+ * Main reason for using this method is performance, as writer is able + * to pre-fetch serializer to use before write, and if writer is used + * more than once this avoids addition per-value serializer lookups. + * + * @since 2.5 + */ + public ObjectWriter writerFor(TypeReference rootType) { + return _newWriter(getSerializationConfig(), + ((rootType == null) ? null : _typeFactory.constructType(rootType)), + /*PrettyPrinter*/null); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified root type, instead of actual + * runtime type of value. Type must be a super-type of runtime type. + *

+ * Main reason for using this method is performance, as writer is able + * to pre-fetch serializer to use before write, and if writer is used + * more than once this avoids addition per-value serializer lookups. + * + * @since 2.5 + */ + public ObjectWriter writerFor(JavaType rootType) { + return _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified pretty printer for indentation + * (or if null, no pretty printer) + */ + public ObjectWriter writer(PrettyPrinter pp) { + if (pp == null) { // need to use a marker to indicate explicit disabling of pp + pp = ObjectWriter.NULL_PRETTY_PRINTER; + } + return _newWriter(getSerializationConfig(), /*root type*/ null, pp); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using the default pretty printer for indentation + */ + public ObjectWriter writerWithDefaultPrettyPrinter() { + SerializationConfig config = getSerializationConfig(); + return _newWriter(config, + /*root type*/ null, config.getDefaultPrettyPrinter()); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * serialize objects using specified filter provider. + */ + public ObjectWriter writer(FilterProvider filterProvider) { + return _newWriter(getSerializationConfig().withFilters(filterProvider)); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * pass specific schema object to {@link JsonGenerator} used for + * writing content. + * + * @param schema Schema to pass to generator + */ + public ObjectWriter writer(FormatSchema schema) { + _verifySchemaType(schema); + return _newWriter(getSerializationConfig(), schema); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * use specified Base64 encoding variant for Base64-encoded binary data. + * + * @since 2.1 + */ + public ObjectWriter writer(Base64Variant defaultBase64) { + return _newWriter(getSerializationConfig().with(defaultBase64)); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * use specified character escaping details for output. + * + * @since 2.3 + */ + public ObjectWriter writer(CharacterEscapes escapes) { + return _newWriter(getSerializationConfig()).with(escapes); + } + + /** + * Factory method for constructing {@link ObjectWriter} that will + * use specified default attributes. + * + * @since 2.3 + */ + public ObjectWriter writer(ContextAttributes attrs) { + return _newWriter(getSerializationConfig().with(attrs)); + } + + /** + * @deprecated Since 2.5, use {@link #writerFor(Class)} instead + */ + @Deprecated + public ObjectWriter writerWithType(Class rootType) { + return _newWriter(getSerializationConfig(), + // 15-Mar-2013, tatu: Important! Indicate that static typing is needed: + ((rootType == null) ? null :_typeFactory.constructType(rootType)), + /*PrettyPrinter*/null); + } + + /** + * @deprecated Since 2.5, use {@link #writerFor(TypeReference)} instead + */ + @Deprecated + public ObjectWriter writerWithType(TypeReference rootType) { + return _newWriter(getSerializationConfig(), + // 15-Mar-2013, tatu: Important! Indicate that static typing is needed: + ((rootType == null) ? null : _typeFactory.constructType(rootType)), + /*PrettyPrinter*/null); + } + + /** + * @deprecated Since 2.5, use {@link #writerFor(JavaType)} instead + */ + @Deprecated + public ObjectWriter writerWithType(JavaType rootType) { + return _newWriter(getSerializationConfig(), rootType, /*PrettyPrinter*/null); + } + + /* + /********************************************************** + /* Extended Public API: constructing ObjectReaders + /* for more advanced configuration + /********************************************************** + */ + + /** + * Factory method for constructing {@link ObjectReader} with + * default settings. Note that the resulting instance is NOT usable as is, + * without defining expected value type. + */ + public ObjectReader reader() { + return _newReader(getDeserializationConfig()).with(_injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} with + * specified feature enabled (compared to settings that this + * mapper instance has). + * Note that the resulting instance is NOT usable as is, + * without defining expected value type. + */ + public ObjectReader reader(DeserializationFeature feature) { + return _newReader(getDeserializationConfig().with(feature)); + } + + /** + * Factory method for constructing {@link ObjectReader} with + * specified features enabled (compared to settings that this + * mapper instance has). + * Note that the resulting instance is NOT usable as is, + * without defining expected value type. + */ + public ObjectReader reader(DeserializationFeature first, + DeserializationFeature... other) { + return _newReader(getDeserializationConfig().with(first, other)); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * update given Object (usually Bean, but can be a Collection or Map + * as well, but NOT an array) with JSON data. Deserialization occurs + * normally except that the root-level value in JSON is not used for + * instantiating a new object; instead give updateable object is used + * as root. + * Runtime type of value object is used for locating deserializer, + * unless overridden by other factory methods of {@link ObjectReader} + */ + public ObjectReader readerForUpdating(Object valueToUpdate) { + JavaType t = _typeFactory.constructType(valueToUpdate.getClass()); + return _newReader(getDeserializationConfig(), t, valueToUpdate, + null, _injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * read or update instances of specified type + * + * @since 2.6 + */ + public ObjectReader readerFor(JavaType type) { + return _newReader(getDeserializationConfig(), type, null, + null, _injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * read or update instances of specified type + * + * @since 2.6 + */ + public ObjectReader readerFor(Class type) { + return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, + null, _injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * read or update instances of specified type + * + * @since 2.6 + */ + public ObjectReader readerFor(TypeReference type) { + return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, + null, _injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * use specified {@link JsonNodeFactory} for constructing JSON trees. + */ + public ObjectReader reader(JsonNodeFactory f) { + return _newReader(getDeserializationConfig()).with(f); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * pass specific schema object to {@link JsonParser} used for + * reading content. + * + * @param schema Schema to pass to parser + */ + public ObjectReader reader(FormatSchema schema) { + _verifySchemaType(schema); + return _newReader(getDeserializationConfig(), null, null, + schema, _injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * use specified injectable values. + * + * @param injectableValues Injectable values to use + */ + public ObjectReader reader(InjectableValues injectableValues) { + return _newReader(getDeserializationConfig(), null, null, + null, injectableValues); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * deserialize objects using specified JSON View (filter). + */ + public ObjectReader readerWithView(Class view) { + return _newReader(getDeserializationConfig().withView(view)); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * use specified Base64 encoding variant for Base64-encoded binary data. + * + * @since 2.1 + */ + public ObjectReader reader(Base64Variant defaultBase64) { + return _newReader(getDeserializationConfig().with(defaultBase64)); + } + + /** + * Factory method for constructing {@link ObjectReader} that will + * use specified default attributes. + * + * @since 2.3 + */ + public ObjectReader reader(ContextAttributes attrs) { + return _newReader(getDeserializationConfig().with(attrs)); + } + + /** + * @deprecated Since 2.5, use {@link #readerFor(JavaType)} instead + */ + @Deprecated + public ObjectReader reader(JavaType type) { + return _newReader(getDeserializationConfig(), type, null, + null, _injectableValues); + } + + /** + * @deprecated Since 2.5, use {@link #readerFor(Class)} instead + */ + @Deprecated + public ObjectReader reader(Class type) { + return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, + null, _injectableValues); + } + + /** + * @deprecated Since 2.5, use {@link #readerFor(TypeReference)} instead + */ + @Deprecated + public ObjectReader reader(TypeReference type) { + return _newReader(getDeserializationConfig(), _typeFactory.constructType(type), null, + null, _injectableValues); + } + + /* + /********************************************************** + /* Extended Public API: convenience type conversion + /********************************************************** + */ + + /** + * Convenience method for doing two-step conversion from given value, into + * instance of given value type, if (but only if!) conversion is needed. + * If given value is already of requested type, value is returned as is. + *

+ * This method is functionally similar to first + * serializing given value into JSON, and then binding JSON data into value + * of given type, but should be more efficient since full serialization does + * not (need to) occur. + * However, same converters (serializers, deserializers) will be used as for + * data binding, meaning same object mapper configuration works. + *

+ * Note that it is possible that in some cases behavior does differ from + * full serialize-then-deserialize cycle: in most case differences are + * unintentional (that is, flaws to fix) and should be reported. + * It is not guaranteed, however, that the behavior is 100% the same: + * the goal is just to allow efficient value conversions for structurally + * compatible Objects, according to standard Jackson configuration. + *

+ * Further note that functianality is not designed to support "advanced" use + * cases, such as conversion of polymorphic values, or cases where Object Identity + * is used. + * + * @throws IllegalArgumentException If conversion fails due to incompatible type; + * if so, root cause will contain underlying checked exception data binding + * functionality threw + */ + @SuppressWarnings("unchecked") + public T convertValue(Object fromValue, Class toValueType) + throws IllegalArgumentException + { + // sanity check for null first: + if (fromValue == null) return null; + return (T) _convert(fromValue, _typeFactory.constructType(toValueType)); + } + + /** + * See {@link #convertValue(Object, Class)} + */ + @SuppressWarnings("unchecked") + public T convertValue(Object fromValue, TypeReference toValueTypeRef) + throws IllegalArgumentException + { + return (T) convertValue(fromValue, _typeFactory.constructType(toValueTypeRef)); + } + + /** + * See {@link #convertValue(Object, Class)} + */ + @SuppressWarnings("unchecked") + public T convertValue(Object fromValue, JavaType toValueType) + throws IllegalArgumentException + { + // sanity check for null first: + if (fromValue == null) return null; + return (T) _convert(fromValue, toValueType); + } + + /** + * Actual conversion implementation: instead of using existing read + * and write methods, much of code is inlined. Reason for this is + * that we must avoid root value wrapping/unwrapping both for efficiency and + * for correctness. If root value wrapping/unwrapping is actually desired, + * caller must use explicit writeValue and + * readValue methods. + */ + @SuppressWarnings("resource") + protected Object _convert(Object fromValue, JavaType toValueType) + throws IllegalArgumentException + { + // also, as per [databind#11], consider case for simple cast + /* But with caveats: one is that while everything is Object.class, we don't + * want to "optimize" that out; and the other is that we also do not want + * to lose conversions of generic types. + */ + Class targetType = toValueType.getRawClass(); + if (targetType != Object.class + && !toValueType.hasGenericTypes() + && targetType.isAssignableFrom(fromValue.getClass())) { + return fromValue; + } + + // Then use TokenBuffer, which is a JsonGenerator: + TokenBuffer buf = new TokenBuffer(this, false); + if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + buf = buf.forceUseOfBigDecimal(true); + } + try { + // inlined 'writeValue' with minor changes: + // first: disable wrapping when writing + SerializationConfig config = getSerializationConfig().without(SerializationFeature.WRAP_ROOT_VALUE); + // no need to check for closing of TokenBuffer + _serializerProvider(config).serializeValue(buf, fromValue); + + // then matching read, inlined 'readValue' with minor mods: + final JsonParser jp = buf.asParser(); + Object result; + // ok to pass in existing feature flags; unwrapping handled by mapper + final DeserializationConfig deserConfig = getDeserializationConfig(); + JsonToken t = _initForReading(jp); + if (t == JsonToken.VALUE_NULL) { + DeserializationContext ctxt = createDeserializationContext(jp, deserConfig); + result = _findRootDeserializer(ctxt, toValueType).getNullValue(ctxt); + } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { + result = null; + } else { // pointing to event other than null + DeserializationContext ctxt = createDeserializationContext(jp, deserConfig); + JsonDeserializer deser = _findRootDeserializer(ctxt, toValueType); + // note: no handling of unwarpping + result = deser.deserialize(jp, ctxt); + } + jp.close(); + return result; + } catch (IOException e) { // should not occur, no real i/o... + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + /* + /********************************************************** + /* Extended Public API: JSON Schema generation + /********************************************************** + */ + + /** + * Generate Json-schema + * instance for specified class. + * + * @param t The class to generate schema for + * @return Constructed JSON schema. + * + * @deprecated Since 2.6 use external JSON Schema generator (https://github.com/FasterXML/jackson-module-jsonSchema) + * (which under the hood calls {@link #acceptJsonFormatVisitor(JavaType, JsonFormatVisitorWrapper)}) + */ + @Deprecated + public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class t) + throws JsonMappingException { + return _serializerProvider(getSerializationConfig()).generateJsonSchema(t); + } + + /** + * Method for visiting type hierarchy for given type, using specified visitor. + *

+ * This method can be used for things like + * generating JSON Schema + * instance for specified type. + * + * @param type Type to generate schema for (possibly with generic signature) + * + * @since 2.1 + */ + public void acceptJsonFormatVisitor(Class type, JsonFormatVisitorWrapper visitor) + throws JsonMappingException + { + acceptJsonFormatVisitor(_typeFactory.constructType(type), visitor); + } + + /** + * Method for visiting type hierarchy for given type, using specified visitor. + * Visitation uses Serializer hierarchy and related properties + *

+ * This method can be used for things like + * generating JSON Schema + * instance for specified type. + * + * @param type Type to generate schema for (possibly with generic signature) + * + * @since 2.1 + */ + public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visitor) + throws JsonMappingException + { + if (type == null) { + throw new IllegalArgumentException("type must be provided"); + } + _serializerProvider(getSerializationConfig()).acceptJsonFormatVisitor(type, visitor); + } + + /* + /********************************************************** + /* Internal methods for serialization, overridable + /********************************************************** + */ + + /** + * Overridable helper method used for constructing + * {@link SerializerProvider} to use for serialization. + */ + protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) { + return _serializerProvider.createInstance(config, _serializerFactory); + } + + /** + * @deprecated Since 2.6, use {@link SerializationConfig#constructDefaultPrettyPrinter()} directly + */ + @Deprecated + protected PrettyPrinter _defaultPrettyPrinter() { + return _serializationConfig.constructDefaultPrettyPrinter(); + } + + /** + * Method called to configure the generator as necessary and then + * call write functionality + */ + protected final void _configAndWriteValue(JsonGenerator g, Object value) + throws IOException + { + SerializationConfig cfg = getSerializationConfig(); + cfg.initialize(g); // since 2.5 + if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { + _configAndWriteCloseable(g, value, cfg); + return; + } + boolean closed = false; + try { + _serializerProvider(cfg).serializeValue(g, value); + closed = true; + g.close(); + } finally { + /* won't try to close twice; also, must catch exception (so it + * will not mask exception that is pending) + */ + if (!closed) { + /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of + * structures, which typically causes more damage. + */ + g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + g.close(); + } catch (IOException ioe) { } + } + } + } + + @Deprecated // since 2.7, not used internally any more + protected final void _configAndWriteValue(JsonGenerator g, Object value, Class viewClass) + throws IOException + { + SerializationConfig cfg = getSerializationConfig().withView(viewClass); + cfg.initialize(g); // since 2.5 + + // [JACKSON-282]: consider Closeable + if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { + _configAndWriteCloseable(g, value, cfg); + return; + } + boolean closed = false; + try { + _serializerProvider(cfg).serializeValue(g, value); + closed = true; + g.close(); + } finally { + if (!closed) { + // 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of + // structures, which typically causes more damage. + g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + g.close(); + } catch (IOException ioe) { } + } + } + } + + /** + * Helper method used when value to serialize is {@link Closeable} and its close() + * method is to be called right after serialization has been called + */ + private final void _configAndWriteCloseable(JsonGenerator g, Object value, SerializationConfig cfg) + throws IOException + { + Closeable toClose = (Closeable) value; + try { + _serializerProvider(cfg).serializeValue(g, value); + JsonGenerator tmpGen = g; + g = null; + tmpGen.close(); + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } finally { + // Need to close both generator and value, as long as they haven't yet been closed + if (g != null) { + // 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of + // structures, which typically causes more damage. + g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + g.close(); + } catch (IOException ioe) { } + } + if (toClose != null) { + try { + toClose.close(); + } catch (IOException ioe) { } + } + } + } + + /** + * Helper method used when value to serialize is {@link Closeable} and its close() + * method is to be called right after serialization has been called + */ + private final void _writeCloseableValue(JsonGenerator g, Object value, SerializationConfig cfg) + throws IOException + { + Closeable toClose = (Closeable) value; + try { + _serializerProvider(cfg).serializeValue(g, value); + if (cfg.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + g.flush(); + } + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } finally { + if (toClose != null) { + try { + toClose.close(); + } catch (IOException ioe) { } + } + } + } + + /* + /********************************************************** + /* Internal methods for deserialization, overridable + /********************************************************** + */ + + /** + * Internal helper method called to create an instance of {@link DeserializationContext} + * for deserializing a single root value. + * Can be overridden if a custom context is needed. + */ + protected DefaultDeserializationContext createDeserializationContext(JsonParser jp, + DeserializationConfig cfg) { + return _deserializationContext.createInstance(cfg, jp, _injectableValues); + } + + /** + * Actual implementation of value reading+binding operation. + */ + protected Object _readValue(DeserializationConfig cfg, JsonParser jp, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + /* First: may need to read the next token, to initialize + * state (either before first read from parser, or after + * previous token has been cleared) + */ + Object result; + JsonToken t = _initForReading(jp); + if (t == JsonToken.VALUE_NULL) { + // [JACKSON-643]: Ask JsonDeserializer what 'null value' to use: + DeserializationContext ctxt = createDeserializationContext(jp, cfg); + result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt); + } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { + result = null; + } else { // pointing to event other than null + DeserializationContext ctxt = createDeserializationContext(jp, cfg); + JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); + // ok, let's get the value + if (cfg.useRootWrapping()) { + result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser); + } else { + result = deser.deserialize(jp, ctxt); + } + } + // Need to consume the token too + jp.clearCurrentToken(); + return result; + } + + protected Object _readMapAndClose(JsonParser jp, JavaType valueType) + throws IOException, JsonParseException, JsonMappingException + { + try { + Object result; + JsonToken t = _initForReading(jp); + if (t == JsonToken.VALUE_NULL) { + // [JACKSON-643]: Ask JsonDeserializer what 'null value' to use: + DeserializationContext ctxt = createDeserializationContext(jp, + getDeserializationConfig()); + result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt); + } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { + result = null; + } else { + DeserializationConfig cfg = getDeserializationConfig(); + DeserializationContext ctxt = createDeserializationContext(jp, cfg); + JsonDeserializer deser = _findRootDeserializer(ctxt, valueType); + if (cfg.useRootWrapping()) { + result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser); + } else { + result = deser.deserialize(jp, ctxt); + } + ctxt.checkUnresolvedObjectId(); + } + // Need to consume the token too + jp.clearCurrentToken(); + return result; + } finally { + try { + jp.close(); + } catch (IOException ioe) { } + } + } + + /** + * Method called to ensure that given parser is ready for reading + * content for data binding. + * + * @return First token to be used for data binding after this call: + * can never be null as exception will be thrown if parser can not + * provide more tokens. + * + * @throws IOException if the underlying input source has problems during + * parsing + * @throws JsonParseException if parser has problems parsing content + * @throws JsonMappingException if the parser does not have any more + * content to map (note: Json "null" value is considered content; + * enf-of-stream not) + */ + protected JsonToken _initForReading(JsonParser p) throws IOException + { + _deserializationConfig.initialize(p); // since 2.5 + + /* First: must point to a token; if not pointing to one, advance. + * This occurs before first read from JsonParser, as well as + * after clearing of current token. + */ + JsonToken t = p.getCurrentToken(); + if (t == null) { + // and then we must get something... + t = p.nextToken(); + if (t == null) { + // Throw mapping exception, since it's failure to map, + // not an actual parsing problem + throw JsonMappingException.from(p, "No content to map due to end-of-input"); + } + } + return t; + } + + protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt, + DeserializationConfig config, + JavaType rootType, JsonDeserializer deser) + throws IOException + { + PropertyName expRootName = config.findRootName(rootType); + // 12-Jun-2015, tatu: Should try to support namespaces etc but... + String expSimpleName = expRootName.getSimpleName(); + if (p.getCurrentToken() != JsonToken.START_OBJECT) { + throw JsonMappingException.from(p, "Current token not START_OBJECT (needed to unwrap root name '" + +expSimpleName+"'), but "+p.getCurrentToken()); + } + if (p.nextToken() != JsonToken.FIELD_NAME) { + throw JsonMappingException.from(p, "Current token not FIELD_NAME (to contain expected root name '" + +expSimpleName+"'), but "+p.getCurrentToken()); + } + String actualName = p.getCurrentName(); + if (!expSimpleName.equals(actualName)) { + throw JsonMappingException.from(p, "Root name '"+actualName+"' does not match expected ('" + +expSimpleName+"') for type "+rootType); + } + // ok, then move to value itself.... + p.nextToken(); + Object result = deser.deserialize(p, ctxt); + // and last, verify that we now get matching END_OBJECT + if (p.nextToken() != JsonToken.END_OBJECT) { + throw JsonMappingException.from(p, "Current token not END_OBJECT (to match wrapper object with root name '" + +expSimpleName+"'), but "+p.getCurrentToken()); + } + return result; + } + + /* + /********************************************************** + /* Internal methods, other + /********************************************************** + */ + + /** + * Method called to locate deserializer for the passed root-level value. + */ + protected JsonDeserializer _findRootDeserializer(DeserializationContext ctxt, + JavaType valueType) + throws JsonMappingException + { + // First: have we already seen it? + JsonDeserializer deser = _rootDeserializers.get(valueType); + if (deser != null) { + return deser; + } + // Nope: need to ask provider to resolve it + deser = ctxt.findRootValueDeserializer(valueType); + if (deser == null) { // can this happen? + throw JsonMappingException.from(ctxt, + "Can not find a deserializer for type "+valueType); + } + _rootDeserializers.put(valueType, deser); + return deser; + } + + /** + * @since 2.2 + */ + protected void _verifySchemaType(FormatSchema schema) + { + if (schema != null) { + if (!_jsonFactory.canUseSchema(schema)) { + throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName() + +" for format "+_jsonFactory.getFormatName()); + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectReader.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectReader.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectReader.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1888 @@ +package com.fasterxml.jackson.databind; + +import java.io.*; +import java.net.URL; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.FilteringParserDelegate; +import com.fasterxml.jackson.core.filter.JsonPointerBasedFilter; +import com.fasterxml.jackson.core.filter.TokenFilter; +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; +import com.fasterxml.jackson.databind.deser.DataFormatReaders; +import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.TreeTraversingParser; +import com.fasterxml.jackson.databind.type.SimpleType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Builder object that can be used for per-serialization configuration of + * deserialization parameters, such as root type to use or object + * to update (instead of constructing new instance). + *

+ * Uses "mutant factory" pattern so that instances are immutable + * (and thus fully thread-safe with no external synchronization); + * new instances are constructed for different configurations. + * Instances are initially constructed by {@link ObjectMapper} and can be + * reused, shared, cached; both because of thread-safety and because + * instances are relatively light-weight. + */ +public class ObjectReader + extends ObjectCodec + implements Versioned, java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1L; // since 2.5 + + private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class); + + /* + /********************************************************** + /* Immutable configuration from ObjectMapper + /********************************************************** + */ + + /** + * General serialization configuration settings; while immutable, + * can use copy-constructor to create modified instances as necessary. + */ + protected final DeserializationConfig _config; + + /** + * Blueprint instance of deserialization context; used for creating + * actual instance when needed. + */ + protected final DefaultDeserializationContext _context; + + /** + * Factory used for constructing {@link JsonGenerator}s + */ + protected final JsonFactory _parserFactory; + + /** + * Flag that indicates whether root values are expected to be unwrapped or not + */ + protected final boolean _unwrapRoot; + + /** + * Filter to be consider for JsonParser. + * Default value to be null as filter not considered. + */ + private final TokenFilter _filter; + + /* + /********************************************************** + /* Configuration that can be changed during building + /********************************************************** + */ + + /** + * Declared type of value to instantiate during deserialization. + * Defines which deserializer to use; as well as base type of instance + * to construct if an updatable value is not configured to be used + * (subject to changes by embedded type information, for polymorphic + * types). If {@link #_valueToUpdate} is non-null, only used for + * locating deserializer. + */ + protected final JavaType _valueType; + + /** + * We may pre-fetch deserializer as soon as {@link #_valueType} + * is known, and if so, reuse it afterwards. + * This allows avoiding further deserializer lookups and increases + * performance a bit on cases where readers are reused. + * + * @since 2.1 + */ + protected final JsonDeserializer _rootDeserializer; + + /** + * Instance to update with data binding; if any. If null, + * a new instance is created, if non-null, properties of + * this value object will be updated instead. + * Note that value can be of almost any type, except not + * {@link com.fasterxml.jackson.databind.type.ArrayType}; array + * types can not be modified because array size is immutable. + */ + protected final Object _valueToUpdate; + + /** + * When using data format that uses a schema, schema is passed + * to parser. + */ + protected final FormatSchema _schema; + + /** + * Values that can be injected during deserialization, if any. + */ + protected final InjectableValues _injectableValues; + + /** + * Optional detector used for auto-detecting data format that byte-based + * input uses. + *

+ * NOTE: If defined non-null, readValue() methods that take + * {@link Reader} or {@link String} input will fail with exception, + * because format-detection only works on byte-sources. Also, if format + * can not be detect reliably (as per detector settings), + * a {@link JsonParseException} will be thrown). + * + * @since 2.1 + */ + protected final DataFormatReaders _dataFormatReaders; + + /* + /********************************************************** + /* Caching + /********************************************************** + */ + + /** + * Root-level cached deserializers. + * Passed by {@link ObjectMapper}, shared with it. + */ + final protected ConcurrentHashMap> _rootDeserializers; + + /* + /********************************************************** + /* Life-cycle, construction + /********************************************************** + */ + + /** + * Constructor used by {@link ObjectMapper} for initial instantiation + */ + protected ObjectReader(ObjectMapper mapper, DeserializationConfig config) { + this(mapper, config, null, null, null, null); + } + + /** + * Constructor called when a root deserializer should be fetched based + * on other configuration. + */ + protected ObjectReader(ObjectMapper mapper, DeserializationConfig config, + JavaType valueType, Object valueToUpdate, + FormatSchema schema, InjectableValues injectableValues) + { + _config = config; + _context = mapper._deserializationContext; + _rootDeserializers = mapper._rootDeserializers; + _parserFactory = mapper._jsonFactory; + _valueType = valueType; + _valueToUpdate = valueToUpdate; + if (valueToUpdate != null && valueType.isArrayType()) { + throw new IllegalArgumentException("Can not update an array value"); + } + _schema = schema; + _injectableValues = injectableValues; + _unwrapRoot = config.useRootWrapping(); + + _rootDeserializer = _prefetchRootDeserializer(valueType); + _dataFormatReaders = null; + _filter = null; + } + + /** + * Copy constructor used for building variations. + */ + protected ObjectReader(ObjectReader base, DeserializationConfig config, + JavaType valueType, JsonDeserializer rootDeser, Object valueToUpdate, + FormatSchema schema, InjectableValues injectableValues, + DataFormatReaders dataFormatReaders) + { + _config = config; + _context = base._context; + + _rootDeserializers = base._rootDeserializers; + _parserFactory = base._parserFactory; + + _valueType = valueType; + _rootDeserializer = rootDeser; + _valueToUpdate = valueToUpdate; + if (valueToUpdate != null && valueType.isArrayType()) { + throw new IllegalArgumentException("Can not update an array value"); + } + _schema = schema; + _injectableValues = injectableValues; + _unwrapRoot = config.useRootWrapping(); + _dataFormatReaders = dataFormatReaders; + _filter = base._filter; + } + + /** + * Copy constructor used when modifying simple feature flags + */ + protected ObjectReader(ObjectReader base, DeserializationConfig config) + { + _config = config; + _context = base._context; + + _rootDeserializers = base._rootDeserializers; + _parserFactory = base._parserFactory; + + _valueType = base._valueType; + _rootDeserializer = base._rootDeserializer; + _valueToUpdate = base._valueToUpdate; + _schema = base._schema; + _injectableValues = base._injectableValues; + _unwrapRoot = config.useRootWrapping(); + _dataFormatReaders = base._dataFormatReaders; + _filter = base._filter; + } + + protected ObjectReader(ObjectReader base, JsonFactory f) + { + // may need to override ordering, based on data format capabilities + _config = base._config + .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, f.requiresPropertyOrdering()); + _context = base._context; + + _rootDeserializers = base._rootDeserializers; + _parserFactory = f; + + _valueType = base._valueType; + _rootDeserializer = base._rootDeserializer; + _valueToUpdate = base._valueToUpdate; + _schema = base._schema; + _injectableValues = base._injectableValues; + _unwrapRoot = base._unwrapRoot; + _dataFormatReaders = base._dataFormatReaders; + _filter = base._filter; + } + + protected ObjectReader(ObjectReader base, TokenFilter filter) { + _config = base._config; + _context = base._context; + _rootDeserializers = base._rootDeserializers; + _parserFactory = base._parserFactory; + _valueType = base._valueType; + _rootDeserializer = base._rootDeserializer; + _valueToUpdate = base._valueToUpdate; + _schema = base._schema; + _injectableValues = base._injectableValues; + _unwrapRoot = base._unwrapRoot; + _dataFormatReaders = base._dataFormatReaders; + _filter = filter; + } + + /** + * Method that will return version information stored in and read from jar + * that contains this class. + */ + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Methods sub-classes MUST override, used for constructing + /* reader instances, (re)configuring parser instances + /* Added in 2.5 + /********************************************************** + */ + + /** + * Overridable factory method called by various "withXxx()" methods + * + * @since 2.5 + */ + protected ObjectReader _new(ObjectReader base, JsonFactory f) { + return new ObjectReader(base, f); + } + + /** + * Overridable factory method called by various "withXxx()" methods + * + * @since 2.5 + */ + protected ObjectReader _new(ObjectReader base, DeserializationConfig config) { + return new ObjectReader(base, config); + } + + /** + * Overridable factory method called by various "withXxx()" methods + * + * @since 2.5 + */ + protected ObjectReader _new(ObjectReader base, DeserializationConfig config, + JavaType valueType, JsonDeserializer rootDeser, Object valueToUpdate, + FormatSchema schema, InjectableValues injectableValues, + DataFormatReaders dataFormatReaders) { + return new ObjectReader(base, config, valueType, rootDeser, valueToUpdate, + schema, injectableValues, dataFormatReaders); + } + + /** + * Factory method used to create {@link MappingIterator} instances; + * either default, or custom subtype. + * + * @since 2.5 + */ + protected MappingIterator _newIterator(JsonParser p, DeserializationContext ctxt, + JsonDeserializer deser, boolean parserManaged) + { + return new MappingIterator(_valueType, p, ctxt, + deser, parserManaged, _valueToUpdate); + } + + /* + /********************************************************** + /* Methods sub-classes may choose to override, if customized + /* initialization is needed. + /********************************************************** + */ + + /** + * NOTE: changed from static to non-static in 2.5; unfortunate but + * necessary change to support overridability + */ + protected JsonToken _initForReading(JsonParser p) throws IOException + { + if (_schema != null) { + p.setSchema(_schema); + } + _config.initialize(p); // since 2.5 + + /* First: must point to a token; if not pointing to one, advance. + * This occurs before first read from JsonParser, as well as + * after clearing of current token. + */ + JsonToken t = p.getCurrentToken(); + if (t == null) { // and then we must get something... + t = p.nextToken(); + if (t == null) { + // Throw mapping exception, since it's failure to map, not an actual parsing problem + throw JsonMappingException.from(p, "No content to map due to end-of-input"); + } + } + return t; + } + + /** + * Alternative to {@link #_initForReading(JsonParser)} used in cases where reading + * of multiple values means that we may or may not want to advance the stream, + * but need to do other initialization. + *

+ * Base implementation only sets configured {@link FormatSchema}, if any, on parser. + * + * @since 2.5 + */ + protected void _initForMultiRead(JsonParser p) throws IOException { + if (_schema != null) { + p.setSchema(_schema); + } + _config.initialize(p); // since 2.5 + } + + /* + /********************************************************** + /* Life-cycle, fluent factory methods for DeserializationFeatures + /********************************************************** + */ + + /** + * Method for constructing a new reader instance that is configured + * with specified feature enabled. + */ + public ObjectReader with(DeserializationFeature feature) { + return _with(_config.with(feature)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features enabled. + */ + public ObjectReader with(DeserializationFeature first, + DeserializationFeature... other) + { + return _with(_config.with(first, other)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features enabled. + */ + public ObjectReader withFeatures(DeserializationFeature... features) { + return _with(_config.withFeatures(features)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified feature disabled. + */ + public ObjectReader without(DeserializationFeature feature) { + return _with(_config.without(feature)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features disabled. + */ + public ObjectReader without(DeserializationFeature first, + DeserializationFeature... other) { + return _with(_config.without(first, other)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features disabled. + */ + public ObjectReader withoutFeatures(DeserializationFeature... features) { + return _with(_config.withoutFeatures(features)); + } + + /* + /********************************************************** + /* Life-cycle, fluent factory methods for JsonParser.Features + /********************************************************** + */ + + /** + * Method for constructing a new reader instance that is configured + * with specified feature enabled. + */ + public ObjectReader with(JsonParser.Feature feature) { + return _with(_config.with(feature)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features enabled. + */ + public ObjectReader withFeatures(JsonParser.Feature... features) { + return _with(_config.withFeatures(features)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified feature disabled. + */ + public ObjectReader without(JsonParser.Feature feature) { + return _with(_config.without(feature)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features disabled. + */ + public ObjectReader withoutFeatures(JsonParser.Feature... features) { + return _with(_config.withoutFeatures(features)); + } + + /* + /********************************************************** + /* Life-cycle, fluent factory methods for FormatFeature (2.7) + /********************************************************** + */ + + /** + * Method for constructing a new reader instance that is configured + * with specified feature enabled. + * + * @since 2.7 + */ + public ObjectReader with(FormatFeature feature) { + return _with(_config.with(feature)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features enabled. + * + * @since 2.7 + */ + public ObjectReader withFeatures(FormatFeature... features) { + return _with(_config.withFeatures(features)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified feature disabled. + * + * @since 2.7 + */ + public ObjectReader without(FormatFeature feature) { + return _with(_config.without(feature)); + } + + /** + * Method for constructing a new reader instance that is configured + * with specified features disabled. + * + * @since 2.7 + */ + public ObjectReader withoutFeatures(FormatFeature... features) { + return _with(_config.withoutFeatures(features)); + } + + /* + /********************************************************** + /* Life-cycle, fluent factory methods, other + /********************************************************** + */ + + /** + * Mutant factory method that will construct a new instance that has + * specified underlying {@link DeserializationConfig}. + *

+ * NOTE: use of this method is not recommended, as there are many other + * re-configuration methods available. + */ + public ObjectReader with(DeserializationConfig config) { + return _with(config); + } + + /** + * Method for constructing a new instance with configuration that uses + * passed {@link InjectableValues} to provide injectable values. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectReader with(InjectableValues injectableValues) + { + if (_injectableValues == injectableValues) { + return this; + } + return _new(this, _config, + _valueType, _rootDeserializer, _valueToUpdate, + _schema, injectableValues, _dataFormatReaders); + } + + /** + * Method for constructing a new reader instance with configuration that uses + * passed {@link JsonNodeFactory} for constructing {@link JsonNode} + * instances. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectReader with(JsonNodeFactory f) { + return _with(_config.with(f)); + } + + /** + * Method for constructing a new reader instance with configuration that uses + * passed {@link JsonFactory} for constructing underlying Readers. + *

+ * NOTE: only factories that DO NOT REQUIRE SPECIAL MAPPERS + * (that is, ones that return false for + * {@link JsonFactory#requiresCustomCodec()}) can be used: trying + * to use one that requires custom codec will throw exception + * + * @since 2.1 + */ + public ObjectReader with(JsonFactory f) { + if (f == _parserFactory) { + return this; + } + ObjectReader r = _new(this, f); + // Also, try re-linking, if possible... + if (f.getCodec() == null) { + f.setCodec(r); + } + return r; + } + + /** + * Method for constructing a new instance with configuration that + * specifies what root name to expect for "root name unwrapping". + * See {@link DeserializationConfig#withRootName(String)} for + * details. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectReader withRootName(String rootName) { + return _with(_config.withRootName(rootName)); + } + + /** + * @since 2.6 + */ + public ObjectReader withRootName(PropertyName rootName) { + return _with(_config.withRootName(rootName)); + } + + /** + * Convenience method that is same as calling: + * + * withRootName("") + * + * which will forcibly prevent use of root name wrapping when writing + * values with this {@link ObjectReader}. + * + * @since 2.6 + */ + public ObjectReader withoutRootName() { + return _with(_config.withRootName(PropertyName.NO_NAME)); + } + + /** + * Method for constructing a new instance with configuration that + * passes specified {@link FormatSchema} to {@link JsonParser} that + * is constructed for parsing content. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectReader with(FormatSchema schema) + { + if (_schema == schema) { + return this; + } + _verifySchemaType(schema); + return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate, + schema, _injectableValues, _dataFormatReaders); + } + + /** + * Method for constructing a new reader instance that is configured + * to data bind into specified type. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + * + * @since 2.5 + */ + public ObjectReader forType(JavaType valueType) + { + if (valueType != null && valueType.equals(_valueType)) { + return this; + } + JsonDeserializer rootDeser = _prefetchRootDeserializer(valueType); + // type is stored here, no need to make a copy of config + DataFormatReaders det = _dataFormatReaders; + if (det != null) { + det = det.withType(valueType); + } + return _new(this, _config, valueType, rootDeser, + _valueToUpdate, _schema, _injectableValues, det); + } + + /** + * Method for constructing a new reader instance that is configured + * to data bind into specified type. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + * + * @since 2.5 + */ + public ObjectReader forType(Class valueType) { + return forType(_config.constructType(valueType)); + } + + /** + * Method for constructing a new reader instance that is configured + * to data bind into specified type. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + * + * @since 2.5 + */ + public ObjectReader forType(TypeReference valueTypeRef) { + return forType(_config.getTypeFactory().constructType(valueTypeRef.getType())); + } + + /** + * @deprecated since 2.5 Use {@link #forType(JavaType)} instead + */ + @Deprecated + public ObjectReader withType(JavaType valueType) { + return forType(valueType); + } + + /** + * @deprecated since 2.5 Use {@link #forType(Class)} instead + */ + @Deprecated + public ObjectReader withType(Class valueType) { + return forType(_config.constructType(valueType)); + } + + /** + * @deprecated since 2.5 Use {@link #forType(Class)} instead + */ + @Deprecated + public ObjectReader withType(java.lang.reflect.Type valueType) { + return forType(_config.getTypeFactory().constructType(valueType)); + } + + /** + * @deprecated since 2.5 Use {@link #forType(TypeReference)} instead + */ + @Deprecated + public ObjectReader withType(TypeReference valueTypeRef) { + return forType(_config.getTypeFactory().constructType(valueTypeRef.getType())); + } + + /** + * Method for constructing a new instance with configuration that + * updates passed Object (as root value), instead of constructing + * a new value. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectReader withValueToUpdate(Object value) + { + if (value == _valueToUpdate) return this; + if (value == null) { + throw new IllegalArgumentException("cat not update null value"); + } + JavaType t; + + /* no real benefit from pre-fetching, as updating readers are much + * less likely to be reused, and value type may also be forced + * with a later chained call... + */ + if (_valueType == null) { + t = _config.constructType(value.getClass()); + } else { + t = _valueType; + } + return _new(this, _config, t, _rootDeserializer, value, + _schema, _injectableValues, _dataFormatReaders); + } + + /** + * Method for constructing a new instance with configuration that + * uses specified View for filtering. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectReader withView(Class activeView) { + return _with(_config.withView(activeView)); + } + + public ObjectReader with(Locale l) { + return _with(_config.with(l)); + } + + public ObjectReader with(TimeZone tz) { + return _with(_config.with(tz)); + } + + public ObjectReader withHandler(DeserializationProblemHandler h) { + return _with(_config.withHandler(h)); + } + + public ObjectReader with(Base64Variant defaultBase64) { + return _with(_config.with(defaultBase64)); + } + + /** + * Fluent factory method for constructing a reader that will try to + * auto-detect underlying data format, using specified list of + * {@link JsonFactory} instances, and default {@link DataFormatReaders} settings + * (for customized {@link DataFormatReaders}, you can construct instance yourself). + * to construct appropriate {@link JsonParser} for actual parsing. + *

+ * Note: since format detection only works with byte sources, it is possible to + * get a failure from some 'readValue()' methods. Also, if input can not be reliably + * (enough) detected as one of specified types, an exception will be thrown. + *

+ * Note: not all {@link JsonFactory} types can be passed: specifically, ones that + * require "custom codec" (like XML factory) will not work. Instead, use + * method that takes {@link ObjectReader} instances instead of factories. + * + * @param readers Data formats accepted, in decreasing order of priority (that is, + * matches checked in listed order, first match wins) + * + * @return Newly configured writer instance + * + * @since 2.1 + */ + public ObjectReader withFormatDetection(ObjectReader... readers) { + return withFormatDetection(new DataFormatReaders(readers)); + } + + /** + * Fluent factory method for constructing a reader that will try to + * auto-detect underlying data format, using specified + * {@link DataFormatReaders}. + *

+ * NOTE: since format detection only works with byte sources, it is possible to + * get a failure from some 'readValue()' methods. Also, if input can not be reliably + * (enough) detected as one of specified types, an exception will be thrown. + * + * @param readers DataFormatReaders to use for detecting underlying format. + * + * @return Newly configured writer instance + * + * @since 2.1 + */ + public ObjectReader withFormatDetection(DataFormatReaders readers) { + return _new(this, _config, _valueType, _rootDeserializer, _valueToUpdate, + _schema, _injectableValues, readers); + } + + /** + * @since 2.3 + */ + public ObjectReader with(ContextAttributes attrs) { + return _with(_config.with(attrs)); + } + + /** + * @since 2.3 + */ + public ObjectReader withAttributes(Map attrs) { + return _with(_config.withAttributes(attrs)); + } + + /** + * @since 2.3 + */ + public ObjectReader withAttribute(Object key, Object value) { + return _with( _config.withAttribute(key, value)); + } + + /** + * @since 2.3 + */ + public ObjectReader withoutAttribute(Object key) { + return _with(_config.withoutAttribute(key)); + } + + /* + /********************************************************** + /* Overridable factory methods may override + /********************************************************** + */ + + protected ObjectReader _with(DeserializationConfig newConfig) { + if (newConfig == _config) { + return this; + } + ObjectReader r = _new(this, newConfig); + if (_dataFormatReaders != null) { + r = r.withFormatDetection(_dataFormatReaders.with(newConfig)); + } + return r; + } + + /* + /********************************************************** + /* Simple accessors + /********************************************************** + */ + + public boolean isEnabled(DeserializationFeature f) { + return _config.isEnabled(f); + } + + public boolean isEnabled(MapperFeature f) { + return _config.isEnabled(f); + } + + public boolean isEnabled(JsonParser.Feature f) { + return _parserFactory.isEnabled(f); + } + + /** + * @since 2.2 + */ + public DeserializationConfig getConfig() { + return _config; + } + + /** + * @since 2.1 + */ + @Override + public JsonFactory getFactory() { + return _parserFactory; + } + + public TypeFactory getTypeFactory() { + return _config.getTypeFactory(); + } + + /** + * @since 2.3 + */ + public ContextAttributes getAttributes() { + return _config.getAttributes(); + } + + /** + * @since 2.6 + */ + public InjectableValues getInjectableValues() { + return _injectableValues; + } + + /* + /********************************************************** + /* Deserialization methods; basic ones to support ObjectCodec first + /* (ones that take JsonParser) + /********************************************************** + */ + + /** + * Method that binds content read using given parser, using + * configuration of this reader, including expected result type. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @SuppressWarnings("unchecked") + public T readValue(JsonParser p) throws IOException + { + return (T) _bind(p, _valueToUpdate); + } + + /** + * Convenience method that binds content read using given parser, using + * configuration of this reader, except that expected value type + * is specified with the call (instead of currently configured root type). + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @SuppressWarnings("unchecked") + @Override + public T readValue(JsonParser p, Class valueType) throws IOException + { + return (T) forType(valueType).readValue(p); + } + + /** + * Convenience method that binds content read using given parser, using + * configuration of this reader, except that expected value type + * is specified with the call (instead of currently configured root type). + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @SuppressWarnings("unchecked") + @Override + public T readValue(JsonParser p, TypeReference valueTypeRef) throws IOException + { + return (T) forType(valueTypeRef).readValue(p); + } + + /** + * Convenience method that binds content read using given parser, using + * configuration of this reader, except that expected value type + * is specified with the call (instead of currently configured root type). + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @Override + @SuppressWarnings("unchecked") + public T readValue(JsonParser p, ResolvedType valueType) throws IOException, JsonProcessingException { + return (T) forType((JavaType)valueType).readValue(p); + } + + /** + * Type-safe overloaded method, basically alias for {@link #readValue(JsonParser, ResolvedType)}. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @SuppressWarnings("unchecked") + public T readValue(JsonParser p, JavaType valueType) throws IOException { + return (T) forType(valueType).readValue(p); + } + + /** + * Convenience method that is equivalent to: + *

+     *   withType(valueType).readValues(p);
+     *
+ *

+ * Method reads a sequence of Objects from parser stream. + * Sequence can be either root-level "unwrapped" sequence (without surrounding + * JSON array), or a sequence contained in a JSON Array. + * In either case {@link JsonParser} MUST point to the first token of + * the first element, OR not point to any token (in which case it is advanced + * to the next token). This means, specifically, that for wrapped sequences, + * parser MUST NOT point to the surrounding START_ARRAY (one that + * contains values to read) but rather to the token following it which is the first + * token of the first value to read. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @Override + public Iterator readValues(JsonParser p, Class valueType) throws IOException { + return forType(valueType).readValues(p); + } + + /** + * Convenience method that is equivalent to: + *

+     *   withType(valueTypeRef).readValues(p);
+     *
+ *

+ * Method reads a sequence of Objects from parser stream. + * Sequence can be either root-level "unwrapped" sequence (without surrounding + * JSON array), or a sequence contained in a JSON Array. + * In either case {@link JsonParser} MUST point to the first token of + * the first element, OR not point to any token (in which case it is advanced + * to the next token). This means, specifically, that for wrapped sequences, + * parser MUST NOT point to the surrounding START_ARRAY (one that + * contains values to read) but rather to the token following it which is the first + * token of the first value to read. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @Override + public Iterator readValues(JsonParser p, TypeReference valueTypeRef) throws IOException { + return forType(valueTypeRef).readValues(p); + } + + /** + * Convenience method that is equivalent to: + *

+     *   withType(valueType).readValues(p);
+     *
+ *

+ * Method reads a sequence of Objects from parser stream. + * Sequence can be either root-level "unwrapped" sequence (without surrounding + * JSON array), or a sequence contained in a JSON Array. + * In either case {@link JsonParser} MUST point to the first token of + * the first element, OR not point to any token (in which case it is advanced + * to the next token). This means, specifically, that for wrapped sequences, + * parser MUST NOT point to the surrounding START_ARRAY (one that + * contains values to read) but rather to the token following it which is the first + * token of the first value to read. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @Override + public Iterator readValues(JsonParser p, ResolvedType valueType) throws IOException { + return readValues(p, (JavaType) valueType); + } + + /** + * Convenience method that is equivalent to: + *

+     *   withType(valueType).readValues(p);
+     *
+ *

+ * Method reads a sequence of Objects from parser stream. + * Sequence can be either root-level "unwrapped" sequence (without surrounding + * JSON array), or a sequence contained in a JSON Array. + * In either case {@link JsonParser} MUST point to the first token of + * the first element, OR not point to any token (in which case it is advanced + * to the next token). This means, specifically, that for wrapped sequences, + * parser MUST NOT point to the surrounding START_ARRAY (one that + * contains values to read) but rather to the token following it which is the first + * token of the first value to read. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + public Iterator readValues(JsonParser p, JavaType valueType) throws IOException { + return forType(valueType).readValues(p); + } + + /* + /********************************************************** + /* TreeCodec impl + /********************************************************** + */ + + @Override + public JsonNode createArrayNode() { + return _config.getNodeFactory().arrayNode(); + } + + @Override + public JsonNode createObjectNode() { + return _config.getNodeFactory().objectNode(); + } + + @Override + public JsonParser treeAsTokens(TreeNode n) { + return new TreeTraversingParser((JsonNode) n, this); + } + + /** + * Convenience method that binds content read using given parser, using + * configuration of this reader, except that content is bound as + * JSON tree instead of configured root value type. + *

+ * Note: if an object was specified with {@link #withValueToUpdate}, it + * will be ignored. + *

+ * NOTE: this method never tries to auto-detect format, since actual + * (data-format specific) parser is given. + */ + @SuppressWarnings("unchecked") + @Override + public T readTree(JsonParser p) throws IOException { + return (T) _bindAsTree(p); + } + + @Override + public void writeTree(JsonGenerator jgen, TreeNode rootNode) { + throw new UnsupportedOperationException(); + } + + /* + /********************************************************** + /* Deserialization methods; others similar to what ObjectMapper has + /********************************************************** + */ + + /** + * Method that binds content read from given input source, + * using configuration of this reader. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + */ + @SuppressWarnings("unchecked") + public T readValue(InputStream src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + } + + /** + * Method that binds content read from given input source, + * using configuration of this reader. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + */ + @SuppressWarnings("unchecked") + public T readValue(Reader src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(src); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + } + + /** + * Method that binds content read from given JSON string, + * using configuration of this reader. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + */ + @SuppressWarnings("unchecked") + public T readValue(String src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(src); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + } + + /** + * Method that binds content read from given byte array, + * using configuration of this reader. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + */ + @SuppressWarnings("unchecked") + public T readValue(byte[] src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return (T) _detectBindAndClose(src, 0, src.length); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + } + + /** + * Method that binds content read from given byte array, + * using configuration of this reader. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + */ + @SuppressWarnings("unchecked") + public T readValue(byte[] src, int offset, int length) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return (T) _detectBindAndClose(src, offset, length); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src, offset, length), + false)); + } + + @SuppressWarnings("unchecked") + public T readValue(File src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + } + + /** + * Method that binds content read from given input source, + * using configuration of this reader. + * Value return is either newly constructed, or root value that + * was specified with {@link #withValueToUpdate(Object)}. + */ + @SuppressWarnings("unchecked") + public T readValue(URL src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true); + } + + return (T) _bindAndClose(_considerFilter(_parserFactory.createParser(src), false)); + } + + /** + * Convenience method for converting results from given JSON tree into given + * value type. Basically short-cut for: + *

+     *   objectReader.readValue(src.traverse())
+     *
+ */ + @SuppressWarnings("unchecked") + public T readValue(JsonNode src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(src); + } + + return (T) _bindAndClose(_considerFilter(treeAsTokens(src), false)); + } + + /** + * Method that reads content from given input source, + * using configuration of this reader, and binds it as JSON Tree. + *

+ * Note that if an object was specified with a call to + * {@link #withValueToUpdate(Object)} + * it will just be ignored; result is always a newly constructed + * {@link JsonNode} instance. + */ + public JsonNode readTree(InputStream in) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return _detectBindAndCloseAsTree(in); + } + + return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(in), false)); + } + + /** + * Method that reads content from given input source, + * using configuration of this reader, and binds it as JSON Tree. + *

+ * Note that if an object was specified with a call to + * {@link #withValueToUpdate(Object)} + * it will just be ignored; result is always a newly constructed + * {@link JsonNode} instance. + */ + public JsonNode readTree(Reader r) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(r); + } + + return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(r), false)); + } + + /** + * Method that reads content from given JSON input String, + * using configuration of this reader, and binds it as JSON Tree. + *

+ * Note that if an object was specified with a call to + * {@link #withValueToUpdate(Object)} + * it will just be ignored; result is always a newly constructed + * {@link JsonNode} instance. + */ + public JsonNode readTree(String json) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(json); + } + + return _bindAndCloseAsTree(_considerFilter(_parserFactory.createParser(json), false)); + } + + /* + /********************************************************** + /* Deserialization methods; reading sequence of values + /********************************************************** + */ + + /** + * Method for reading sequence of Objects from parser stream. + *

+ * Sequence can be either root-level "unwrapped" sequence (without surrounding + * JSON array), or a sequence contained in a JSON Array. + * In either case {@link JsonParser} must point to the first token of + * the first element, OR not point to any token (in which case it is advanced + * to the next token). This means, specifically, that for wrapped sequences, + * parser MUST NOT point to the surrounding START_ARRAY but rather + * to the token following it. + */ + public MappingIterator readValues(JsonParser p) + throws IOException, JsonProcessingException + { + DeserializationContext ctxt = createDeserializationContext(p); + // false -> do not close as caller gave parser instance + return _newIterator(p, ctxt, _findRootDeserializer(ctxt), false); + } + + /** + * Method for reading sequence of Objects from parser stream. + *

+ * Sequence can be either wrapped or unwrapped root-level sequence: + * wrapped means that the elements are enclosed in JSON Array; + * and unwrapped that elements are directly accessed at main level. + * Assumption is that iff the first token of the document is + * START_ARRAY, we have a wrapped sequence; otherwise + * unwrapped. For wrapped sequences, leading START_ARRAY + * is skipped, so that for both cases, underlying {@link JsonParser} + * will point to what is expected to be the first token of the first + * element. + *

+ * Note that the wrapped vs unwrapped logic means that it is NOT + * possible to use this method for reading an unwrapped sequence + * of elements written as JSON Arrays: to read such sequences, one + * has to use {@link #readValues(JsonParser)}, making sure parser + * points to the first token of the first element (i.e. the second + * START_ARRAY which is part of the first element). + */ + public MappingIterator readValues(InputStream src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return _detectBindAndReadValues(_dataFormatReaders.findFormat(src), false); + } + + return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + } + + /** + * Overloaded version of {@link #readValue(InputStream)}. + */ + @SuppressWarnings("resource") + public MappingIterator readValues(Reader src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(src); + } + JsonParser p = _considerFilter(_parserFactory.createParser(src), true); + _initForMultiRead(p); + p.nextToken(); + DeserializationContext ctxt = createDeserializationContext(p); + return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true); + } + + /** + * Overloaded version of {@link #readValue(InputStream)}. + * + * @param json String that contains JSON content to parse + */ + @SuppressWarnings("resource") + public MappingIterator readValues(String json) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + _reportUndetectableSource(json); + } + JsonParser p = _considerFilter(_parserFactory.createParser(json), true); + _initForMultiRead(p); + p.nextToken(); + DeserializationContext ctxt = createDeserializationContext(p); + return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true); + } + + /** + * Overloaded version of {@link #readValue(InputStream)}. + */ + public MappingIterator readValues(byte[] src, int offset, int length) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return _detectBindAndReadValues(_dataFormatReaders.findFormat(src, offset, length), false); + } + return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + } + + /** + * Overloaded version of {@link #readValue(InputStream)}. + */ + public final MappingIterator readValues(byte[] src) + throws IOException, JsonProcessingException { + return readValues(src, 0, src.length); + } + + /** + * Overloaded version of {@link #readValue(InputStream)}. + */ + public MappingIterator readValues(File src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return _detectBindAndReadValues( + _dataFormatReaders.findFormat(_inputStream(src)), false); + } + return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + } + + /** + * Overloaded version of {@link #readValue(InputStream)}. + * + * @param src URL to read to access JSON content to parse. + */ + public MappingIterator readValues(URL src) + throws IOException, JsonProcessingException + { + if (_dataFormatReaders != null) { + return _detectBindAndReadValues( + _dataFormatReaders.findFormat(_inputStream(src)), true); + } + return _bindAndReadValues(_considerFilter(_parserFactory.createParser(src), true)); + } + + /* + /********************************************************** + /* Implementation of rest of ObjectCodec methods + /********************************************************** + */ + + @Override + public T treeToValue(TreeNode n, Class valueType) throws JsonProcessingException + { + try { + return readValue(treeAsTokens(n), valueType); + } catch (JsonProcessingException e) { + throw e; + } catch (IOException e) { // should not occur, no real i/o... + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + @Override + public void writeValue(JsonGenerator gen, Object value) throws IOException, JsonProcessingException { + throw new UnsupportedOperationException("Not implemented for ObjectReader"); + } + + /* + /********************************************************** + /* Helper methods, data-binding + /********************************************************** + */ + + /** + * Actual implementation of value reading+binding operation. + */ + protected Object _bind(JsonParser p, Object valueToUpdate) throws IOException + { + /* First: may need to read the next token, to initialize state (either + * before first read from parser, or after previous token has been cleared) + */ + Object result; + JsonToken t = _initForReading(p); + if (t == JsonToken.VALUE_NULL) { + if (valueToUpdate == null) { + DeserializationContext ctxt = createDeserializationContext(p); + result = _findRootDeserializer(ctxt).getNullValue(ctxt); + } else { + result = valueToUpdate; + } + } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { + result = valueToUpdate; + } else { // pointing to event other than null + DeserializationContext ctxt = createDeserializationContext(p); + JsonDeserializer deser = _findRootDeserializer(ctxt); + if (_unwrapRoot) { + result = _unwrapAndDeserialize(p, ctxt, _valueType, deser); + } else { + if (valueToUpdate == null) { + result = deser.deserialize(p, ctxt); + } else { + deser.deserialize(p, ctxt, valueToUpdate); + result = valueToUpdate; + } + } + } + // Need to consume the token too + p.clearCurrentToken(); + return result; + } + + /** + * Consider filter when creating JsonParser. + */ + protected JsonParser _considerFilter(final JsonParser p, boolean multiValue) { + // 26-Mar-2016, tatu: Need to allow multiple-matches at least if we have + // have a multiple-value read (that is, "readValues()"). + return ((_filter == null) || FilteringParserDelegate.class.isInstance(p)) + ? p : new FilteringParserDelegate(p, _filter, false, multiValue); + } + + protected Object _bindAndClose(JsonParser p) throws IOException + { + try { + Object result; + JsonToken t = _initForReading(p); + if (t == JsonToken.VALUE_NULL) { + if (_valueToUpdate == null) { + DeserializationContext ctxt = createDeserializationContext(p); + result = _findRootDeserializer(ctxt).getNullValue(ctxt); + } else { + result = _valueToUpdate; + } + } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { + result = _valueToUpdate; + } else { + DeserializationContext ctxt = createDeserializationContext(p); + JsonDeserializer deser = _findRootDeserializer(ctxt); + if (_unwrapRoot) { + result = _unwrapAndDeserialize(p, ctxt, _valueType, deser); + } else { + if (_valueToUpdate == null) { + result = deser.deserialize(p, ctxt); + } else { + deser.deserialize(p, ctxt, _valueToUpdate); + result = _valueToUpdate; + } + } + } + return result; + } finally { + try { + p.close(); + } catch (IOException ioe) { } + } + } + + protected JsonNode _bindAndCloseAsTree(JsonParser p) throws IOException { + try { + return _bindAsTree(p); + } finally { + try { + p.close(); + } catch (IOException ioe) { } + } + } + + protected JsonNode _bindAsTree(JsonParser p) throws IOException + { + JsonNode result; + JsonToken t = _initForReading(p); + if (t == JsonToken.VALUE_NULL || t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { + result = NullNode.instance; + } else { + DeserializationContext ctxt = createDeserializationContext(p); + JsonDeserializer deser = _findTreeDeserializer(ctxt); + if (_unwrapRoot) { + result = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser); + } else { + result = (JsonNode) deser.deserialize(p, ctxt); + } + } + // Need to consume the token too + p.clearCurrentToken(); + return result; + } + + /** + * @since 2.1 + */ + protected MappingIterator _bindAndReadValues(JsonParser p) throws IOException + { + _initForMultiRead(p); + p.nextToken(); + DeserializationContext ctxt = createDeserializationContext(p); + return _newIterator(p, ctxt, _findRootDeserializer(ctxt), true); + } + + protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt, + JavaType rootType, JsonDeserializer deser) throws IOException + { + PropertyName expRootName = _config.findRootName(rootType); + // 12-Jun-2015, tatu: Should try to support namespaces etc but... + String expSimpleName = expRootName.getSimpleName(); + + if (p.getCurrentToken() != JsonToken.START_OBJECT) { + throw JsonMappingException.from(p, "Current token not START_OBJECT (needed to unwrap root name '" + +expSimpleName+"'), but "+p.getCurrentToken()); + } + if (p.nextToken() != JsonToken.FIELD_NAME) { + throw JsonMappingException.from(p, "Current token not FIELD_NAME (to contain expected root name '" + +expSimpleName+"'), but "+p.getCurrentToken()); + } + String actualName = p.getCurrentName(); + if (!expSimpleName.equals(actualName)) { + throw JsonMappingException.from(p, "Root name '"+actualName+"' does not match expected ('" + +expSimpleName+"') for type "+rootType); + } + // ok, then move to value itself.... + p.nextToken(); + Object result; + if (_valueToUpdate == null) { + result = deser.deserialize(p, ctxt); + } else { + deser.deserialize(p, ctxt, _valueToUpdate); + result = _valueToUpdate; + } + // and last, verify that we now get matching END_OBJECT + if (p.nextToken() != JsonToken.END_OBJECT) { + throw JsonMappingException.from(p, "Current token not END_OBJECT (to match wrapper object with root name '" + +expSimpleName+"'), but "+p.getCurrentToken()); + } + return result; + } + + /* + /********************************************************** + /* Internal methods, format auto-detection + /********************************************************** + */ + + @SuppressWarnings("resource") + protected Object _detectBindAndClose(byte[] src, int offset, int length) throws IOException + { + DataFormatReaders.Match match = _dataFormatReaders.findFormat(src, offset, length); + if (!match.hasMatch()) { + _reportUnkownFormat(_dataFormatReaders, match); + } + JsonParser p = match.createParserWithMatch(); + return match.getReader()._bindAndClose(p); + } + + @SuppressWarnings("resource") + protected Object _detectBindAndClose(DataFormatReaders.Match match, boolean forceClosing) + throws IOException + { + if (!match.hasMatch()) { + _reportUnkownFormat(_dataFormatReaders, match); + } + JsonParser p = match.createParserWithMatch(); + // One more thing: we Own the input stream now; and while it's + // not super clean way to do it, we must ensure closure so: + if (forceClosing) { + p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); + } + // important: use matching ObjectReader (may not be 'this') + return match.getReader()._bindAndClose(p); + } + + @SuppressWarnings("resource") + protected MappingIterator _detectBindAndReadValues(DataFormatReaders.Match match, boolean forceClosing) + throws IOException, JsonProcessingException + { + if (!match.hasMatch()) { + _reportUnkownFormat(_dataFormatReaders, match); + } + JsonParser p = match.createParserWithMatch(); + // One more thing: we Own the input stream now; and while it's + // not super clean way to do it, we must ensure closure so: + if (forceClosing) { + p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); + } + // important: use matching ObjectReader (may not be 'this') + return match.getReader()._bindAndReadValues(p); + } + + @SuppressWarnings("resource") + protected JsonNode _detectBindAndCloseAsTree(InputStream in) throws IOException + { + DataFormatReaders.Match match = _dataFormatReaders.findFormat(in); + if (!match.hasMatch()) { + _reportUnkownFormat(_dataFormatReaders, match); + } + JsonParser p = match.createParserWithMatch(); + p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); + return match.getReader()._bindAndCloseAsTree(p); + } + + /** + * Method called to indicate that format detection failed to detect format + * of given input + */ + protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match) throws JsonProcessingException + { + // 17-Aug-2015, tatu: Unfortunately, no parser/generator available so: + throw new JsonParseException(null, "Can not detect format from input, does not look like any of detectable formats " + +detector.toString()); + } + + /* + /********************************************************** + /* Internal methods, other + /********************************************************** + */ + + /** + * @since 2.2 + */ + protected void _verifySchemaType(FormatSchema schema) + { + if (schema != null) { + if (!_parserFactory.canUseSchema(schema)) { + throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName() + +" for format "+_parserFactory.getFormatName()); + } + } + } + + /** + * Internal helper method called to create an instance of {@link DeserializationContext} + * for deserializing a single root value. + * Can be overridden if a custom context is needed. + */ + protected DefaultDeserializationContext createDeserializationContext(JsonParser p) { + return _context.createInstance(_config, p, _injectableValues); + } + + protected void _reportUndetectableSource(Object src) throws JsonProcessingException + { + // 17-Aug-2015, tatu: Unfortunately, no parser/generator available so: + throw new JsonParseException(null, "Can not use source of type " + +src.getClass().getName()+" with format auto-detection: must be byte- not char-based"); + } + + protected InputStream _inputStream(URL src) throws IOException { + return src.openStream(); + } + + protected InputStream _inputStream(File f) throws IOException { + return new FileInputStream(f); + } + + /* + /********************************************************** + /* Helper methods, locating deserializers etc + /********************************************************** + */ + + /** + * Method called to locate deserializer for the passed root-level value. + */ + protected JsonDeserializer _findRootDeserializer(DeserializationContext ctxt) + throws JsonMappingException + { + if (_rootDeserializer != null) { + return _rootDeserializer; + } + + // Sanity check: must have actual type... + JavaType t = _valueType; + if (t == null) { + throw JsonMappingException.from(ctxt, "No value type configured for ObjectReader"); + } + + // First: have we already seen it? + JsonDeserializer deser = _rootDeserializers.get(t); + if (deser != null) { + return deser; + } + // Nope: need to ask provider to resolve it + deser = ctxt.findRootValueDeserializer(t); + if (deser == null) { // can this happen? + throw JsonMappingException.from(ctxt, "Can not find a deserializer for type "+t); + } + _rootDeserializers.put(t, deser); + return deser; + } + + /** + * @since 2.6 + */ + protected JsonDeserializer _findTreeDeserializer(DeserializationContext ctxt) + throws JsonMappingException + { + JsonDeserializer deser = _rootDeserializers.get(JSON_NODE_TYPE); + if (deser == null) { + // Nope: need to ask provider to resolve it + deser = ctxt.findRootValueDeserializer(JSON_NODE_TYPE); + if (deser == null) { // can this happen? + throw JsonMappingException.from(ctxt, + "Can not find a deserializer for type "+JSON_NODE_TYPE); + } + _rootDeserializers.put(JSON_NODE_TYPE, deser); + } + return deser; + } + + /** + * Method called to locate deserializer ahead of time, if permitted + * by configuration. Method also is NOT to throw an exception if + * access fails. + */ + protected JsonDeserializer _prefetchRootDeserializer(JavaType valueType) + { + if (valueType == null || !_config.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH)) { + return null; + } + // already cached? + JsonDeserializer deser = _rootDeserializers.get(valueType); + if (deser == null) { + try { + // If not, need to resolve; for which we need a temporary context as well: + DeserializationContext ctxt = createDeserializationContext(null); + deser = ctxt.findRootValueDeserializer(valueType); + if (deser != null) { + _rootDeserializers.put(valueType, deser); + } + return deser; + } catch (JsonProcessingException e) { + // need to swallow? + } + } + return deser; + } + + /** + * Convenience method to bind from {@link JsonPointer}. + * {@link JsonPointerBasedFilter} is registered and will be used for parsing later. + * @since 2.6 + */ + public ObjectReader at(final String value) { + return new ObjectReader(this, new JsonPointerBasedFilter(value)); + } + + /** + * Convenience method to bind from {@link JsonPointer} + * {@link JsonPointerBasedFilter} is registered and will be used for parsing later. + * @since 2.6 + */ + public ObjectReader at(final JsonPointer pointer) { + return new ObjectReader(this, new JsonPointerBasedFilter(pointer)); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ObjectWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1431 @@ +package com.fasterxml.jackson.databind; + +import java.io.*; +import java.text.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.io.SegmentedStringWriter; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.core.util.*; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Builder object that can be used for per-serialization configuration of + * serialization parameters, such as JSON View and root type to use. + * (and thus fully thread-safe with no external synchronization); + * new instances are constructed for different configurations. + * Instances are initially constructed by {@link ObjectMapper} and can be + * reused in completely thread-safe manner with no explicit synchronization + */ +public class ObjectWriter + implements Versioned, + java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1; // since 2.5 + + /** + * We need to keep track of explicit disabling of pretty printing; + * easiest to do by a token value. + */ + protected final static PrettyPrinter NULL_PRETTY_PRINTER = new MinimalPrettyPrinter(); + + /* + /********************************************************** + /* Immutable configuration from ObjectMapper + /********************************************************** + */ + + /** + * General serialization configuration settings + */ + protected final SerializationConfig _config; + + protected final DefaultSerializerProvider _serializerProvider; + + protected final SerializerFactory _serializerFactory; + + /** + * Factory used for constructing {@link JsonGenerator}s + */ + protected final JsonFactory _generatorFactory; + + /* + /********************************************************** + /* Configuration that can be changed via mutant factories + /********************************************************** + */ + + /** + * Container for settings that need to be passed to {@link JsonGenerator} + * constructed for serializing values. + * + * @since 2.5 + */ + protected final GeneratorSettings _generatorSettings; + + /** + * We may pre-fetch serializer if root type + * is known (has been explicitly declared), and if so, reuse it afterwards. + * This allows avoiding further serializer lookups and increases + * performance a bit on cases where readers are reused. + * + * @since 2.5 + */ + protected final Prefetch _prefetch; + + /* + /********************************************************** + /* Life-cycle, constructors + /********************************************************** + */ + + /** + * Constructor used by {@link ObjectMapper} for initial instantiation + */ + protected ObjectWriter(ObjectMapper mapper, SerializationConfig config, + JavaType rootType, PrettyPrinter pp) + { + _config = config; + _serializerProvider = mapper._serializerProvider; + _serializerFactory = mapper._serializerFactory; + _generatorFactory = mapper._jsonFactory; + _generatorSettings = (pp == null) ? GeneratorSettings.empty + : new GeneratorSettings(pp, null, null, null); + + // 29-Apr-2014, tatu: There is no "untyped serializer", so: + if (rootType == null || rootType.hasRawClass(Object.class)) { + _prefetch = Prefetch.empty; + } else { + rootType = rootType.withStaticTyping(); + _prefetch = Prefetch.empty.forRootType(this, rootType); + } + } + + /** + * Alternative constructor for initial instantiation by {@link ObjectMapper} + */ + protected ObjectWriter(ObjectMapper mapper, SerializationConfig config) + { + _config = config; + _serializerProvider = mapper._serializerProvider; + _serializerFactory = mapper._serializerFactory; + _generatorFactory = mapper._jsonFactory; + + _generatorSettings = GeneratorSettings.empty; + _prefetch = Prefetch.empty; + } + + /** + * Alternative constructor for initial instantiation by {@link ObjectMapper} + */ + protected ObjectWriter(ObjectMapper mapper, SerializationConfig config, + FormatSchema s) + { + _config = config; + + _serializerProvider = mapper._serializerProvider; + _serializerFactory = mapper._serializerFactory; + _generatorFactory = mapper._jsonFactory; + + _generatorSettings = (s == null) ? GeneratorSettings.empty + : new GeneratorSettings(null, s, null, null); + _prefetch = Prefetch.empty; + } + + /** + * Copy constructor used for building variations. + */ + protected ObjectWriter(ObjectWriter base, SerializationConfig config, + GeneratorSettings genSettings, Prefetch prefetch) + { + _config = config; + + _serializerProvider = base._serializerProvider; + _serializerFactory = base._serializerFactory; + _generatorFactory = base._generatorFactory; + + _generatorSettings = genSettings; + _prefetch = prefetch; + } + + /** + * Copy constructor used for building variations. + */ + protected ObjectWriter(ObjectWriter base, SerializationConfig config) + { + _config = config; + + _serializerProvider = base._serializerProvider; + _serializerFactory = base._serializerFactory; + _generatorFactory = base._generatorFactory; + + _generatorSettings = base._generatorSettings; + _prefetch = base._prefetch; + } + + /** + * @since 2.3 + */ + protected ObjectWriter(ObjectWriter base, JsonFactory f) + { + // may need to override ordering, based on data format capabilities + _config = base._config + .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, f.requiresPropertyOrdering()); + + _serializerProvider = base._serializerProvider; + _serializerFactory = base._serializerFactory; + _generatorFactory = base._generatorFactory; + + _generatorSettings = base._generatorSettings; + _prefetch = base._prefetch; + } + + /** + * Method that will return version information stored in and read from jar + * that contains this class. + */ + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Methods sub-classes MUST override, used for constructing + /* writer instances, (re)configuring parser instances. + /* Added in 2.5 + /********************************************************** + */ + + /** + * Overridable factory method called by various "withXxx()" methods + * + * @since 2.5 + */ + protected ObjectWriter _new(ObjectWriter base, JsonFactory f) { + return new ObjectWriter(base, f); + } + + /** + * Overridable factory method called by various "withXxx()" methods + * + * @since 2.5 + */ + protected ObjectWriter _new(ObjectWriter base, SerializationConfig config) { + return new ObjectWriter(base, config); + } + + /** + * Overridable factory method called by various "withXxx()" methods. + * It assumes `this` as base for settings other than those directly + * passed in. + * + * @since 2.5 + */ + protected ObjectWriter _new(GeneratorSettings genSettings, Prefetch prefetch) { + return new ObjectWriter(this, _config, genSettings, prefetch); + } + + /** + * Overridable factory method called by {@link #writeValues(OutputStream)} + * method (and its various overrides), and initializes it as necessary. + * + * @since 2.5 + */ + @SuppressWarnings("resource") + protected SequenceWriter _newSequenceWriter(boolean wrapInArray, + JsonGenerator gen, boolean managedInput) + throws IOException + { + _configureGenerator(gen); + return new SequenceWriter(_serializerProvider(), + gen, managedInput, _prefetch) + .init(wrapInArray); + } + + /* + /********************************************************** + /* Life-cycle, fluent factories for SerializationFeature + /********************************************************** + */ + + /** + * Method for constructing a new instance that is configured + * with specified feature enabled. + */ + public ObjectWriter with(SerializationFeature feature) { + SerializationConfig newConfig = _config.with(feature); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method for constructing a new instance that is configured + * with specified features enabled. + */ + public ObjectWriter with(SerializationFeature first, SerializationFeature... other) { + SerializationConfig newConfig = _config.with(first, other); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method for constructing a new instance that is configured + * with specified features enabled. + */ + public ObjectWriter withFeatures(SerializationFeature... features) { + SerializationConfig newConfig = _config.withFeatures(features); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method for constructing a new instance that is configured + * with specified feature enabled. + */ + public ObjectWriter without(SerializationFeature feature) { + SerializationConfig newConfig = _config.without(feature); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method for constructing a new instance that is configured + * with specified features enabled. + */ + public ObjectWriter without(SerializationFeature first, SerializationFeature... other) { + SerializationConfig newConfig = _config.without(first, other); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method for constructing a new instance that is configured + * with specified features enabled. + */ + public ObjectWriter withoutFeatures(SerializationFeature... features) { + SerializationConfig newConfig = _config.withoutFeatures(features); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /* + /********************************************************** + /* Life-cycle, fluent factories for JsonGenerator.Feature (2.5) + /********************************************************** + */ + + /** + * @since 2.5 + */ + public ObjectWriter with(JsonGenerator.Feature feature) { + SerializationConfig newConfig = _config.with(feature); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.5 + */ + public ObjectWriter withFeatures(JsonGenerator.Feature... features) { + SerializationConfig newConfig = _config.withFeatures(features); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.5 + */ + public ObjectWriter without(JsonGenerator.Feature feature) { + SerializationConfig newConfig = _config.without(feature); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.5 + */ + public ObjectWriter withoutFeatures(JsonGenerator.Feature... features) { + SerializationConfig newConfig = _config.withoutFeatures(features); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /* + /********************************************************** + /* Life-cycle, fluent factories for FormatFeature (2.7) + /********************************************************** + */ + + /** + * @since 2.7 + */ + public ObjectWriter with(FormatFeature feature) { + SerializationConfig newConfig = _config.with(feature); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.7 + */ + public ObjectWriter withFeatures(FormatFeature... features) { + SerializationConfig newConfig = _config.withFeatures(features); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.7 + */ + public ObjectWriter without(FormatFeature feature) { + SerializationConfig newConfig = _config.without(feature); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.7 + */ + public ObjectWriter withoutFeatures(FormatFeature... features) { + SerializationConfig newConfig = _config.withoutFeatures(features); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /* + /********************************************************** + /* Life-cycle, fluent factories, type-related + /********************************************************** + */ + + /** + * Method that will construct a new instance that uses specific type + * as the root type for serialization, instead of runtime dynamic + * type of the root object itself. + *

+ * Note that method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + * + * @since 2.5 + */ + public ObjectWriter forType(JavaType rootType) + { + Prefetch pf = _prefetch.forRootType(this, rootType); + return (pf == _prefetch) ? this : _new(_generatorSettings, pf); + } + + /** + * Method that will construct a new instance that uses specific type + * as the root type for serialization, instead of runtime dynamic + * type of the root object itself. + * + * @since 2.5 + */ + public ObjectWriter forType(Class rootType) { + if (rootType == Object.class) { + return forType((JavaType) null); + } + return forType(_config.constructType(rootType)); + } + + /** + * Method that will construct a new instance that uses specific type + * as the root type for serialization, instead of runtime dynamic + * type of the root object itself. + * + * @since 2.5 + */ + public ObjectWriter forType(TypeReference rootType) { + return forType(_config.getTypeFactory().constructType(rootType.getType())); + } + + /** + * @deprecated since 2.5 Use {@link #forType(JavaType)} instead + */ + @Deprecated // since 2.5 + public ObjectWriter withType(JavaType rootType) { + return forType(rootType); + } + + /** + * @deprecated since 2.5 Use {@link #forType(Class)} instead + */ + @Deprecated // since 2.5 + public ObjectWriter withType(Class rootType) { + return forType(rootType); + } + + /** + * @deprecated since 2.5 Use {@link #forType(TypeReference)} instead + */ + @Deprecated // since 2.5 + public ObjectWriter withType(TypeReference rootType) { + return forType(rootType); + } + + /* + /********************************************************** + /* Life-cycle, fluent factories, other + /********************************************************** + */ + + /** + * Fluent factory method that will construct a new writer instance that will + * use specified date format for serializing dates; or if null passed, one + * that will serialize dates as numeric timestamps. + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectWriter with(DateFormat df) { + SerializationConfig newConfig = _config.with(df); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method that will construct a new instance that will use the default + * pretty printer for serialization. + */ + public ObjectWriter withDefaultPrettyPrinter() { + return with(_config.getDefaultPrettyPrinter()); + } + + /** + * Method that will construct a new instance that uses specified + * provider for resolving filter instances by id. + */ + public ObjectWriter with(FilterProvider filterProvider) { + return (filterProvider == _config.getFilterProvider()) ? this + : _new(this, _config.withFilters(filterProvider)); + } + + /** + * Method that will construct a new instance that will use specified pretty + * printer (or, if null, will not do any pretty-printing) + */ + public ObjectWriter with(PrettyPrinter pp) { + GeneratorSettings genSet = _generatorSettings.with(pp); + if (genSet == _generatorSettings) { + return this; + } + return _new(genSet, _prefetch); + } + + /** + * Method for constructing a new instance with configuration that + * specifies what root name to use for "root element wrapping". + * See {@link SerializationConfig#withRootName(String)} for details. + *

+ * Note that method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + * + * @param rootName Root name to use, if non-empty; `null` for "use defaults", + * and empty String ("") for "do NOT add root wrapper" + */ + public ObjectWriter withRootName(String rootName) { + SerializationConfig newConfig = _config.withRootName(rootName); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.6 + */ + public ObjectWriter withRootName(PropertyName rootName) { + SerializationConfig newConfig = _config.withRootName(rootName); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Convenience method that is same as calling: + * + * withRootName("") + * + * which will forcibly prevent use of root name wrapping when writing + * values with this {@link ObjectWriter}. + * + * @since 2.6 + */ + public ObjectWriter withoutRootName() { + SerializationConfig newConfig = _config.withRootName(PropertyName.NO_NAME); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method that will construct a new instance that uses specific format schema + * for serialization. + *

+ * Note that method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectWriter with(FormatSchema schema) { + GeneratorSettings genSet = _generatorSettings.with(schema); + if (genSet == _generatorSettings) { + return this; + } + _verifySchemaType(schema); + return _new(genSet, _prefetch); + } + + /** + * @deprecated Since 2.5 use {@link #with(FormatSchema)} instead + */ + @Deprecated + public ObjectWriter withSchema(FormatSchema schema) { + return with(schema); + } + + /** + * Method that will construct a new instance that uses specified + * serialization view for serialization (with null basically disables + * view processing) + *

+ * Note that the method does NOT change state of this reader, but + * rather construct and returns a newly configured instance. + */ + public ObjectWriter withView(Class view) { + SerializationConfig newConfig = _config.withView(view); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + public ObjectWriter with(Locale l) { + SerializationConfig newConfig = _config.with(l); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + public ObjectWriter with(TimeZone tz) { + SerializationConfig newConfig = _config.with(tz); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Method that will construct a new instance that uses specified default + * {@link Base64Variant} for base64 encoding + * + * @since 2.1 + */ + public ObjectWriter with(Base64Variant b64variant) { + SerializationConfig newConfig = _config.with(b64variant); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.3 + */ + public ObjectWriter with(CharacterEscapes escapes) { + GeneratorSettings genSet = _generatorSettings.with(escapes); + if (genSet == _generatorSettings) { + return this; + } + return _new(genSet, _prefetch); + } + + /** + * @since 2.3 + */ + public ObjectWriter with(JsonFactory f) { + return (f == _generatorFactory) ? this : _new(this, f); + } + + /** + * @since 2.3 + */ + public ObjectWriter with(ContextAttributes attrs) { + SerializationConfig newConfig = _config.with(attrs); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * Mutant factory method that allows construction of a new writer instance + * that uses specified set of default attribute values. + * + * @since 2.3 + */ + public ObjectWriter withAttributes(Map attrs) { + SerializationConfig newConfig = _config.withAttributes(attrs); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.3 + */ + public ObjectWriter withAttribute(Object key, Object value) { + SerializationConfig newConfig = _config.withAttribute(key, value); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.3 + */ + public ObjectWriter withoutAttribute(Object key) { + SerializationConfig newConfig = _config.withoutAttribute(key); + return (newConfig == _config) ? this : _new(this, newConfig); + } + + /** + * @since 2.5 + */ + public ObjectWriter withRootValueSeparator(String sep) { + GeneratorSettings genSet = _generatorSettings.withRootValueSeparator(sep); + if (genSet == _generatorSettings) { + return this; + } + return _new(genSet, _prefetch); + } + + /** + * @since 2.5 + */ + public ObjectWriter withRootValueSeparator(SerializableString sep) { + GeneratorSettings genSet = _generatorSettings.withRootValueSeparator(sep); + if (genSet == _generatorSettings) { + return this; + } + return _new(genSet, _prefetch); + } + + /* + /********************************************************** + /* Factory methods for sequence writers (2.5) + /********************************************************** + */ + + /** + * Method for creating a {@link SequenceWriter} to write a sequence of root + * values using configuration of this {@link ObjectWriter}. + * Sequence is not surrounded by JSON array; some backend types may not + * support writing of such sequences as root level. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure closing of underlying generator and + * output stream. + * + * @param out Target file to write value sequence to. + * + * @since 2.5 + */ + public SequenceWriter writeValues(File out) throws IOException { + return _newSequenceWriter(false, + _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + } + + /** + * Method for creating a {@link SequenceWriter} to write a sequence of root + * values using configuration of this {@link ObjectWriter}. + * Sequence is not surrounded by JSON array; some backend types may not + * support writing of such sequences as root level. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure that all content gets flushed by + * the generator. However, since a {@link JsonGenerator} is explicitly passed, + * it will NOT be closed when {@link SequenceWriter#close()} is called. + * + * @param gen Low-level generator caller has already constructed that will + * be used for actual writing of token stream. + * + * @since 2.5 + */ + public SequenceWriter writeValues(JsonGenerator gen) throws IOException { + _configureGenerator(gen); + return _newSequenceWriter(false, gen, false); + } + + /** + * Method for creating a {@link SequenceWriter} to write a sequence of root + * values using configuration of this {@link ObjectWriter}. + * Sequence is not surrounded by JSON array; some backend types may not + * support writing of such sequences as root level. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure closing of underlying generator and + * output stream. + * + * @param out Target writer to use for writing the token stream + * + * @since 2.5 + */ + public SequenceWriter writeValues(Writer out) throws IOException { + return _newSequenceWriter(false, + _generatorFactory.createGenerator(out), true); + } + + /** + * Method for creating a {@link SequenceWriter} to write a sequence of root + * values using configuration of this {@link ObjectWriter}. + * Sequence is not surrounded by JSON array; some backend types may not + * support writing of such sequences as root level. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure closing of underlying generator and + * output stream. + * + * @param out Physical output stream to use for writing the token stream + * + * @since 2.5 + */ + public SequenceWriter writeValues(OutputStream out) throws IOException { + return _newSequenceWriter(false, + _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + } + + /** + * Method for creating a {@link SequenceWriter} to write an array of + * root-level values, using configuration of this {@link ObjectWriter}. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure closing of underlying generator and + * output stream. + *

+ * Note that the type to use with {@link ObjectWriter#forType(Class)} needs to + * be type of individual values (elements) to write and NOT matching array + * or {@link java.util.Collection} type. + * + * @param out File to write token stream to + * + * @since 2.5 + */ + public SequenceWriter writeValuesAsArray(File out) throws IOException { + return _newSequenceWriter(true, + _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + } + + /** + * Method for creating a {@link SequenceWriter} to write an array of + * root-level values, using configuration of this {@link ObjectWriter}. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure that all content gets flushed by + * the generator. However, since a {@link JsonGenerator} is explicitly passed, + * it will NOT be closed when {@link SequenceWriter#close()} is called. + *

+ * Note that the type to use with {@link ObjectWriter#forType(Class)} needs to + * be type of individual values (elements) to write and NOT matching array + * or {@link java.util.Collection} type. + * + * @param gen Underlying generator to use for writing the token stream + * + * @since 2.5 + */ + public SequenceWriter writeValuesAsArray(JsonGenerator gen) throws IOException { + return _newSequenceWriter(true, gen, false); + } + + /** + * Method for creating a {@link SequenceWriter} to write an array of + * root-level values, using configuration of this {@link ObjectWriter}. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure closing of underlying generator and + * output stream. + *

+ * Note that the type to use with {@link ObjectWriter#forType(Class)} needs to + * be type of individual values (elements) to write and NOT matching array + * or {@link java.util.Collection} type. + * + * @param out Writer to use for writing the token stream + * + * @since 2.5 + */ + public SequenceWriter writeValuesAsArray(Writer out) throws IOException { + return _newSequenceWriter(true, _generatorFactory.createGenerator(out), true); + } + + /** + * Method for creating a {@link SequenceWriter} to write an array of + * root-level values, using configuration of this {@link ObjectWriter}. + * Resulting writer needs to be {@link SequenceWriter#close()}d after all + * values have been written to ensure closing of underlying generator and + * output stream. + *

+ * Note that the type to use with {@link ObjectWriter#forType(Class)} needs to + * be type of individual values (elements) to write and NOT matching array + * or {@link java.util.Collection} type. + * + * @param out Physical output stream to use for writing the token stream + * + * @since 2.5 + */ + public SequenceWriter writeValuesAsArray(OutputStream out) throws IOException { + return _newSequenceWriter(true, + _generatorFactory.createGenerator(out, JsonEncoding.UTF8), true); + } + + /* + /********************************************************** + /* Simple accessors + /********************************************************** + */ + + public boolean isEnabled(SerializationFeature f) { + return _config.isEnabled(f); + } + + public boolean isEnabled(MapperFeature f) { + return _config.isEnabled(f); + } + + public boolean isEnabled(JsonParser.Feature f) { + return _generatorFactory.isEnabled(f); + } + + /** + * @since 2.2 + */ + public SerializationConfig getConfig() { + return _config; + } + + /** + * @since 2.2 + */ + public JsonFactory getFactory() { + return _generatorFactory; + } + + public TypeFactory getTypeFactory() { + return _config.getTypeFactory(); + } + + /** + * Diagnostics method that can be called to check whether this writer + * has pre-fetched serializer to use: pre-fetching improves performance + * when writer instances are reused as it avoids a per-call serializer + * lookup. + * + * @since 2.2 + */ + public boolean hasPrefetchedSerializer() { + return _prefetch.hasSerializer(); + } + + /** + * @since 2.3 + */ + public ContextAttributes getAttributes() { + return _config.getAttributes(); + } + + /* + /********************************************************** + /* Serialization methods; ones from ObjectCodec first + /********************************************************** + */ + + /** + * Method that can be used to serialize any Java value as + * JSON output, using provided {@link JsonGenerator}. + */ + public void writeValue(JsonGenerator gen, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configureGenerator(gen); + if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) + && (value instanceof Closeable)) { + + Closeable toClose = (Closeable) value; + try { + _prefetch.serialize(gen, value, _serializerProvider()); + if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + gen.flush(); + } + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } finally { + if (toClose != null) { + try { + toClose.close(); + } catch (IOException ioe) { } + } + } + } else { + _prefetch.serialize(gen, value, _serializerProvider()); + if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { + gen.flush(); + } + } + } + + /* + /********************************************************** + /* Serialization methods, others + /********************************************************** + */ + + /** + * Method that can be used to serialize any Java value as + * JSON output, written to File provided. + */ + public void writeValue(File resultFile, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configAndWriteValue(_generatorFactory.createGenerator(resultFile, JsonEncoding.UTF8), value); + } + + /** + * Method that can be used to serialize any Java value as + * JSON output, using output stream provided (using encoding + * {@link JsonEncoding#UTF8}). + *

+ * Note: method does not close the underlying stream explicitly + * here; however, {@link JsonFactory} this mapper uses may choose + * to close the stream depending on its settings (by default, + * it will try to close it when {@link JsonGenerator} we construct + * is closed). + */ + public void writeValue(OutputStream out, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configAndWriteValue(_generatorFactory.createGenerator(out, JsonEncoding.UTF8), value); + } + + /** + * Method that can be used to serialize any Java value as + * JSON output, using Writer provided. + *

+ * Note: method does not close the underlying stream explicitly + * here; however, {@link JsonFactory} this mapper uses may choose + * to close the stream depending on its settings (by default, + * it will try to close it when {@link JsonGenerator} we construct + * is closed). + */ + public void writeValue(Writer w, Object value) + throws IOException, JsonGenerationException, JsonMappingException + { + _configAndWriteValue(_generatorFactory.createGenerator(w), value); + } + + /** + * Method that can be used to serialize any Java value as + * a String. Functionally equivalent to calling + * {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter} + * and constructing String, but more efficient. + *

+ * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. + */ + @SuppressWarnings("resource") + public String writeValueAsString(Object value) + throws JsonProcessingException + { + // alas, we have to pull the recycler directly here... + SegmentedStringWriter sw = new SegmentedStringWriter(_generatorFactory._getBufferRecycler()); + try { + _configAndWriteValue(_generatorFactory.createGenerator(sw), value); + } catch (JsonProcessingException e) { // to support [JACKSON-758] + throw e; + } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: + throw JsonMappingException.fromUnexpectedIOE(e); + } + return sw.getAndClear(); + } + + /** + * Method that can be used to serialize any Java value as + * a byte array. Functionally equivalent to calling + * {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream} + * and getting bytes, but more efficient. + * Encoding used will be UTF-8. + *

+ * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it. + */ + @SuppressWarnings("resource") + public byte[] writeValueAsBytes(Object value) + throws JsonProcessingException + { + ByteArrayBuilder bb = new ByteArrayBuilder(_generatorFactory._getBufferRecycler()); + try { + _configAndWriteValue(_generatorFactory.createGenerator(bb, JsonEncoding.UTF8), value); + } catch (JsonProcessingException e) { // to support [JACKSON-758] + throw e; + } catch (IOException e) { // shouldn't really happen, but is declared as possibility so: + throw JsonMappingException.fromUnexpectedIOE(e); + } + byte[] result = bb.toByteArray(); + bb.release(); + return result; + } + + /* + /********************************************************** + /* Other public methods + /********************************************************** + */ + + /** + * Method for visiting type hierarchy for given type, using specified visitor. + * Visitation uses Serializer hierarchy and related properties + *

+ * This method can be used for things like + * generating Json Schema + * instance for specified type. + * + * @param type Type to generate schema for (possibly with generic signature) + * + * @since 2.2 + */ + public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visitor) throws JsonMappingException + { + if (type == null) { + throw new IllegalArgumentException("type must be provided"); + } + _serializerProvider().acceptJsonFormatVisitor(type, visitor); + } + + /** + * Since 2.6 + */ + public void acceptJsonFormatVisitor(Class rawType, JsonFormatVisitorWrapper visitor) throws JsonMappingException { + acceptJsonFormatVisitor(_config.constructType(rawType), visitor); + } + + public boolean canSerialize(Class type) { + return _serializerProvider().hasSerializerFor(type, null); + } + + /** + * Method for checking whether instances of given type can be serialized, + * and optionally why (as per {@link Throwable} returned). + * + * @since 2.3 + */ + public boolean canSerialize(Class type, AtomicReference cause) { + return _serializerProvider().hasSerializerFor(type, cause); + } + + /* + /********************************************************** + /* Overridable helper methods + /********************************************************** + */ + + /** + * Overridable helper method used for constructing + * {@link SerializerProvider} to use for serialization. + */ + protected DefaultSerializerProvider _serializerProvider() { + return _serializerProvider.createInstance(_config, _serializerFactory); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * @since 2.2 + */ + protected void _verifySchemaType(FormatSchema schema) + { + if (schema != null) { + if (!_generatorFactory.canUseSchema(schema)) { + throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName() + +" for format "+_generatorFactory.getFormatName()); + } + } + } + + /** + * Method called to configure the generator as necessary and then + * call write functionality + */ + protected final void _configAndWriteValue(JsonGenerator gen, Object value) throws IOException + { + _configureGenerator(gen); + if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) { + _writeCloseable(gen, value); + return; + } + boolean closed = false; + try { + _prefetch.serialize(gen, value, _serializerProvider()); + closed = true; + gen.close(); + } finally { + // won't try to close twice; also, must catch exception (so it + // will not mask exception that is pending) + if (!closed) { + /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of + * structures, which typically causes more damage. + */ + gen.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + gen.close(); + } catch (IOException ioe) { } + } + } + } + + /** + * Helper method used when value to serialize is {@link Closeable} and its close() + * method is to be called right after serialization has been called + */ + private final void _writeCloseable(JsonGenerator gen, Object value) + throws IOException + { + Closeable toClose = (Closeable) value; + try { + _prefetch.serialize(gen, value, _serializerProvider()); + JsonGenerator tmpGen = gen; + gen = null; + tmpGen.close(); + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } finally { + /* Need to close both generator and value, as long as they haven't yet + * been closed + */ + if (gen != null) { + /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of + * structures, which typically causes more damage. + */ + gen.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + gen.close(); + } catch (IOException ioe) { } + } + if (toClose != null) { + try { + toClose.close(); + } catch (IOException ioe) { } + } + } + } + + /** + * Helper method called to set or override settings of passed-in + * {@link JsonGenerator} + * + * @since 2.5 + */ + protected final void _configureGenerator(JsonGenerator gen) + { + // order is slightly significant: both may change PrettyPrinter + // settings. + _config.initialize(gen); // since 2.5 + _generatorSettings.initialize(gen); + } + + /* + /********************************************************** + /* Helper classes for configuration + /********************************************************** + */ + + /** + * Helper class used for containing settings specifically related + * to (re)configuring {@link JsonGenerator} constructed for + * writing output. + * + * @since 2.5 + */ + public final static class GeneratorSettings + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + public final static GeneratorSettings empty = new GeneratorSettings(null, null, null, null); + + /** + * To allow for dynamic enabling/disabling of pretty printing, + * pretty printer can be optionally configured for writer + * as well + */ + public final PrettyPrinter prettyPrinter; + + /** + * When using data format that uses a schema, schema is passed + * to generator. + */ + public final FormatSchema schema; + + /** + * Caller may want to specify character escaping details, either as + * defaults, or on call-by-call basis. + */ + public final CharacterEscapes characterEscapes; + + /** + * Caller may want to override so-called "root value separator", + * String added (verbatim, with no quoting or escaping) between + * values in root context. Default value is a single space character, + * but this is often changed to linefeed. + */ + public final SerializableString rootValueSeparator; + + public GeneratorSettings(PrettyPrinter pp, FormatSchema sch, + CharacterEscapes esc, SerializableString rootSep) { + prettyPrinter = pp; + schema = sch; + characterEscapes = esc; + rootValueSeparator = rootSep; + } + + public GeneratorSettings with(PrettyPrinter pp) { + // since null would mean "don't care", need to use placeholder to indicate "disable" + if (pp == null) { + pp = NULL_PRETTY_PRINTER; + } + return (pp == prettyPrinter) ? this + : new GeneratorSettings(pp, schema, characterEscapes, rootValueSeparator); + } + + public GeneratorSettings with(FormatSchema sch) { + return (schema == sch) ? this + : new GeneratorSettings(prettyPrinter, sch, characterEscapes, rootValueSeparator); + } + + public GeneratorSettings with(CharacterEscapes esc) { + return (characterEscapes == esc) ? this + : new GeneratorSettings(prettyPrinter, schema, esc, rootValueSeparator); + } + + public GeneratorSettings withRootValueSeparator(String sep) { + if (sep == null) { + if (rootValueSeparator == null) { + return this; + } + } else if (sep.equals(rootValueSeparator)) { + return this; + } + return new GeneratorSettings(prettyPrinter, schema, characterEscapes, + (sep == null) ? null : new SerializedString(sep)); + } + + public GeneratorSettings withRootValueSeparator(SerializableString sep) { + if (sep == null) { + if (rootValueSeparator == null) { + return this; + } + } else { + if (rootValueSeparator != null + && sep.getValue().equals(rootValueSeparator.getValue())) { + return this; + } + } + return new GeneratorSettings(prettyPrinter, schema, characterEscapes, sep); + } + + /** + * @since 2.6 + */ + public void initialize(JsonGenerator gen) + { + PrettyPrinter pp = prettyPrinter; + if (prettyPrinter != null) { + if (pp == NULL_PRETTY_PRINTER) { + gen.setPrettyPrinter(null); + } else { + if (pp instanceof Instantiatable) { + pp = (PrettyPrinter) ((Instantiatable) pp).createInstance(); + } + gen.setPrettyPrinter(pp); + } + } + if (characterEscapes != null) { + gen.setCharacterEscapes(characterEscapes); + } + if (schema != null) { + gen.setSchema(schema); + } + if (rootValueSeparator != null) { + gen.setRootValueSeparator(rootValueSeparator); + } + } + } + + /** + * As a minor optimization, we will make an effort to pre-fetch a serializer, + * or at least relevant TypeSerializer, if given enough + * information. + * + * @since 2.5 + */ + public final static class Prefetch + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + public final static Prefetch empty = new Prefetch(null, null, null); + + /** + * Specified root serialization type to use; can be same + * as runtime type, but usually one of its super types + * (parent class or interface it implements). + */ + private final JavaType rootType; + + /** + * We may pre-fetch serializer if {@link #rootType} + * is known, and if so, reuse it afterwards. + * This allows avoiding further serializer lookups and increases + * performance a bit on cases where readers are reused. + */ + private final JsonSerializer valueSerializer; + + /** + * When dealing with polymorphic types, we can not pre-fetch + * serializer, but can pre-fetch {@link TypeSerializer}. + */ + private final TypeSerializer typeSerializer; + + private Prefetch(JavaType rootT, + JsonSerializer ser, TypeSerializer typeSer) + { + rootType = rootT; + valueSerializer = ser; + typeSerializer = typeSer; + } + + public Prefetch forRootType(ObjectWriter parent, JavaType newType) { + // First: if nominal type not defined, or trivial (java.lang.Object), + // not thing much to do + boolean noType = (newType == null) || newType.isJavaLangObject(); + + if (noType) { + if ((rootType == null) || (valueSerializer == null)) { + return this; + } + return new Prefetch(null, null, typeSerializer); + } + if (newType.equals(rootType)) { + return this; + } + if (parent.isEnabled(SerializationFeature.EAGER_SERIALIZER_FETCH)) { + DefaultSerializerProvider prov = parent._serializerProvider(); + // 17-Dec-2014, tatu: Need to be bit careful here; TypeSerializers are NOT cached, + // so although it'd seem like a good idea to look for those first, and avoid + // serializer for polymorphic types, it is actually more efficient to do the + // reverse here. + try { + JsonSerializer ser = prov.findTypedValueSerializer(newType, true, null); + // Important: for polymorphic types, "unwrap"... + if (ser instanceof TypeWrappedSerializer) { + return new Prefetch(newType, null, + ((TypeWrappedSerializer) ser).typeSerializer()); + } + return new Prefetch(newType, ser, null); + } catch (JsonProcessingException e) { + // need to swallow? + ; + } + } + return new Prefetch(null, null, typeSerializer); + } + + public final JsonSerializer getValueSerializer() { + return valueSerializer; + } + + public final TypeSerializer getTypeSerializer() { + return typeSerializer; + } + + public boolean hasSerializer() { + return (valueSerializer != null) || (typeSerializer != null); + } + + public void serialize(JsonGenerator gen, Object value, DefaultSerializerProvider prov) + throws IOException + { + if (typeSerializer != null) { + prov.serializePolymorphic(gen, value, rootType, valueSerializer, typeSerializer); + return; + } + if (valueSerializer != null) { + prov.serializeValue(gen, value, rootType, valueSerializer); + return; + } + prov.serializeValue(gen, value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyMetadata.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyMetadata.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyMetadata.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,171 @@ +package com.fasterxml.jackson.databind; + +/** + * Simple container class used for storing "additional" metadata about + * properties. Carved out to reduce number of distinct properties that + * actual property implementations and placeholders need to store; + * since instances are immutable, they can be freely shared. + * + * @since 2.3 + */ +public class PropertyMetadata + implements java.io.Serializable +{ + private static final long serialVersionUID = -1; + + public final static PropertyMetadata STD_REQUIRED = new PropertyMetadata(Boolean.TRUE, null, null, null); + + public final static PropertyMetadata STD_OPTIONAL = new PropertyMetadata(Boolean.FALSE, null, null, null); + + public final static PropertyMetadata STD_REQUIRED_OR_OPTIONAL = new PropertyMetadata(null, null, null, null); + + /** + * Three states: required, not required and unknown; unknown represented + * as null. + */ + protected final Boolean _required; + + /** + * Optional human-readable description associated with the property. + */ + protected final String _description; + + /** + * Optional index of the property within containing Object. + * + * @since 2.4 + */ + protected final Integer _index; + + /** + * Optional default value, as String, for property; not used cor + * any functionality by core databind, offered as metadata for + * extensions. + */ + protected final String _defaultValue; + + /* + /********************************************************** + /* Construction, configuration + /********************************************************** + */ + + @Deprecated // since 2.4 + protected PropertyMetadata(Boolean req, String desc) { this(req, desc, null, null); } + + /** + * @since 2.5 + */ + protected PropertyMetadata(Boolean req, String desc, Integer index, String def) + { + _required = req; + _description = desc; + _index = index; + _defaultValue = (def == null || def.isEmpty()) ? null : def; + } + + /** + * @since 2.4 Use variant that takes more arguments. + */ + @Deprecated + public static PropertyMetadata construct(boolean req, String desc) { + return construct(req, desc, null, null); + } + + public static PropertyMetadata construct(boolean req, String desc, Integer index, + String defaultValue) { + if (desc != null || index != null || defaultValue != null) { + return new PropertyMetadata(req, desc, index, defaultValue); + } + return req ? STD_REQUIRED : STD_OPTIONAL; + } + + /** + * Minor optimization: let's canonicalize back to placeholders in cases + * where there is no real data to consider + */ + protected Object readResolve() + { + if (_description == null && _index == null && _defaultValue == null) { + if (_required == null) { + return STD_REQUIRED_OR_OPTIONAL; + } + return _required.booleanValue() ? STD_REQUIRED : STD_OPTIONAL; + } + return this; + } + + public PropertyMetadata withDescription(String desc) { + return new PropertyMetadata(_required, desc, _index, _defaultValue); + } + + public PropertyMetadata withDefaultValue(String def) { + if ((def == null) || def.isEmpty()) { + if (_defaultValue == null) { + return this; + } + def = null; + } else if (_defaultValue.equals(def)) { + return this; + } + return new PropertyMetadata(_required, _description, _index, def); + } + + public PropertyMetadata withIndex(Integer index) { + return new PropertyMetadata(_required, _description, index, _defaultValue); + } + + public PropertyMetadata withRequired(Boolean b) { + if (b == null) { + if (_required == null) { + return this; + } + } else { + if (_required != null && _required.booleanValue() == b.booleanValue()) { + return this; + } + } + return new PropertyMetadata(b, _description, _index, _defaultValue); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + public String getDescription() { return _description; } + + /** + * @since 2.5 + */ + public String getDefaultValue() { return _defaultValue; } + + /** + * @deprecated Since 2.6: typo in name, use {@link #hasDefaultValue()} instead. + */ + @Deprecated + public boolean hasDefuaultValue() { return hasDefaultValue(); } + + /** + * Accessor for determining whether property has declared "default value", + * which may be used by extension modules. + * + * @since 2.6 + */ + public boolean hasDefaultValue() { return (_defaultValue != null); } + + public boolean isRequired() { return (_required != null) && _required.booleanValue(); } + + public Boolean getRequired() { return _required; } + + /** + * @since 2.4 + */ + public Integer getIndex() { return _index; } + + /** + * @since 2.4 + */ + public boolean hasIndex() { return _index != null; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyName.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyName.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyName.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,253 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.core.util.InternCache; +import com.fasterxml.jackson.databind.cfg.MapperConfig; + +/** + * Simple value class used for containing names of properties as defined + * by annotations (and possibly other configuration sources). + * + * @since 2.1 + */ +public class PropertyName + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; // 2.5 + + private final static String _USE_DEFAULT = ""; + private final static String _NO_NAME = ""; + + /** + * Special placeholder value that indicates that name to use should be + * based on the standard heuristics. This can be different from returning + * null, as null means "no information available, whereas this value + * indicates explicit defaulting. + */ + public final static PropertyName USE_DEFAULT = new PropertyName(_USE_DEFAULT, null); + + /** + * Special placeholder value that indicates that there is no name associated. + * Exact semantics to use (if any) depend on actual annotation in use, but + * commonly this value disables behavior for which name would be needed. + */ + public final static PropertyName NO_NAME = new PropertyName(new String(_NO_NAME), null); + + /** + * Basic name of the property. + */ + protected final String _simpleName; + + /** + * Additional namespace, for formats that have such concept (JSON + * does not, XML does, for example). + */ + protected final String _namespace; + + /** + * Lazily-constructed efficient representation of the simple name. + *

+ * NOTE: not defined as volatile to avoid performance problem with + * concurrent access in multi-core environments; due to statelessness + * of {@link SerializedString} at most leads to multiple instantiations. + * + * @since 2.4 + */ + protected SerializableString _encodedSimple; + + public PropertyName(String simpleName) { + this(simpleName, null); + } + + public PropertyName(String simpleName, String namespace) + { + _simpleName = (simpleName == null) ? "" : simpleName; + _namespace = namespace; + } + + // To support JDK serialization, recovery of Singleton instance + protected Object readResolve() { + if (_simpleName == null || _USE_DEFAULT.equals(_simpleName)) { + return USE_DEFAULT; + } + if (_simpleName.equals(_NO_NAME) && _namespace == null) { + return NO_NAME; + } + return this; + } + + /** + * @since 2.6 + */ + public static PropertyName construct(String simpleName) + { + if (simpleName == null || simpleName.length() == 0) { + return USE_DEFAULT; + } + return new PropertyName(InternCache.instance.intern(simpleName), null); + } + + public static PropertyName construct(String simpleName, String ns) + { + if (simpleName == null) { + simpleName = ""; + } + if (ns == null && simpleName.length() == 0) { + return USE_DEFAULT; + } + return new PropertyName(InternCache.instance.intern(simpleName), ns); + } + + public PropertyName internSimpleName() + { + if (_simpleName.length() == 0) { // empty String is canonical already + return this; + } + String interned = InternCache.instance.intern(_simpleName); + if (interned == _simpleName) { // was already interned + return this; + } + return new PropertyName(interned, _namespace); + } + + /** + * Fluent factory method for constructing an instance with different + * simple name. + */ + public PropertyName withSimpleName(String simpleName) + { + if (simpleName == null) { + simpleName = ""; + } + if (simpleName.equals(_simpleName)) { + return this; + } + return new PropertyName(simpleName, _namespace); + } + + /** + * Fluent factory method for constructing an instance with different + * namespace. + */ + public PropertyName withNamespace(String ns) { + if (ns == null) { + if (_namespace == null) { + return this; + } + } else if (ns.equals(_namespace)) { + return this; + } + return new PropertyName(_simpleName, ns); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + public String getSimpleName() { + return _simpleName; + } + + /** + * Accessor that may be used to get lazily-constructed efficient + * representation of the simple name. + * + * @since 2.4 + */ + public SerializableString simpleAsEncoded(MapperConfig config) { + SerializableString sstr = _encodedSimple; + if (sstr == null) { + if (config == null) { + sstr = new SerializedString(_simpleName); + } else { + sstr = config.compileString(_simpleName); + } + _encodedSimple = sstr; + } + return sstr; + } + + public String getNamespace() { + return _namespace; + } + + public boolean hasSimpleName() { + return _simpleName.length() > 0; + } + + /** + * @since 2.3 + */ + public boolean hasSimpleName(String str) { + if (str == null) { + return _simpleName == null; + } + return str.equals(_simpleName); + } + + public boolean hasNamespace() { + return _namespace != null; + } + + /** + * Method that is basically equivalent of: + *

+     *   !hasSimpleName() << !hasNamespace();
+     *
+ * + * @since 2.4 + */ + public boolean isEmpty() { + return (_namespace == null) && (_simpleName.isEmpty()); + } + + /* + /********************************************************** + /* Std method overrides + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + /* 13-Nov-2012, tatu: by default, require strict type equality. + * Re-evaluate if this becomes an issue. + */ + if (o.getClass() != getClass()) return false; + // 13-Nov-2012, tatu: Should we have specific rules on matching USE_DEFAULT? + // (like, it only ever matching exact instance) + // If we did, would need to check symmetrically; that is, if either 'this' + // or 'o' was USE_DEFAULT, both would have to be. + PropertyName other = (PropertyName) o; + if (_simpleName == null) { + if (other._simpleName != null) return false; + } else if (!_simpleName.equals(other._simpleName)) { + return false; + } + if (_namespace == null) { + return (null == other._namespace); + } + return _namespace.equals(other._namespace); + } + + @Override + public int hashCode() { + if (_namespace == null) { + return _simpleName.hashCode(); + } + return _namespace.hashCode() ^ _simpleName.hashCode(); + } + + @Override + public String toString() { + if (_namespace == null) { + return _simpleName; + } + return "{"+_namespace + "}" + _simpleName; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyNamingStrategy.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyNamingStrategy.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/PropertyNamingStrategy.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,430 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; + +/** + * Class that defines how names of JSON properties ("external names") + * are derived from names of POJO methods and fields ("internal names"), + * in cases where they are not + * auto-detected and no explicit annotations exist for naming. + * Methods are passed information about POJO member for which name is needed, + * as well as default name that would be used if no custom strategy was used. + *

+ * Default (empty) implementation returns suggested ("default") name unmodified. + *

+ * Note that the strategy is guaranteed to be called once per logical property + * (which may be represented by multiple members; such as pair of a getter and + * a setter), but may be called for each: implementations should not count on + * exact number of times, and should work for any member that represent a + * property. + *

+ * In absence of a registered custom strategy, default Java property naming strategy + * is used, which leaves field names as is, and removes set/get/is prefix + * from methods (as well as lower-cases initial sequence of capitalized + * characters). + */ +@SuppressWarnings("serial") +public class PropertyNamingStrategy // NOTE: was abstract until 2.7 + implements java.io.Serializable +{ + /** + * Naming convention used in languages like C, where words are in lower-case + * letters, separated by underscores. + * See {@link SnakeCaseStrategy} for details. + * + * @since 2.7 (was formerly called {@link #CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES}) + */ + public static final PropertyNamingStrategy SNAKE_CASE = new SnakeCaseStrategy(); + + /** + * Naming convention used in languages like Pascal, where words are capitalized + * and no separator is used between words. + * See {@link PascalCaseStrategy} for details. + * + * @since 2.7 (was formerly called {@link #PASCAL_CASE_TO_CAMEL_CASE}) + */ + public static final PropertyNamingStrategy UPPER_CAMEL_CASE = new UpperCamelCaseStrategy(); + + /** + * Naming convention used in Java, where words other than first are capitalized + * and no separator is used between words. Since this is the native Java naming convention, + * naming strategy will not do any transformation between names in data (JSON) and + * POJOS. + * + * @since 2.7 (was formerly called {@link #PASCAL_CASE_TO_CAMEL_CASE}) + */ + public static final PropertyNamingStrategy LOWER_CAMEL_CASE = new PropertyNamingStrategy(); + + /** + * Naming convention in which all words of the logical name are in lower case, and + * no separator is used between words. + * See {@link LowerCaseStrategy} for details. + * + * @since 2.4 + */ + public static final PropertyNamingStrategy LOWER_CASE = new LowerCaseStrategy(); + + /** + * Naming convention used in languages like Lisp, where words are in lower-case + * letters, separated by hyphens. + * See {@link KebabCaseStrategy} for details. + * + * @since 2.7 + */ + public static final PropertyNamingStrategy KEBAB_CASE = new KebabCaseStrategy(); + + /* + /********************************************************** + /* API + /********************************************************** + */ + + /** + * Method called to find external name (name used in JSON) for given logical + * POJO property, + * as defined by given field. + * + * @param config Configuration in used: either SerializationConfig + * or DeserializationConfig, depending on whether method is called + * during serialization or deserialization + * @param field Field used to access property + * @param defaultName Default name that would be used for property in absence of custom strategy + * + * @return Logical name to use for property that the field represents + */ + public String nameForField(MapperConfig config, AnnotatedField field, + String defaultName) + { + return defaultName; + } + + /** + * Method called to find external name (name used in JSON) for given logical + * POJO property, + * as defined by given getter method; typically called when building a serializer. + * (but not always -- when using "getter-as-setter", may be called during + * deserialization) + * + * @param config Configuration in used: either SerializationConfig + * or DeserializationConfig, depending on whether method is called + * during serialization or deserialization + * @param method Method used to access property. + * @param defaultName Default name that would be used for property in absence of custom strategy + * + * @return Logical name to use for property that the method represents + */ + public String nameForGetterMethod(MapperConfig config, AnnotatedMethod method, + String defaultName) + { + return defaultName; + } + + /** + * Method called to find external name (name used in JSON) for given logical + * POJO property, + * as defined by given setter method; typically called when building a deserializer + * (but not necessarily only then). + * + * @param config Configuration in used: either SerializationConfig + * or DeserializationConfig, depending on whether method is called + * during serialization or deserialization + * @param method Method used to access property. + * @param defaultName Default name that would be used for property in absence of custom strategy + * + * @return Logical name to use for property that the method represents + */ + public String nameForSetterMethod(MapperConfig config, AnnotatedMethod method, + String defaultName) + { + return defaultName; + } + + /** + * Method called to find external name (name used in JSON) for given logical + * POJO property, + * as defined by given constructor parameter; typically called when building a deserializer + * (but not necessarily only then). + * + * @param config Configuration in used: either SerializationConfig + * or DeserializationConfig, depending on whether method is called + * during serialization or deserialization + * @param ctorParam Constructor parameter used to pass property. + * @param defaultName Default name that would be used for property in absence of custom strategy + */ + public String nameForConstructorParameter(MapperConfig config, AnnotatedParameter ctorParam, + String defaultName) + { + return defaultName; + } + + /* + /********************************************************** + /* Public base class for simple implementations + /********************************************************** + */ + + public static abstract class PropertyNamingStrategyBase extends PropertyNamingStrategy + { + @Override + public String nameForField(MapperConfig config, AnnotatedField field, String defaultName) + { + return translate(defaultName); + } + + @Override + public String nameForGetterMethod(MapperConfig config, AnnotatedMethod method, String defaultName) + { + return translate(defaultName); + } + + @Override + public String nameForSetterMethod(MapperConfig config, AnnotatedMethod method, String defaultName) + { + return translate(defaultName); + } + + @Override + public String nameForConstructorParameter(MapperConfig config, AnnotatedParameter ctorParam, + String defaultName) + { + return translate(defaultName); + } + + public abstract String translate(String propertyName); + } + + + /* + /********************************************************** + /* Standard implementations + /********************************************************** + */ + + /** + * A {@link PropertyNamingStrategy} that translates typical camel case Java + * property names to lower case JSON element names, separated by + * underscores. This implementation is somewhat lenient, in that it + * provides some additional translations beyond strictly translating from + * camel case only. In particular, the following translations are applied + * by this PropertyNamingStrategy. + * + *

  • Every upper case letter in the Java property name is translated + * into two characters, an underscore and the lower case equivalent of the + * target character, with three exceptions. + *
    1. For contiguous sequences of upper case letters, characters after + * the first character are replaced only by their lower case equivalent, + * and are not preceded by an underscore. + *
      • This provides for reasonable translations of upper case acronyms, + * e.g., "theWWW" is translated to "the_www".
    2. + *
    3. An upper case character in the first position of the Java property + * name is not preceded by an underscore character, and is translated only + * to its lower case equivalent. + *
      • For example, "Results" is translated to "results", + * and not to "_results".
    4. + *
    5. An upper case character in the Java property name that is already + * preceded by an underscore character is translated only to its lower case + * equivalent, and is not preceded by an additional underscore. + *
      • For example, "user_Name" is translated to + * "user_name", and not to "user__name" (with two + * underscore characters).
  • + *
  • If the Java property name starts with an underscore, then that + * underscore is not included in the translated name, unless the Java + * property name is just one character in length, i.e., it is the + * underscore character. This applies only to the first character of the + * Java property name.
+ * + * These rules result in the following additional example translations from + * Java property names to JSON element names. + *
  • "userName" is translated to "user_name"
  • + *
  • "UserName" is translated to "user_name"
  • + *
  • "USER_NAME" is translated to "user_name"
  • + *
  • "user_name" is translated to "user_name" (unchanged)
  • + *
  • "user" is translated to "user" (unchanged)
  • + *
  • "User" is translated to "user"
  • + *
  • "USER" is translated to "user"
  • + *
  • "_user" is translated to "user"
  • + *
  • "_User" is translated to "user"
  • + *
  • "__user" is translated to "_user" + * (the first of two underscores was removed)
  • + *
  • "user__name" is translated to "user__name" + * (unchanged, with two underscores)
+ * + * @since 2.7 (was previously called } + */ + public static class SnakeCaseStrategy extends PropertyNamingStrategyBase + { + @Override + public String translate(String input) + { + if (input == null) return input; // garbage in, garbage out + int length = input.length(); + StringBuilder result = new StringBuilder(length * 2); + int resultLength = 0; + boolean wasPrevTranslated = false; + for (int i = 0; i < length; i++) + { + char c = input.charAt(i); + if (i > 0 || c != '_') // skip first starting underscore + { + if (Character.isUpperCase(c)) + { + if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') + { + result.append('_'); + resultLength++; + } + c = Character.toLowerCase(c); + wasPrevTranslated = true; + } + else + { + wasPrevTranslated = false; + } + result.append(c); + resultLength++; + } + } + return resultLength > 0 ? result.toString() : input; + } + } + + /** + * A {@link PropertyNamingStrategy} that translates typical camelCase Java + * property names to PascalCase JSON element names (i.e., with a capital + * first letter). In particular, the following translations are applied by + * this PropertyNamingStrategy. + * + *
  • The first lower-case letter in the Java property name is translated + * into its equivalent upper-case representation.
+ * + * This rules result in the following example translation from + * Java property names to JSON element names. + *
  • "userName" is translated to "UserName"
+ * + * @since 2.7 (was formerly called {@link PascalCaseStrategy}) + */ + public static class UpperCamelCaseStrategy extends PropertyNamingStrategyBase + { + /** + * Converts camelCase to PascalCase + * + * For example, "userName" would be converted to + * "UserName". + * + * @param input formatted as camelCase string + * @return input converted to PascalCase format + */ + @Override + public String translate(String input) { + if (input == null || input.length() == 0){ + return input; // garbage in, garbage out + } + // Replace first lower-case letter with upper-case equivalent + char c = input.charAt(0); + char uc = Character.toUpperCase(c); + if (c == uc) { + return input; + } + StringBuilder sb = new StringBuilder(input); + sb.setCharAt(0, uc); + return sb.toString(); + } + } + + /** + * Simple strategy where external name simply only uses lower-case characters, + * and no separators. + * Conversion from internal name like "someOtherValue" would be into external name + * if "someothervalue". + * + * @since 2.4 + */ + public static class LowerCaseStrategy extends PropertyNamingStrategyBase + { + @Override + public String translate(String input) { + return input.toLowerCase(); + } + } + + /** + * Naming strategy similar to {@link SnakeCaseStrategy}, but instead of underscores + * as separators, uses hyphens. Naming convention traditionally used for languages + * like Lisp. + * + * @since 2.7 + */ + public static class KebabCaseStrategy extends PropertyNamingStrategyBase + { + @Override + public String translate(String input) + { + if (input == null) return input; // garbage in, garbage out + int length = input.length(); + if (length == 0) { + return input; + } + + StringBuilder result = new StringBuilder(length + (length >> 1)); + + int upperCount = 0; + + for (int i = 0; i < length; ++i) { + char ch = input.charAt(i); + char lc = Character.toLowerCase(ch); + + if (lc == ch) { // lower-case letter means we can get new word + // but need to check for multi-letter upper-case (acronym), where assumption + // is that the last upper-case char is start of a new word + if (upperCount > 1) { + // so insert hyphen before the last character now + result.insert(result.length() - 1, '-'); + } + upperCount = 0; + } else { + // Otherwise starts new word, unless beginning of string + if ((upperCount == 0) && (i > 0)) { + result.append('-'); + } + ++upperCount; + } + result.append(lc); + } + return result.toString(); + } + } + + /* + /********************************************************** + /* Deprecated variants, aliases + /********************************************************** + */ + + /** + * @deprecated Since 2.7 use {@link #SNAKE_CASE} instead; + */ + @Deprecated // since 2.7 + public static final PropertyNamingStrategy CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES = SNAKE_CASE; + + /** + * @deprecated Since 2.7 use {@link #UPPER_CAMEL_CASE} instead; + */ + @Deprecated // since 2.7 + public static final PropertyNamingStrategy PASCAL_CASE_TO_CAMEL_CASE = UPPER_CAMEL_CASE; + + /** + * @deprecated In 2.7 use {@link SnakeCaseStrategy} instead + */ + @Deprecated + public static class LowerCaseWithUnderscoresStrategy extends SnakeCaseStrategy {} + + /** + * @deprecated In 2.7 use {@link SnakeCaseStrategy} instead + */ + @Deprecated + public static class PascalCaseStrategy extends UpperCamelCaseStrategy {} +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,21 @@ +package com.fasterxml.jackson.databind; + +/** + * Wrapper used when interface does not allow throwing a checked + * {@link JsonMappingException} + */ +@SuppressWarnings("serial") +public class RuntimeJsonMappingException extends RuntimeException +{ + public RuntimeJsonMappingException(JsonMappingException cause) { + super(cause); + } + + public RuntimeJsonMappingException(String message) { + super(message); + } + + public RuntimeJsonMappingException(String message, JsonMappingException cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SequenceWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SequenceWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SequenceWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,327 @@ +package com.fasterxml.jackson.databind; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; + +/** + * Writer class similar to {@link ObjectWriter}, except that it can be used + * for writing sequences of values, not just a single value. + * The main use case is in writing very long sequences, or sequences where + * values are incrementally produced; cases where it would be impractical + * or at least inconvenient to construct a wrapper container around values + * (or where no JSON array is desired around values). + *

+ * Differences from {@link ObjectWriter} include: + *

    + *
  • Instances of {@link SequenceWriter} are stateful, and not thread-safe: + * if sharing, external synchronization must be used. + *
  • Explicit {@link #close} is needed after all values have been written + * ({@link ObjectWriter} can auto-close after individual value writes) + *
+ * + * @since 2.5 + */ +public class SequenceWriter + implements Versioned, java.io.Closeable, java.io.Flushable +{ + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected final DefaultSerializerProvider _provider; + protected final SerializationConfig _config; + protected final JsonGenerator _generator; + + protected final JsonSerializer _rootSerializer; + protected final TypeSerializer _typeSerializer; + + protected final boolean _closeGenerator; + protected final boolean _cfgFlush; + protected final boolean _cfgCloseCloseable; + + /* + /********************************************************** + /* State + /********************************************************** + */ + + /** + * If {@link #_rootSerializer} is not defined (no root type + * was used for constructing {@link ObjectWriter}), we will + * use simple scheme for keeping track of serializers needed. + * Assumption is that + */ + protected PropertySerializerMap _dynamicSerializers; + + /** + * State flag for keeping track of need to write matching END_ARRAY, + * if a START_ARRAY was written during initialization + */ + protected boolean _openArray; + protected boolean _closed; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public SequenceWriter(DefaultSerializerProvider prov, JsonGenerator gen, + boolean closeGenerator, ObjectWriter.Prefetch prefetch) + throws IOException + { + _provider = prov; + _generator = gen; + _closeGenerator = closeGenerator; + _rootSerializer = prefetch.getValueSerializer(); + _typeSerializer = prefetch.getTypeSerializer(); + + _config = prov.getConfig(); + _cfgFlush = _config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE); + _cfgCloseCloseable = _config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE); + // important: need to cache "root value" serializers, to handle polymorphic + // types properly + _dynamicSerializers = PropertySerializerMap.emptyForRootValues(); + } + + public SequenceWriter init(boolean wrapInArray) throws IOException + { + if (wrapInArray) { + _generator.writeStartArray(); + _openArray = true; + } + return this; + } + + /* + /********************************************************** + /* Public API, basic accessors + /********************************************************** + */ + + /** + * Method that will return version information stored in and read from jar + * that contains this class. + */ + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Public API, write operations, related + /********************************************************** + */ + + /** + * Method for writing given value into output, as part of sequence + * to write. If root type was specified for {@link ObjectWriter}, + * value must be of compatible type (same or subtype). + */ + public SequenceWriter write(Object value) throws IOException + { + if (value == null) { + _provider.serializeValue(_generator, null); + return this; + } + + if (_cfgCloseCloseable && (value instanceof Closeable)) { + return _writeCloseableValue(value); + } + JsonSerializer ser = _rootSerializer; + if (ser == null) { + Class type = value.getClass(); + ser = _dynamicSerializers.serializerFor(type); + if (ser == null) { + ser = _findAndAddDynamic(type); + } + } + _provider.serializeValue(_generator, value, null, ser); + if (_cfgFlush) { + _generator.flush(); + } + return this; + } + + /** + * Method for writing given value into output, as part of sequence + * to write; further, full type (often generic, like {@link java.util.Map} + * is passed in case a new + * {@link JsonSerializer} needs to be fetched to handle type + * + * If root type was specified for {@link ObjectWriter}, + * value must be of compatible type (same or subtype). + */ + public SequenceWriter write(Object value, JavaType type) throws IOException + { + if (value == null) { + _provider.serializeValue(_generator, null); + return this; + } + + if (_cfgCloseCloseable && (value instanceof Closeable)) { + return _writeCloseableValue(value, type); + } + /* 15-Dec-2014, tatu: I wonder if this could be come problematic. It shouldn't + * really, since trying to use differently paramterized types in a sequence + * is likely to run into other issues. But who knows; if it does become an + * issue, may need to implement alternative, JavaType-based map. + */ + JsonSerializer ser = _dynamicSerializers.serializerFor(type.getRawClass()); + if (ser == null) { + ser = _findAndAddDynamic(type); + } + _provider.serializeValue(_generator, value, type, ser); + if (_cfgFlush) { + _generator.flush(); + } + return this; + } + + public SequenceWriter writeAll(Object[] value) throws IOException + { + for (int i = 0, len = value.length; i < len; ++i) { + write(value[i]); + } + return this; + } + + // NOTE: redundant wrt variant that takes Iterable, but can not remove or even + // deprecate due to backwards-compatibility needs + public > SequenceWriter writeAll(C container) throws IOException { + for (Object value : container) { + write(value); + } + return this; + } + + /** + * @since 2.7 + */ + public SequenceWriter writeAll(Iterable iterable) throws IOException + { + for (Object value : iterable) { + write(value); + } + return this; + } + + @Override + public void flush() throws IOException { + if (!_closed) { + _generator.flush(); + } + } + + @Override + public void close() throws IOException + { + if (!_closed) { + _closed = true; + if (_openArray) { + _openArray = false; + _generator.writeEndArray(); + } + if (_closeGenerator) { + _generator.close(); + } + } + } + + /* + /********************************************************** + /* Internal helper methods, serializer lookups + /********************************************************** + */ + + protected SequenceWriter _writeCloseableValue(Object value) throws IOException + { + Closeable toClose = (Closeable) value; + try { + JsonSerializer ser = _rootSerializer; + if (ser == null) { + Class type = value.getClass(); + ser = _dynamicSerializers.serializerFor(type); + if (ser == null) { + ser = _findAndAddDynamic(type); + } + } + _provider.serializeValue(_generator, value, null, ser); + if (_cfgFlush) { + _generator.flush(); + } + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } finally { + if (toClose != null) { + try { + toClose.close(); + } catch (IOException ioe) { } + } + } + return this; + } + + protected SequenceWriter _writeCloseableValue(Object value, JavaType type) throws IOException + { + Closeable toClose = (Closeable) value; + try { + // 15-Dec-2014, tatu: As per above, could be problem that we do not pass generic type + JsonSerializer ser = _dynamicSerializers.serializerFor(type.getRawClass()); + if (ser == null) { + ser = _findAndAddDynamic(type); + } + _provider.serializeValue(_generator, value, type, ser); + if (_cfgFlush) { + _generator.flush(); + } + Closeable tmpToClose = toClose; + toClose = null; + tmpToClose.close(); + } finally { + if (toClose != null) { + try { + toClose.close(); + } catch (IOException ioe) { } + } + } + return this; + } + + private final JsonSerializer _findAndAddDynamic(Class type) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result; + if (_typeSerializer == null) { + result = _dynamicSerializers.findAndAddRootValueSerializer(type, _provider); + } else { + result = _dynamicSerializers.addSerializer(type, + new TypeWrappedSerializer(_typeSerializer, _provider.findValueSerializer(type, null))); + } + _dynamicSerializers = result.map; + return result.serializer; + } + + private final JsonSerializer _findAndAddDynamic(JavaType type) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result; + if (_typeSerializer == null) { + result = _dynamicSerializers.findAndAddRootValueSerializer(type, _provider); + } else { + result = _dynamicSerializers.addSerializer(type, + new TypeWrappedSerializer(_typeSerializer, _provider.findValueSerializer(type, null))); + } + _dynamicSerializers = result.map; + return result.serializer; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializationConfig.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializationConfig.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializationConfig.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1007 @@ +package com.fasterxml.jackson.databind; + +import java.text.DateFormat; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.core.util.Instantiatable; +import com.fasterxml.jackson.databind.cfg.*; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.RootNameLookup; + +/** + * Object that contains baseline configuration for serialization + * process. An instance is owned by {@link ObjectMapper}, which + * passes an immutable instance for serialization process to + * {@link SerializerProvider} and {@link SerializerFactory} + * (either directly, or through {@link ObjectWriter}. + *

+ * Note that instances are considered immutable and as such no copies + * should need to be created for sharing; all copying is done with + * "fluent factory" methods. + */ +public final class SerializationConfig + extends MapperConfigBase + implements java.io.Serializable // since 2.1 +{ + // since 2.5 + private static final long serialVersionUID = 1; + + // since 2.6 + protected final static PrettyPrinter DEFAULT_PRETTY_PRINTER = new DefaultPrettyPrinter(); + + // since 2.7 + // Default is "USE_DEFAULTS, USE_DEFAULTS" + protected final static JsonInclude.Value DEFAULT_INCLUSION = JsonInclude.Value.empty(); + + /* + /********************************************************** + /* Configured helper objects + /********************************************************** + */ + /** + * Object used for resolving filter ids to filter instances. + * Non-null if explicitly defined; null by default. + */ + protected final FilterProvider _filterProvider; + + /** + * If "default pretty-printing" is enabled, it will create the instance + * from this blueprint object. + * + * @since 2.6 + */ + protected final PrettyPrinter _defaultPrettyPrinter; + + /* + /********************************************************** + /* Serialization features + /********************************************************** + */ + + /** + * Set of {@link SerializationFeature}s enabled. + */ + protected final int _serFeatures; + + /* + /********************************************************** + /* Generator features: generic, format-specific + /********************************************************** + */ + /** + * States of {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s to enable/disable. + */ + protected final int _generatorFeatures; + + /** + * Bitflag of {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s to enable/disable + */ + protected final int _generatorFeaturesToChange; + + /** + * States of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable. + * + * @since 2.7 + */ + protected final int _formatWriteFeatures; + + /** + * Bitflag of {@link com.fasterxml.jackson.core.FormatFeature}s to enable/disable + * + * @since 2.7 + */ + protected final int _formatWriteFeaturesToChange; + + /* + /********************************************************** + /* Other configuration + /********************************************************** + */ + + /** + * Which Bean/Map properties are to be included in serialization? + * Default settings is to include all regardless of value; can be + * changed to only include non-null properties, or properties + * with non-default values. + *

+ * NOTE: type changed in 2.7, to include both value and content + * inclusion options./ + */ + protected final JsonInclude.Value _serializationInclusion; + + /* + /********************************************************** + /* Life-cycle, constructors + /********************************************************** + */ + + /** + * Constructor used by ObjectMapper to create default configuration object instance. + */ + public SerializationConfig(BaseSettings base, + SubtypeResolver str, SimpleMixInResolver mixins, + RootNameLookup rootNames) + { + super(base, str, mixins, rootNames); + _serFeatures = collectFeatureDefaults(SerializationFeature.class); + _filterProvider = null; + _defaultPrettyPrinter = DEFAULT_PRETTY_PRINTER; + _generatorFeatures = 0; + _generatorFeaturesToChange = 0; + _formatWriteFeatures = 0; + _formatWriteFeaturesToChange = 0; + _serializationInclusion = DEFAULT_INCLUSION; + } + + private SerializationConfig(SerializationConfig src, SubtypeResolver str) + { + super(src, str); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + private SerializationConfig(SerializationConfig src, + int mapperFeatures, int serFeatures, + int generatorFeatures, int generatorFeatureMask, + int formatFeatures, int formatFeaturesMask) + { + super(src, mapperFeatures); + _serFeatures = serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = generatorFeatures; + _generatorFeaturesToChange = generatorFeatureMask; + _formatWriteFeatures = formatFeatures; + _formatWriteFeaturesToChange = formatFeaturesMask; + } + + private SerializationConfig(SerializationConfig src, BaseSettings base) + { + super(src, base); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + private SerializationConfig(SerializationConfig src, FilterProvider filters) + { + super(src); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = filters; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + private SerializationConfig(SerializationConfig src, Class view) + { + super(src, view); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + private SerializationConfig(SerializationConfig src, JsonInclude.Value incl) + { + super(src); + _serFeatures = src._serFeatures; + _serializationInclusion = incl; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + private SerializationConfig(SerializationConfig src, PropertyName rootName) + { + super(src, rootName); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + /** + * @since 2.1 + */ + protected SerializationConfig(SerializationConfig src, ContextAttributes attrs) + { + super(src, attrs); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + /** + * @since 2.1 + */ + protected SerializationConfig(SerializationConfig src, SimpleMixInResolver mixins) + { + super(src, mixins); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + /** + * @since 2.6 + */ + protected SerializationConfig(SerializationConfig src, PrettyPrinter defaultPP) + { + super(src); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = defaultPP; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + /** + * Copy-constructor used for making a copy to be used by new {@link ObjectMapper} + * or {@link ObjectReader}. + * + * @since 2.6 + */ + protected SerializationConfig(SerializationConfig src, SimpleMixInResolver mixins, + RootNameLookup rootNames) + { + super(src, mixins, rootNames); + _serFeatures = src._serFeatures; + _serializationInclusion = src._serializationInclusion; + _filterProvider = src._filterProvider; + _defaultPrettyPrinter = src._defaultPrettyPrinter; + _generatorFeatures = src._generatorFeatures; + _generatorFeaturesToChange = src._generatorFeaturesToChange; + _formatWriteFeatures = src._formatWriteFeatures; + _formatWriteFeaturesToChange = src._formatWriteFeaturesToChange; + } + + /* + /********************************************************** + /* Life-cycle, factory methods from MapperConfig + /********************************************************** + */ + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + */ + @Override + public SerializationConfig with(MapperFeature... features) + { + int newMapperFlags = _mapperFeatures; + for (MapperFeature f : features) { + newMapperFlags |= f.getMask(); + } + return (newMapperFlags == _mapperFeatures) ? this + : new SerializationConfig(this, newMapperFlags, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + */ + @Override + public SerializationConfig without(MapperFeature... features) + { + int newMapperFlags = _mapperFeatures; + for (MapperFeature f : features) { + newMapperFlags &= ~f.getMask(); + } + return (newMapperFlags == _mapperFeatures) ? this + : new SerializationConfig(this, newMapperFlags, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + @Override + public SerializationConfig with(MapperFeature feature, boolean state) + { + int newMapperFlags; + if (state) { + newMapperFlags = _mapperFeatures | feature.getMask(); + } else { + newMapperFlags = _mapperFeatures & ~feature.getMask(); + } + return (newMapperFlags == _mapperFeatures) ? this + : new SerializationConfig(this, newMapperFlags, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + @Override + public SerializationConfig with(AnnotationIntrospector ai) { + return _withBase(_base.withAnnotationIntrospector(ai)); + } + + @Override + public SerializationConfig withAppendedAnnotationIntrospector(AnnotationIntrospector ai) { + return _withBase(_base.withAppendedAnnotationIntrospector(ai)); + } + + @Override + public SerializationConfig withInsertedAnnotationIntrospector(AnnotationIntrospector ai) { + return _withBase(_base.withInsertedAnnotationIntrospector(ai)); + } + + @Override + public SerializationConfig with(ClassIntrospector ci) { + return _withBase(_base.withClassIntrospector(ci)); + } + + /** + * In addition to constructing instance with specified date format, + * will enable or disable SerializationFeature.WRITE_DATES_AS_TIMESTAMPS + * (enable if format set as null; disable if non-null) + */ + @Override + public SerializationConfig with(DateFormat df) { + SerializationConfig cfg = new SerializationConfig(this, _base.withDateFormat(df)); + // Also need to toggle this feature based on existence of date format: + if (df == null) { + cfg = cfg.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } else { + cfg = cfg.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + return cfg; + } + + @Override + public SerializationConfig with(HandlerInstantiator hi) { + return _withBase(_base.withHandlerInstantiator(hi)); + } + + @Override + public SerializationConfig with(PropertyNamingStrategy pns) { + return _withBase(_base.withPropertyNamingStrategy(pns)); + } + + @Override + public SerializationConfig withRootName(PropertyName rootName) { + if (rootName == null) { + if (_rootName == null) { + return this; + } + } else if (rootName.equals(_rootName)) { + return this; + } + return new SerializationConfig(this, rootName); + } + + @Override + public SerializationConfig with(SubtypeResolver str) { + return (str == _subtypeResolver)? this : new SerializationConfig(this, str); + } + + @Override + public SerializationConfig with(TypeFactory tf) { + return _withBase(_base.withTypeFactory(tf)); + } + + @Override + public SerializationConfig with(TypeResolverBuilder trb) { + return _withBase(_base.withTypeResolverBuilder(trb)); + } + + @Override + public SerializationConfig withView(Class view) { + return (_view == view) ? this : new SerializationConfig(this, view); + } + + @Override + public SerializationConfig with(VisibilityChecker vc) { + return _withBase(_base.withVisibilityChecker(vc)); + } + + @Override + public SerializationConfig withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) { + return _withBase(_base.withVisibility(forMethod, visibility)); + } + + @Override + public SerializationConfig with(Locale l) { + return _withBase(_base.with(l)); + } + + @Override + public SerializationConfig with(TimeZone tz) { + return _withBase(_base.with(tz)); + } + + @Override + public SerializationConfig with(Base64Variant base64) { + return _withBase(_base.with(base64)); + } + + @Override + public SerializationConfig with(ContextAttributes attrs) { + return (attrs == _attributes) ? this : new SerializationConfig(this, attrs); + } + + private final SerializationConfig _withBase(BaseSettings newBase) { + return (_base == newBase) ? this : new SerializationConfig(this, newBase); + } + + /* + /********************************************************** + /* Factory methods for SerializationFeature + /********************************************************** + */ + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature enabled. + */ + public SerializationConfig with(SerializationFeature feature) + { + int newSerFeatures = _serFeatures | feature.getMask(); + return (newSerFeatures == _serFeatures) ? this + : new SerializationConfig(this, _mapperFeatures, newSerFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + */ + public SerializationConfig with(SerializationFeature first, SerializationFeature... features) + { + int newSerFeatures = _serFeatures | first.getMask(); + for (SerializationFeature f : features) { + newSerFeatures |= f.getMask(); + } + return (newSerFeatures == _serFeatures) ? this + : new SerializationConfig(this, _mapperFeatures, newSerFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + */ + public SerializationConfig withFeatures(SerializationFeature... features) + { + int newSerFeatures = _serFeatures; + for (SerializationFeature f : features) { + newSerFeatures |= f.getMask(); + } + return (newSerFeatures == _serFeatures) ? this + : new SerializationConfig(this, _mapperFeatures, newSerFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature disabled. + */ + public SerializationConfig without(SerializationFeature feature) + { + int newSerFeatures = _serFeatures & ~feature.getMask(); + return (newSerFeatures == _serFeatures) ? this + : new SerializationConfig(this, _mapperFeatures, newSerFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + */ + public SerializationConfig without(SerializationFeature first, SerializationFeature... features) + { + int newSerFeatures = _serFeatures & ~first.getMask(); + for (SerializationFeature f : features) { + newSerFeatures &= ~f.getMask(); + } + return (newSerFeatures == _serFeatures) ? this + : new SerializationConfig(this, _mapperFeatures, newSerFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + */ + public SerializationConfig withoutFeatures(SerializationFeature... features) + { + int newSerFeatures = _serFeatures; + for (SerializationFeature f : features) { + newSerFeatures &= ~f.getMask(); + } + return (newSerFeatures == _serFeatures) ? this + : new SerializationConfig(this, _mapperFeatures, newSerFeatures, + _generatorFeatures, _generatorFeaturesToChange, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /* + /********************************************************** + /* Factory methods for JsonGenerator.Feature (2.5) + /********************************************************** + */ + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature enabled. + * + * @since 2.5 + */ + public SerializationConfig with(JsonGenerator.Feature feature) + { + int newSet = _generatorFeatures | feature.getMask(); + int newMask = _generatorFeaturesToChange | feature.getMask(); + return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + newSet, newMask, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + * + * @since 2.5 + */ + public SerializationConfig withFeatures(JsonGenerator.Feature... features) + { + int newSet = _generatorFeatures; + int newMask = _generatorFeaturesToChange; + for (JsonGenerator.Feature f : features) { + int mask = f.getMask(); + newSet |= mask; + newMask |= mask; + } + return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + newSet, newMask, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature disabled. + * + * @since 2.5 + */ + public SerializationConfig without(JsonGenerator.Feature feature) + { + int newSet = _generatorFeatures & ~feature.getMask(); + int newMask = _generatorFeaturesToChange | feature.getMask(); + return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + newSet, newMask, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + * + * @since 2.5 + */ + public SerializationConfig withoutFeatures(JsonGenerator.Feature... features) + { + int newSet = _generatorFeatures; + int newMask = _generatorFeaturesToChange; + for (JsonGenerator.Feature f : features) { + int mask = f.getMask(); + newSet &= ~mask; + newMask |= mask; + } + return ((_generatorFeatures == newSet) && (_generatorFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + newSet, newMask, + _formatWriteFeatures, _formatWriteFeaturesToChange); + } + + /* + /********************************************************** + /* Factory methods for FormatFeature (2.7) + /********************************************************** + */ + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature enabled. + * + * @since 2.7 + */ + public SerializationConfig with(FormatFeature feature) + { + int newSet = _formatWriteFeatures | feature.getMask(); + int newMask = _formatWriteFeaturesToChange | feature.getMask(); + return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + newSet, newMask); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features enabled. + * + * @since 2.7 + */ + public SerializationConfig withFeatures(FormatFeature... features) + { + int newSet = _formatWriteFeatures; + int newMask = _formatWriteFeaturesToChange; + for (FormatFeature f : features) { + int mask = f.getMask(); + newSet |= mask; + newMask |= mask; + } + return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + newSet, newMask); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified feature disabled. + * + * @since 2.7 + */ + public SerializationConfig without(FormatFeature feature) + { + int newSet = _formatWriteFeatures & ~feature.getMask(); + int newMask = _formatWriteFeaturesToChange | feature.getMask(); + return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + newSet, newMask); + } + + /** + * Fluent factory method that will construct and return a new configuration + * object instance with specified features disabled. + * + * @since 2.7 + */ + public SerializationConfig withoutFeatures(FormatFeature... features) + { + int newSet = _formatWriteFeatures; + int newMask = _formatWriteFeaturesToChange; + for (FormatFeature f : features) { + int mask = f.getMask(); + newSet &= ~mask; + newMask |= mask; + } + return ((_formatWriteFeatures == newSet) && (_formatWriteFeaturesToChange == newMask)) ? this : + new SerializationConfig(this, _mapperFeatures, _serFeatures, + _generatorFeatures, _generatorFeaturesToChange, + newSet, newMask); + } + + /* + /********************************************************** + /* Factory methods, other + /********************************************************** + */ + + public SerializationConfig withFilters(FilterProvider filterProvider) { + return (filterProvider == _filterProvider) ? this : new SerializationConfig(this, filterProvider); + } + + /** + * @deprecated Since 2.7 use {@link #withPropertyInclusion} instead + */ + @Deprecated + public SerializationConfig withSerializationInclusion(JsonInclude.Include incl) { + return withPropertyInclusion(DEFAULT_INCLUSION.withValueInclusion(incl)); + } + + /** + * @since 2.7 + */ + public SerializationConfig withPropertyInclusion(JsonInclude.Value incl) { + if (_serializationInclusion.equals(incl)) { + return this; + } + return new SerializationConfig(this, incl); + } + + /** + * @since 2.6 + */ + public SerializationConfig withDefaultPrettyPrinter(PrettyPrinter pp) { + return (_defaultPrettyPrinter == pp) ? this: new SerializationConfig(this, pp); + } + + /* + /********************************************************** + /* Factories for objects configured here + /********************************************************** + */ + + public PrettyPrinter constructDefaultPrettyPrinter() { + PrettyPrinter pp = _defaultPrettyPrinter; + if (pp instanceof Instantiatable) { + pp = (PrettyPrinter) ((Instantiatable) pp).createInstance(); + } + return pp; + } + + /* + /********************************************************** + /* JsonParser initialization + /********************************************************** + */ + + /** + * Method called by {@link ObjectMapper} and {@link ObjectWriter} + * to modify those {@link com.fasterxml.jackson.core.JsonGenerator.Feature} settings + * that have been configured via this config instance. + * + * @since 2.5 + */ + public void initialize(JsonGenerator g) + { + if (SerializationFeature.INDENT_OUTPUT.enabledIn(_serFeatures)) { + // but do not override an explicitly set one + if (g.getPrettyPrinter() == null) { + PrettyPrinter pp = constructDefaultPrettyPrinter(); + if (pp != null) { + g.setPrettyPrinter(pp); + } + } + } + @SuppressWarnings("deprecation") + boolean useBigDec = SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_serFeatures); + + int mask = _generatorFeaturesToChange; + if ((mask != 0) || useBigDec) { + int newFlags = _generatorFeatures; + // although deprecated, needs to be supported for now + if (useBigDec) { + int f = JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN.getMask(); + newFlags |= f; + mask |= f; + } + g.overrideStdFeatures(newFlags, mask); + } + if (_formatWriteFeaturesToChange != 0) { + g.overrideFormatFeatures(_formatWriteFeatures, _formatWriteFeaturesToChange); + } + } + + /* + /********************************************************** + /* MapperConfig implementation/overrides: introspection + /********************************************************** + */ + + @Override + public AnnotationIntrospector getAnnotationIntrospector() + { + if (isEnabled(MapperFeature.USE_ANNOTATIONS)) { + return super.getAnnotationIntrospector(); + } + return AnnotationIntrospector.nopInstance(); + } + + /** + * Accessor for getting bean description that only contains class + * annotations: useful if no getter/setter/creator information is needed. + */ + @Override + public BeanDescription introspectClassAnnotations(JavaType type) { + return getClassIntrospector().forClassAnnotations(this, type, this); + } + + /** + * Accessor for getting bean description that only contains immediate class + * annotations: ones from the class, and its direct mix-in, if any, but + * not from super types. + */ + @Override + public BeanDescription introspectDirectClassAnnotations(JavaType type) { + return getClassIntrospector().forDirectClassAnnotations(this, type, this); + } + + @Override + public VisibilityChecker getDefaultVisibilityChecker() + { + VisibilityChecker vchecker = super.getDefaultVisibilityChecker(); + if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) { + vchecker = vchecker.withGetterVisibility(Visibility.NONE); + } + // then global overrides (disabling) + if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) { + vchecker = vchecker.withIsGetterVisibility(Visibility.NONE); + } + if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) { + vchecker = vchecker.withFieldVisibility(Visibility.NONE); + } + return vchecker; + } + + /* + /********************************************************** + /* Configuration: default settings with per-type overrides + /********************************************************** + */ + + /** + * @deprecated Since 2.7 use {@link #getDefaultPropertyInclusion} instead + */ + @Deprecated + public JsonInclude.Include getSerializationInclusion() + { + JsonInclude.Include incl = _serializationInclusion.getValueInclusion(); + return (incl == JsonInclude.Include.USE_DEFAULTS) ? JsonInclude.Include.ALWAYS : incl; + } + + @Override + public JsonInclude.Value getDefaultPropertyInclusion() { + return _serializationInclusion; + } + + @Override + public JsonInclude.Value getDefaultPropertyInclusion(Class baseType) { + // !!! TODO: per-type defaults + return _serializationInclusion; + } + + @Override + public JsonFormat.Value getDefaultPropertyFormat(Class baseType) { + // !!! TODO: per-type defaults + return EMPTY_FORMAT; + } + + /* + /********************************************************** + /* Configuration: other + /********************************************************** + */ + + @Override + public boolean useRootWrapping() + { + if (_rootName != null) { // empty String disables wrapping; non-empty enables + return !_rootName.isEmpty(); + } + return isEnabled(SerializationFeature.WRAP_ROOT_VALUE); + } + + public final boolean isEnabled(SerializationFeature f) { + return (_serFeatures & f.getMask()) != 0; + } + + /** + * Accessor method that first checks if we have any overrides + * for feature, and only if not, checks state of passed-in + * factory. + * + * @since 2.5 + */ + public final boolean isEnabled(JsonGenerator.Feature f, JsonFactory factory) { + int mask = f.getMask(); + if ((_generatorFeaturesToChange & mask) != 0) { + return (_generatorFeatures & f.getMask()) != 0; + } + return factory.isEnabled(f); + } + + /** + * "Bulk" access method for checking that all features specified by + * mask are enabled. + * + * @since 2.3 + */ + public final boolean hasSerializationFeatures(int featureMask) { + return (_serFeatures & featureMask) == featureMask; + } + + public final int getSerializationFeatures() { + return _serFeatures; + } + + /** + * Method for getting provider used for locating filters given + * id (which is usually provided with filter annotations). + * Will be null if no provided was set for {@link ObjectWriter} + * (or if serialization directly called from {@link ObjectMapper}) + */ + public FilterProvider getFilterProvider() { + return _filterProvider; + } + + /** + * Accessor for configured blueprint "default" {@link PrettyPrinter} to + * use, if default pretty-printing is enabled. + *

+ * NOTE: returns the "blueprint" instance, and does NOT construct + * an instance ready to use; call {@link #constructDefaultPrettyPrinter()} if + * actually usable instance is desired. + * + * @since 2.6 + */ + public PrettyPrinter getDefaultPrettyPrinter() { + return _defaultPrettyPrinter; + } + + /* + /********************************************************** + /* Introspection methods + /********************************************************** + */ + + /** + * Method that will introspect full bean properties for the purpose + * of building a bean serializer + */ + @SuppressWarnings("unchecked") + public T introspect(JavaType type) { + return (T) getClassIntrospector().forSerialization(this, type, this); + } + + /* + /********************************************************** + /* Debug support + /********************************************************** + */ + + @Override + public String toString() { + return "[SerializationConfig: flags=0x"+Integer.toHexString(_serFeatures)+"]"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializationFeature.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializationFeature.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializationFeature.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,425 @@ +package com.fasterxml.jackson.databind; + +import com.fasterxml.jackson.databind.cfg.ConfigFeature; + +/** + * Enumeration that defines simple on/off features that affect + * the way Java objects are serialized. + *

+ * Note that features can be set both through + * {@link ObjectMapper} (as sort of defaults) and through + * {@link ObjectWriter}. + * In first case these defaults must follow "config-then-use" patterns + * (i.e. defined once, not changed afterwards); all per-call + * changes must be done using {@link ObjectWriter}. + */ +public enum SerializationFeature implements ConfigFeature +{ + /* + /****************************************************** + /* Generic output features + /****************************************************** + */ + + /** + * Feature that can be enabled to make root value (usually JSON + * Object but can be any type) wrapped within a single property + * JSON object, where key as the "root name", as determined by + * annotation introspector (esp. for JAXB that uses + * @XmlRootElement.name) or fallback (non-qualified + * class name). + * Feature is mostly intended for JAXB compatibility. + *

+ * Feature is disabled by default. + */ + WRAP_ROOT_VALUE(false), + + /** + * Feature that allows enabling (or disabling) indentation + * for the underlying generator, using the default pretty + * printer configured for {@link ObjectMapper} (and + * {@link ObjectWriter}s created from mapper). + *

+ * Note that the default pretty printer is only used if + * no explicit {@link com.fasterxml.jackson.core.PrettyPrinter} has been configured + * for the generator or {@link ObjectWriter}. + *

+ * Feature is disabled by default. + */ + INDENT_OUTPUT(false), + + /* + /****************************************************** + /* Error handling features + /****************************************************** + */ + + /** + * Feature that determines what happens when no accessors are + * found for a type (and there are no annotations to indicate + * it is meant to be serialized). If enabled (default), an + * exception is thrown to indicate these as non-serializable + * types; if disabled, they are serialized as empty Objects, + * i.e. without any properties. + *

+ * Note that empty types that this feature has only effect on + * those "empty" beans that do not have any recognized annotations + * (like @JsonSerialize): ones that do have annotations + * do not result in an exception being thrown. + *

+ * Feature is enabled by default. + */ + FAIL_ON_EMPTY_BEANS(true), + + /** + * Feature that determines what happens when a direct self-reference + * is detected by a POJO (and no Object Id handling is enabled for it): + * either a {@link JsonMappingException} is + * thrown (if true), or reference is normally processed (false). + *

+ * Feature is enabled by default. + * + * @since 2.4 + */ + FAIL_ON_SELF_REFERENCES(true), + + /** + * Feature that determines whether Jackson code should catch + * and wrap {@link Exception}s (but never {@link Error}s!) + * to add additional information about + * location (within input) of problem or not. If enabled, + * most exceptions will be caught and re-thrown (exception + * specifically being that {@link java.io.IOException}s may be passed + * as is, since they are declared as throwable); this can be + * convenient both in that all exceptions will be checked and + * declared, and so there is more contextual information. + * However, sometimes calling application may just want "raw" + * unchecked exceptions passed as is. + *

+ *

+ * Feature is enabled by default. + */ + WRAP_EXCEPTIONS(true), + + /** + * Feature that determines what happens when an object which + * normally has type information included by Jackson is used + * in conjunction with {@link com.fasterxml.jackson.annotation.JsonUnwrapped}. + * In the default (enabled) state, an error will be thrown when + * an unwrapped object has type information. When disabled, the + * object will be unwrapped and the type information discarded. + *

+ * Feature is enabled by default. + * + * @since 2.4 + */ + FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS(true), + + /* + /****************************************************** + /* Output life cycle features + /****************************************************** + */ + + /** + * Feature that determines whether close method of + * serialized root level objects (ones for which ObjectMapper's + * writeValue() (or equivalent) method is called) + * that implement {@link java.io.Closeable} + * is called after serialization or not. If enabled, close() will + * be called after serialization completes (whether succesfully, or + * due to an error manifested by an exception being thrown). You can + * think of this as sort of "finally" processing. + *

+ * NOTE: only affects behavior with root objects, and not other + * objects reachable from the root object. Put another way, only one + * call will be made for each 'writeValue' call. + *

+ * Feature is disabled by default. + */ + CLOSE_CLOSEABLE(false), + + /** + * Feature that determines whether JsonGenerator.flush() is + * called after writeValue() method that takes JsonGenerator + * as an argument completes (i.e. does NOT affect methods + * that use other destinations); same for methods in {@link ObjectWriter}. + * This usually makes sense; but there are cases where flushing + * should not be forced: for example when underlying stream is + * compressing and flush() causes compression state to be flushed + * (which occurs with some compression codecs). + *

+ * Feature is enabled by default. + */ + FLUSH_AFTER_WRITE_VALUE(true), + + /* + /****************************************************** + /* Datatype-specific serialization configuration + /****************************************************** + */ + + /** + * Feature that determines whether Date (and date/time) values + * (and Date-based things like {@link java.util.Calendar}s) are to be + * serialized as numeric timestamps (true; the default), + * or as something else (usually textual representation). + * If textual representation is used, the actual format is + * one returned by a call to + * {@link com.fasterxml.jackson.databind.SerializationConfig#getDateFormat}: + * the default setting being {@link com.fasterxml.jackson.databind.util.StdDateFormat}, + * which corresponds to format String of "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + * (see {@link java.text.DateFormat} for details of format Strings). + *

+ * Note: whether this feature affects handling of other date-related + * types depend on handlers of those types, although ideally they + * should use this feature + *

+ * Note: whether {@link java.util.Map} keys are serialized as Strings + * or not is controlled using {@link #WRITE_DATE_KEYS_AS_TIMESTAMPS}. + *

+ * Feature is enabled by default, so that date/time are by default + * serialized as timestamps. + */ + WRITE_DATES_AS_TIMESTAMPS(true), + + /** + * Feature that determines whether {@link java.util.Date}s + * (and sub-types) used as {@link java.util.Map} keys are serialized + * as timestamps or not (if not, will be serialized as textual + * values). + *

+ * Default value is 'false', meaning that Date-valued Map keys are serialized + * as textual (ISO-8601) values. + *

+ * Feature is disabled by default. + */ + WRITE_DATE_KEYS_AS_TIMESTAMPS(false), + + /** + * Feature that determines whether date/date-time values should be serialized + * so that they include timezone id, in cases where type itself contains + * timezone information. Including this information may lead to compatibility + * issues because ISO-8601 specification does not define formats that include + * such information. + *

+ * If enabled, Timezone id should be included using format specified + * with Java 8 DateTimeFormatter#ISO_ZONED_DATE_TIME definition + * (for example, '2011-12-03T10:15:30+01:00[Europe/Paris]'). + *

+ * Note: setting has no relevance if date/time values are serialized as timestamps. + *

+ * Feature is disabled by default, so that zone id is NOT included; rather, timezone + * offset is used for ISO-8601 compatibility (if any timezone information is + * included in value). + * + * @since 2.6 + */ + WRITE_DATES_WITH_ZONE_ID(false), + + /** + * Feature that determines whether time values that represents time periods + * (durations, periods, ranges) are to be serialized by default using + * a numeric (true) or textual (false) representations. Note that numeric + * representation may mean either simple number, or an array of numbers, + * depending on type. + *

+ * Note: whether {@link java.util.Map} keys are serialized as Strings + * or not is controlled using {@link #WRITE_DATE_KEYS_AS_TIMESTAMPS}. + *

+ * Feature is enabled by default, so that period/duration are by default + * serialized as timestamps. + * + * @since 2.5 + */ + WRITE_DURATIONS_AS_TIMESTAMPS(true), + + /** + * Feature that determines how type char[] is serialized: + * when enabled, will be serialized as an explict JSON array (with + * single-character Strings as values); when disabled, defaults to + * serializing them as Strings (which is more compact). + *

+ * Feature is disabled by default. + */ + WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false), + + /** + * Feature that determines standard serialization mechanism used for + * Enum values: if enabled, return value of Enum.toString() + * is used; if disabled, return value of Enum.name() is used. + *

+ * Note: this feature should usually have same value + * as {@link DeserializationFeature#READ_ENUMS_USING_TO_STRING}. + *

+ * Feature is disabled by default. + */ + WRITE_ENUMS_USING_TO_STRING(false), + + /** + * Feature that determines whethere Java Enum values are serialized + * as numbers (true), or textual values (false). If textual values are + * used, other settings are also considered. + * If this feature is enabled, + * return value of Enum.ordinal() + * (an integer) will be used as the serialization. + *

+ * Note that this feature has precedence over {@link #WRITE_ENUMS_USING_TO_STRING}, + * which is only considered if this feature is set to false. + *

+ * Feature is disabled by default. + */ + WRITE_ENUMS_USING_INDEX(false), + + /** + * Feature that determines whether Map entries with null values are + * to be serialized (true) or not (false). + *

+ * Feature is enabled by default. + */ + WRITE_NULL_MAP_VALUES(true), + + /** + * Feature that determines whether Container properties (POJO properties + * with declared value of Collection or array; i.e. things that produce JSON + * arrays) that are empty (have no elements) + * will be serialized as empty JSON arrays (true), or suppressed from output (false). + *

+ * Note that this does not change behavior of {@link java.util.Map}s, or + * "Collection-like" types. + *

+ * Feature is enabled by default. + */ + WRITE_EMPTY_JSON_ARRAYS(true), + + /** + * Feature added for interoperability, to work with oddities of + * so-called "BadgerFish" convention. + * Feature determines handling of single element {@link java.util.Collection}s + * and arrays: if enabled, {@link java.util.Collection}s and arrays that contain exactly + * one element will be serialized as if that element itself was serialized. + *

+ * When enabled, a POJO with array that normally looks like this: + *

+     *  { "arrayProperty" : [ 1 ] }
+     *
+ * will instead be serialized as + *
+     *  { "arrayProperty" : 1 }
+     *
+ *

+ * Note that this feature is counterpart to {@link DeserializationFeature#ACCEPT_SINGLE_VALUE_AS_ARRAY} + * (that is, usually both are enabled, or neither is). + *

+ * Feature is disabled by default, so that no special handling is done. + */ + WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(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: since this feature typically requires use of + * {@link com.fasterxml.jackson.core.JsonGenerator#writeNumber(String)} + * it may cause compatibility problems since not all {@link com.fasterxml.jackson.core.JsonGenerator} + * implementations support such mode of output: usually only text-based formats + * support it. + *

+ * Feature is disabled by default. + * + * @deprecated Since 2.5: use {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_BIGDECIMAL_AS_PLAIN} instead + * (using {@link ObjectWriter#with(com.fasterxml.jackson.core.JsonGenerator.Feature)}). + */ + @Deprecated // since 2.5 + WRITE_BIGDECIMAL_AS_PLAIN(false), + + /** + * Feature that controls whether numeric timestamp values are + * to be written using nanosecond timestamps (enabled) or not (disabled); + * if and only if datatype supports such resolution. + * Only newer datatypes (such as Java8 Date/Time) support such resolution -- + * older types (pre-Java8 java.util.Date etc) and Joda do not -- + * and this setting has no effect on such types. + *

+ * If disabled, standard millisecond timestamps are assumed. + * This is the counterpart to {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}. + *

+ * Feature is enabled by default, to support most accurate time values possible. + * + * @since 2.2 + */ + WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true), + + /** + * Feature that determines whether {@link java.util.Map} entries are first + * sorted by key before serialization or not: if enabled, additional sorting + * step is performed if necessary (not necessary for {@link java.util.SortedMap}s), + * if disabled, no additional sorting is needed. + *

+ * Feature is disabled by default. + */ + ORDER_MAP_ENTRIES_BY_KEYS(false), + + /* + /****************************************************** + /* Other + /****************************************************** + */ + + /** + * Feature that determines whether {@link ObjectWriter} should + * try to eagerly fetch necessary {@link JsonSerializer} when + * possible. This improves performance in cases where similarly + * configured {@link ObjectWriter} instance is used multiple + * times; and should not significantly affect single-use cases. + *

+ * Note that there should not be any need to normally disable this + * feature: only consider that if there are actual perceived problems. + *

+ * Feature is enabled by default. + * + * @since 2.1 + */ + EAGER_SERIALIZER_FETCH(true), + + /** + * Feature that determines whether Object Identity is compared using + * true JVM-level identity of Object (false); or, equals() method. + * Latter is sometimes useful when dealing with Database-bound objects with + * ORM libraries (like Hibernate). Note that Object itself is actually compared, + * and NOT Object Id; naming of this feature is somewhat confusing, so it is important + * that Object for which identity is to be preserved are considered equal, + * above and beyond ids (which are always compared using equality anyway). + *

+ * NOTE: due to the way functionality is implemented, it is very important that + * in addition to overriding {@link Object#equals} for Objects to match (to be considered + * "same") it is also necessary to ensure that {@link Object#hashCode()} is overridden + * to produce the exact same value for equal instances. + *

+ * Feature is disabled by default; meaning that strict identity is used, not + * equals() + * + * @since 2.3 + */ + USE_EQUALITY_FOR_OBJECT_ID(false) + ; + + private final boolean _defaultState; + private final int _mask; + + private SerializationFeature(boolean defaultState) { + _defaultState = defaultState; + _mask = (1 << ordinal()); + } + + @Override + public boolean enabledByDefault() { return _defaultState; } + + + @Override + public int getMask() { return _mask; } + + @Override + public boolean enabledIn(int flags) { return (flags & _mask) != 0; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializerProvider.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializerProvider.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/SerializerProvider.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1271 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.impl.FailingSerializer; +import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap; +import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; +import com.fasterxml.jackson.databind.ser.impl.UnknownSerializer; +import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; +import com.fasterxml.jackson.databind.ser.std.NullSerializer; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Class that defines API used by {@link ObjectMapper} and + * {@link JsonSerializer}s to obtain serializers capable of serializing + * instances of specific types; as well as the default implementation + * of the functionality. + *

+ * Provider handles caching aspects of serializer handling; all construction + * details are delegated to {@link SerializerFactory} instance. + *

+ * Object life-cycle is such that an initial instance ("blueprint") is created + * and referenced by {@link ObjectMapper} and {@link ObjectWriter} intances; + * but for actual usage, a configured instance is created by using + * a create method in sub-class + * {@link com.fasterxml.jackson.databind.ser.DefaultSerializerProvider}. + * Only this instance can be used for actual serialization calls; blueprint + * object is only to be used for creating instances. + */ +public abstract class SerializerProvider + extends DatabindContext +{ + /** + * Setting for determining whether mappings for "unknown classes" should be + * cached for faster resolution. Usually this isn't needed, but maybe it + * is in some cases? + */ + protected final static boolean CACHE_UNKNOWN_MAPPINGS = false; + + public final static JsonSerializer DEFAULT_NULL_KEY_SERIALIZER = + new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)"); + + /** + * Placeholder serializer used when java.lang.Object typed property + * is marked to be serialized. + *
+ * NOTE: starting with 2.6, this instance is NOT used for any other types, and + * separate instances are constructed for "empty" Beans. + *

+ * NOTE: changed to protected for 2.3; no need to be publicly available. + */ + protected final static JsonSerializer DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer(); + + /* + /********************************************************** + /* Configuration, general + /********************************************************** + */ + + /** + * Serialization configuration to use for serialization processing. + */ + final protected SerializationConfig _config; + + /** + * View used for currently active serialization, if any. + * Only set for non-blueprint instances. + */ + final protected Class _serializationView; + + /* + /********************************************************** + /* Configuration, factories + /********************************************************** + */ + + /** + * Factory used for constructing actual serializer instances. + * Only set for non-blueprint instances. + */ + final protected SerializerFactory _serializerFactory; + + /* + /********************************************************** + /* Helper objects for caching, reuse + /********************************************************** + */ + + /** + * Cache for doing type-to-value-serializer lookups. + */ + final protected SerializerCache _serializerCache; + + /** + * Lazily-constructed holder for per-call attributes. + * Only set for non-blueprint instances. + * + * @since 2.3 + */ + protected transient ContextAttributes _attributes; + + /* + /********************************************************** + /* Configuration, specialized serializers + /********************************************************** + */ + + /** + * Serializer that gets called for values of types for which no + * serializers can be constructed. + *

+ * The default serializer will simply thrown an exception. + */ + protected JsonSerializer _unknownTypeSerializer = DEFAULT_UNKNOWN_SERIALIZER; + + /** + * Serializer used to output non-null keys of Maps (which will get + * output as JSON Objects), if not null; if null, us the standard + * default key serializer. + */ + protected JsonSerializer _keySerializer; + + /** + * Serializer used to output a null value. Default implementation + * writes nulls using {@link JsonGenerator#writeNull}. + */ + protected JsonSerializer _nullValueSerializer = NullSerializer.instance; + + /** + * Serializer used to (try to) output a null key, due to an entry of + * {@link java.util.Map} having null key. + * The default implementation will throw an exception if this happens; + * alternative implementation (like one that would write an Empty String) + * can be defined. + */ + protected JsonSerializer _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER; + + /* + /********************************************************** + /* State, for non-blueprint instances: generic + /********************************************************** + */ + + /** + * For fast lookups, we will have a local non-shared read-only + * map that contains serializers previously fetched. + */ + protected final ReadOnlyClassToSerializerMap _knownSerializers; + + /** + * Lazily acquired and instantiated formatter object: initialized + * first time it is needed, reused afterwards. Used via instances + * (not blueprints), so that access need not be thread-safe. + */ + protected DateFormat _dateFormat; + + /** + * Flag set to indicate that we are using vanilla null value serialization + * + * @since 2.3 + */ + protected final boolean _stdNullValueSerializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Constructor for creating master (or "blue-print") provider object, + * which is only used as the template for constructing per-binding + * instances. + */ + public SerializerProvider() + { + _config = null; + _serializerFactory = null; + _serializerCache = new SerializerCache(); + // Blueprints doesn't have access to any serializers... + _knownSerializers = null; + + _serializationView = null; + _attributes = null; + + // not relevant for blueprint instance, could set either way: + _stdNullValueSerializer = true; + } + + /** + * "Copy-constructor", used by sub-classes when creating actual non-blueprint + * instances to use. + * + * @param src Blueprint object used as the baseline for this instance + */ + protected SerializerProvider(SerializerProvider src, + SerializationConfig config, SerializerFactory f) + { + if (config == null) { + throw new NullPointerException(); + } + _serializerFactory = f; + _config = config; + + _serializerCache = src._serializerCache; + _unknownTypeSerializer = src._unknownTypeSerializer; + _keySerializer = src._keySerializer; + _nullValueSerializer = src._nullValueSerializer; + _nullKeySerializer = src._nullKeySerializer; + + _stdNullValueSerializer = (_nullValueSerializer == DEFAULT_NULL_KEY_SERIALIZER); + + _serializationView = config.getActiveView(); + _attributes = config.getAttributes(); + + /* Non-blueprint instances do have a read-only map; one that doesn't + * need synchronization for lookups. + */ + _knownSerializers = _serializerCache.getReadOnlyLookupMap(); + } + + /** + * Copy-constructor used when making a copy of a blueprint instance. + * + * @since 2.5 + */ + protected SerializerProvider(SerializerProvider src) + { + // since this is assumed to be a blue-print instance, many settings missing: + _config = null; + _serializationView = null; + _serializerFactory = null; + _knownSerializers = null; + + // and others initialized to default empty state + _serializerCache = new SerializerCache(); + + _unknownTypeSerializer = src._unknownTypeSerializer; + _keySerializer = src._keySerializer; + _nullValueSerializer = src._nullValueSerializer; + _nullKeySerializer = src._nullKeySerializer; + + _stdNullValueSerializer = src._stdNullValueSerializer; + } + + /* + /********************************************************** + /* Methods for configuring default settings + /********************************************************** + */ + + /** + * Method that can be used to specify serializer that will be + * used to write JSON property names matching null keys for Java + * Maps (which will throw an exception if try write such property + * name) + */ + public void setDefaultKeySerializer(JsonSerializer ks) + { + if (ks == null) { + throw new IllegalArgumentException("Can not pass null JsonSerializer"); + } + _keySerializer = ks; + } + + /** + * Method that can be used to specify serializer that will be + * used to write JSON values matching Java null values + * instead of default one (which simply writes JSON null). + *

+ * Note that you can get finer control over serializer to use by overriding + * {@link #findNullValueSerializer}, which gets called once per each + * property. + */ + public void setNullValueSerializer(JsonSerializer nvs) + { + if (nvs == null) { + throw new IllegalArgumentException("Can not pass null JsonSerializer"); + } + _nullValueSerializer = nvs; + } + + /** + * Method that can be used to specify serializer to use for serializing + * all non-null JSON property names, unless more specific key serializer + * is found (i.e. if not custom key serializer has been registered for + * Java type). + *

+ * Note that key serializer registration are different from value serializer + * registrations. + */ + public void setNullKeySerializer(JsonSerializer nks) + { + if (nks == null) { + throw new IllegalArgumentException("Can not pass null JsonSerializer"); + } + _nullKeySerializer = nks; + } + + /* + /********************************************************** + /* DatabindContext implementation + /********************************************************** + */ + + /** + * Method for accessing configuration for the serialization processing. + */ + @Override + public final SerializationConfig getConfig() { return _config; } + + @Override + public final AnnotationIntrospector getAnnotationIntrospector() { + return _config.getAnnotationIntrospector(); + } + + @Override + public final TypeFactory getTypeFactory() { + return _config.getTypeFactory(); + } + + @Override + public final Class getActiveView() { return _serializationView; } + + /** + * @deprecated Since 2.2, use {@link #getActiveView} instead. + */ + @Deprecated + public final Class getSerializationView() { return _serializationView; } + + @Override + public final boolean canOverrideAccessModifiers() { + return _config.canOverrideAccessModifiers(); + } + + @Override + public final boolean isEnabled(MapperFeature feature) { + return _config.isEnabled(feature); + } + + @Override + public final JsonFormat.Value getDefaultPropertyFormat(Class baseType) { + return _config.getDefaultPropertyFormat(baseType); + } + + /** + * Method for accessing default Locale to use: convenience method for + *
+     *   getConfig().getLocale();
+     *
+ */ + @Override + public Locale getLocale() { + return _config.getLocale(); + } + + /** + * Method for accessing default TimeZone to use: convenience method for + *
+     *   getConfig().getTimeZone();
+     *
+ */ + @Override + public TimeZone getTimeZone() { + return _config.getTimeZone(); + } + + /* + /********************************************************** + /* Generic attributes (2.3+) + /********************************************************** + */ + + @Override + public Object getAttribute(Object key) { + return _attributes.getAttribute(key); + } + + @Override + public SerializerProvider setAttribute(Object key, Object value) + { + _attributes = _attributes.withPerCallAttribute(key, value); + return this; + } + + /* + /********************************************************** + /* Access to general configuration + /********************************************************** + */ + + /** + * Convenience method for checking whether specified serialization + * feature is enabled or not. + * Shortcut for: + *
+     *  getConfig().isEnabled(feature);
+     *
+ */ + public final boolean isEnabled(SerializationFeature feature) { + return _config.isEnabled(feature); + } + + /** + * "Bulk" access method for checking that all features specified by + * mask are enabled. + * + * @since 2.3 + */ + public final boolean hasSerializationFeatures(int featureMask) { + return _config.hasSerializationFeatures(featureMask); + } + + /** + * Convenience method for accessing provider to find serialization filters used, + * equivalent to calling: + *
+     *   getConfig().getFilterProvider();
+     *
+ */ + public final FilterProvider getFilterProvider() { + return _config.getFilterProvider(); + } + + /* + /********************************************************** + /* Access to Object Id aspects + /********************************************************** + */ + + /** + * Method called to find the Object Id for given POJO, if one + * has been generated. Will always return a non-null Object; + * contents vary depending on whether an Object Id already + * exists or not. + */ + public abstract WritableObjectId findObjectId(Object forPojo, + ObjectIdGenerator generatorType); + + /* + /********************************************************** + /* General serializer locating functionality + /********************************************************** + */ + + /** + * Method called to get hold of a serializer for a value of given type; + * or if no such serializer can be found, a default handler (which + * may do a best-effort generic serialization or just simply + * throw an exception when invoked). + *

+ * Note: this method is only called for non-null values; not for keys + * or null values. For these, check out other accessor methods. + *

+ * Note that serializers produced should NOT handle polymorphic serialization + * aspects; separate {@link TypeSerializer} is to be constructed by caller + * if and as necessary. + * + * @throws JsonMappingException if there are fatal problems with + * accessing suitable serializer; including that of not + * finding any serializer + */ + @SuppressWarnings("unchecked") + public JsonSerializer findValueSerializer(Class valueType, BeanProperty property) + throws JsonMappingException + { + // Fast lookup from local lookup thingy works? + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + // If not, maybe shared map already has it? + ser = _serializerCache.untypedValueSerializer(valueType); + if (ser == null) { + // ... possibly as fully typed? + ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); + if (ser == null) { + // If neither, must create + ser = _createAndCacheUntypedSerializer(valueType); + // Not found? Must use the unknown type serializer, which will report error later on + if (ser == null) { + ser = getUnknownTypeSerializer(valueType); + // Should this be added to lookups? + if (CACHE_UNKNOWN_MAPPINGS) { + _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); + } + return ser; + } + } + } + } + // at this point, resolution has occured, but not contextualization + return (JsonSerializer) handleSecondaryContextualization(ser, property); + } + + /** + * Similar to {@link #findValueSerializer(Class,BeanProperty)}, but takes + * full generics-aware type instead of raw class. + * This is necessary for accurate handling of external type information, + * to handle polymorphic types. + * + * @param property When creating secondary serializers, property for which + * serializer is needed: annotations of the property (or bean that contains it) + * may be checked to create contextual serializers. + */ + @SuppressWarnings("unchecked") + public JsonSerializer findValueSerializer(JavaType valueType, BeanProperty property) + throws JsonMappingException + { + // (see comments from above method) + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(valueType); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(valueType); + if (ser == null) { + ser = getUnknownTypeSerializer(valueType.getRawClass()); + if (CACHE_UNKNOWN_MAPPINGS) { + _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); + } + return ser; + } + } + } + return (JsonSerializer) handleSecondaryContextualization(ser, property); + } + + /** + * Method variant used when we do NOT want contextualization to happen; it will need + * to be handled at a later point, but caller wants to be able to do that + * as needed; sometimes to avoid infinite loops + * + * @since 2.5 + */ + public JsonSerializer findValueSerializer(Class valueType) throws JsonMappingException + { + // (see comments from above method) + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(valueType); + if (ser == null) { + ser = getUnknownTypeSerializer(valueType); + if (CACHE_UNKNOWN_MAPPINGS) { + _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); + } + } + } + } + } + return ser; + } + + /** + * Method variant used when we do NOT want contextualization to happen; it will need + * to be handled at a later point, but caller wants to be able to do that + * as needed; sometimes to avoid infinite loops + * + * @since 2.5 + */ + public JsonSerializer findValueSerializer(JavaType valueType) + throws JsonMappingException + { + // (see comments from above method) + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(valueType); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(valueType); + if (ser == null) { + ser = getUnknownTypeSerializer(valueType.getRawClass()); + if (CACHE_UNKNOWN_MAPPINGS) { + _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); + } + } + } + } + return ser; + } + + /** + * Similar to {@link #findValueSerializer(JavaType, BeanProperty)}, but used + * when finding "primary" property value serializer (one directly handling + * value of the property). Difference has to do with contextual resolution, + * and method(s) called: this method should only be called when caller is + * certain that this is the primary property value serializer. + * + * @param property Property that is being handled; will never be null, and its + * type has to match valueType parameter. + * + * @since 2.3 + */ + @SuppressWarnings("unchecked") + public JsonSerializer findPrimaryPropertySerializer(JavaType valueType, BeanProperty property) + throws JsonMappingException + { + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(valueType); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(valueType); + if (ser == null) { + ser = getUnknownTypeSerializer(valueType.getRawClass()); + // Should this be added to lookups? + if (CACHE_UNKNOWN_MAPPINGS) { + _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); + } + return ser; + } + } + } + return (JsonSerializer) handlePrimaryContextualization(ser, property); + } + + /** + * @since 2.3 + */ + @SuppressWarnings("unchecked") + public JsonSerializer findPrimaryPropertySerializer(Class valueType, + BeanProperty property) + throws JsonMappingException + { + JsonSerializer ser = _knownSerializers.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(valueType); + if (ser == null) { + ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType)); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(valueType); + if (ser == null) { + ser = getUnknownTypeSerializer(valueType); + if (CACHE_UNKNOWN_MAPPINGS) { + _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this); + } + return ser; + } + } + } + } + return (JsonSerializer) handlePrimaryContextualization(ser, property); + } + + /** + * Method called to locate regular serializer, matching type serializer, + * and if both found, wrap them in a serializer that calls both in correct + * sequence. This method is currently only used for root-level serializer + * handling to allow for simpler caching. A call can always be replaced + * by equivalent calls to access serializer and type serializer separately. + * + * @param valueType Type for purpose of locating a serializer; usually dynamic + * runtime type, but can also be static declared type, depending on configuration + * @param cache Whether resulting value serializer should be cached or not; this is just + * a hint + * @param property When creating secondary serializers, property for which + * serializer is needed: annotations of the property (or bean that contains it) + * may be checked to create contextual serializers. + */ + public JsonSerializer findTypedValueSerializer(Class valueType, + boolean cache, BeanProperty property) + throws JsonMappingException + { + // Two-phase lookups; local non-shared cache, then shared: + JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); + if (ser != null) { + return ser; + } + // If not, maybe shared map already has it? + ser = _serializerCache.typedValueSerializer(valueType); + if (ser != null) { + return ser; + } + + // Well, let's just compose from pieces: + ser = findValueSerializer(valueType, property); + TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, + _config.constructType(valueType)); + if (typeSer != null) { + typeSer = typeSer.forProperty(property); + ser = new TypeWrappedSerializer(typeSer, ser); + } + if (cache) { + _serializerCache.addTypedSerializer(valueType, ser); + } + return ser; + } + + /** + * Method called to locate regular serializer, matching type serializer, + * and if both found, wrap them in a serializer that calls both in correct + * sequence. This method is currently only used for root-level serializer + * handling to allow for simpler caching. A call can always be replaced + * by equivalent calls to access serializer and type serializer separately. + * + * @param valueType Declared type of value being serialized (which may not + * be actual runtime type); used for finding both value serializer and + * type serializer to use for adding polymorphic type (if any) + * @param cache Whether resulting value serializer should be cached or not; this is just + * a hint + * @param property When creating secondary serializers, property for which + * serializer is needed: annotations of the property (or bean that contains it) + * may be checked to create contextual serializers. + */ + public JsonSerializer findTypedValueSerializer(JavaType valueType, boolean cache, + BeanProperty property) + throws JsonMappingException + { + // Two-phase lookups; local non-shared cache, then shared: + JsonSerializer ser = _knownSerializers.typedValueSerializer(valueType); + if (ser != null) { + return ser; + } + // If not, maybe shared map already has it? + ser = _serializerCache.typedValueSerializer(valueType); + if (ser != null) { + return ser; + } + + // Well, let's just compose from pieces: + ser = findValueSerializer(valueType, property); + TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, valueType); + if (typeSer != null) { + typeSer = typeSer.forProperty(property); + ser = new TypeWrappedSerializer(typeSer, ser); + } + if (cache) { + _serializerCache.addTypedSerializer(valueType, ser); + } + return ser; + } + + /** + * Method called to get the {@link TypeSerializer} to use for including Type Id necessary + * for serializing for the given Java class. + * Useful for schema generators. + * + * @since 2.6 + */ + public TypeSerializer findTypeSerializer(JavaType javaType) throws JsonMappingException { + return _serializerFactory.createTypeSerializer(_config, javaType); + } + + /** + * Method called to get the serializer to use for serializing + * non-null Map keys. Separation from regular + * {@link #findValueSerializer} method is because actual write + * method must be different (@link JsonGenerator#writeFieldName}; + * but also since behavior for some key types may differ. + *

+ * Note that the serializer itself can be called with instances + * of any Java object, but not nulls. + */ + public JsonSerializer findKeySerializer(JavaType keyType, BeanProperty property) + throws JsonMappingException + { + JsonSerializer ser = _serializerFactory.createKeySerializer(_config, keyType, _keySerializer); + // 25-Feb-2011, tatu: As per [JACKSON-519], need to ensure contextuality works here, too + return _handleContextualResolvable(ser, property); + } + + /** + * @since 2.7 + */ + public JsonSerializer findKeySerializer(Class rawKeyType, BeanProperty property) + throws JsonMappingException + { + return findKeySerializer(_config.constructType(rawKeyType), property); + } + + /* + /******************************************************** + /* Accessors for specialized serializers + /******************************************************** + */ + + /** + * @since 2.0 + */ + public JsonSerializer getDefaultNullKeySerializer() { + return _nullKeySerializer; + } + + /** + * @since 2.0 + */ + public JsonSerializer getDefaultNullValueSerializer() { + return _nullValueSerializer; + } + + /** + * Method called to get the serializer to use for serializing + * Map keys that are nulls: this is needed since JSON does not allow + * any non-String value as key, including null. + *

+ * Typically, returned serializer + * will either throw an exception, or use an empty String; but + * other behaviors are possible. + */ + /** + * Method called to find a serializer to use for null values for given + * declared type. Note that type is completely based on declared type, + * since nulls in Java have no type and thus runtime type can not be + * determined. + * + * @since 2.0 + */ + public JsonSerializer findNullKeySerializer(JavaType serializationType, + BeanProperty property) + throws JsonMappingException + { + return _nullKeySerializer; + } + + /** + * Method called to get the serializer to use for serializing null + * values for specified property. + *

+ * Default implementation simply calls {@link #getDefaultNullValueSerializer()}; + * can be overridden to add custom null serialization for properties + * of certain type or name. This gives method full granularity to basically + * override null handling for any specific property or class of properties. + * + * @since 2.0 + */ + public JsonSerializer findNullValueSerializer(BeanProperty property) + throws JsonMappingException { + return _nullValueSerializer; + } + + /** + * Method called to get the serializer to use if provider + * can not determine an actual type-specific serializer + * to use; typically when none of {@link SerializerFactory} + * instances are able to construct a serializer. + *

+ * Typically, returned serializer will throw an exception, + * although alternatively {@link com.fasterxml.jackson.databind.ser.std.ToStringSerializer} + * could be returned as well. + * + * @param unknownType Type for which no serializer is found + */ + public JsonSerializer getUnknownTypeSerializer(Class unknownType) { + // 23-Apr-2015, tatu: Only return shared instance if nominal type is Object.class + if (unknownType == Object.class) { + return _unknownTypeSerializer; + } + // otherwise construct explicit instance with property handled type + return new UnknownSerializer(unknownType); + } + + /** + * Helper method called to see if given serializer is considered to be + * something returned by {@link #getUnknownTypeSerializer}, that is, something + * for which no regular serializer was found or constructed. + * + * @since 2.5 + */ + public boolean isUnknownTypeSerializer(JsonSerializer ser) { + if ((ser == _unknownTypeSerializer) || (ser == null)) { + return true; + } + // 23-Apr-2015, tatu: "empty" serializer is trickier; needs to consider + // error handling + if (isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { + if (ser.getClass() == UnknownSerializer.class) { + return true; + } + } + return false; + } + + /* + /********************************************************** + /* Methods for creating instances based on annotations + /********************************************************** + */ + + /** + * Method that can be called to construct and configure serializer instance, + * either given a {@link Class} to instantiate (with default constructor), + * or an uninitialized serializer instance. + * Either way, serialize will be properly resolved + * (via {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer}) and/or contextualized + * (via {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}) as necessary. + * + * @param annotated Annotated entity that contained definition + * @param serDef Serializer definition: either an instance or class + */ + public abstract JsonSerializer serializerInstance(Annotated annotated, + Object serDef) + throws JsonMappingException; + + /* + /********************************************************** + /* Support for contextualization + /********************************************************** + */ + + /** + * Method called for primary property serializers (ones + * directly created to serialize values of a POJO property), + * to handle details of resolving + * {@link ContextualSerializer} with given property context. + * + * @param property Property for which the given primary serializer is used; never null. + * + * @since 2.3 + */ + public JsonSerializer handlePrimaryContextualization(JsonSerializer ser, + BeanProperty property) + throws JsonMappingException + { + if (ser != null) { + if (ser instanceof ContextualSerializer) { + ser = ((ContextualSerializer) ser).createContextual(this, property); + } + } + return ser; + } + + /** + * Method called for secondary property serializers (ones + * NOT directly created to serialize values of a POJO property + * but instead created as a dependant serializer -- such as value serializers + * for structured types, or serializers for root values) + * to handle details of resolving + * {@link ContextualDeserializer} with given property context. + * Given that these serializers are not directly related to given property + * (or, in case of root value property, to any property), annotations + * accessible may or may not be relevant. + * + * @param property Property for which serializer is used, if any; null + * when deserializing root values + * + * @since 2.3 + */ + public JsonSerializer handleSecondaryContextualization(JsonSerializer ser, + BeanProperty property) + throws JsonMappingException + { + if (ser != null) { + if (ser instanceof ContextualSerializer) { + ser = ((ContextualSerializer) ser).createContextual(this, property); + } + } + return ser; + } + + /* + /******************************************************** + /* Convenience methods for serializing using default methods + /******************************************************** + */ + + /** + * Convenience method that will serialize given value (which can be + * null) using standard serializer locating functionality. It can + * be called for all values including field and Map values, but usually + * field values are best handled calling + * {@link #defaultSerializeField} instead. + */ + public final void defaultSerializeValue(Object value, JsonGenerator gen) throws IOException + { + if (value == null) { + if (_stdNullValueSerializer) { // minor perf optimization + gen.writeNull(); + } else { + _nullValueSerializer.serialize(null, gen, this); + } + } else { + Class cls = value.getClass(); + findTypedValueSerializer(cls, true, null).serialize(value, gen, this); + } + } + + /** + * Convenience method that will serialize given field with specified + * value. Value may be null. Serializer is done using the usual + * null) using standard serializer locating functionality. + */ + public final void defaultSerializeField(String fieldName, Object value, JsonGenerator gen) + throws IOException + { + gen.writeFieldName(fieldName); + if (value == null) { + /* Note: can't easily check for suppression at this point + * any more; caller must check it. + */ + if (_stdNullValueSerializer) { // minor perf optimization + gen.writeNull(); + } else { + _nullValueSerializer.serialize(null, gen, this); + } + } else { + Class cls = value.getClass(); + findTypedValueSerializer(cls, true, null).serialize(value, gen, this); + } + } + + /** + * Method that will handle serialization of Date(-like) values, using + * {@link SerializationConfig} settings to determine expected serialization + * behavior. + * Note: date here means "full" date, that is, date AND time, as per + * Java convention (and not date-only values like in SQL) + */ + public final void defaultSerializeDateValue(long timestamp, JsonGenerator gen) + throws IOException + { + if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) { + gen.writeNumber(timestamp); + } else { + gen.writeString(_dateFormat().format(new Date(timestamp))); + } + } + + /** + * Method that will handle serialization of Date(-like) values, using + * {@link SerializationConfig} settings to determine expected serialization + * behavior. + * Note: date here means "full" date, that is, date AND time, as per + * Java convention (and not date-only values like in SQL) + */ + public final void defaultSerializeDateValue(Date date, JsonGenerator gen) throws IOException + { + if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) { + gen.writeNumber(date.getTime()); + } else { + gen.writeString(_dateFormat().format(date)); + } + } + + /** + * Method that will handle serialization of Dates used as {@link java.util.Map} keys, + * based on {@link SerializationFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS} + * value (and if using textual representation, configured date format) + */ + public void defaultSerializeDateKey(long timestamp, JsonGenerator gen) throws IOException + { + if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) { + gen.writeFieldName(String.valueOf(timestamp)); + } else { + gen.writeFieldName(_dateFormat().format(new Date(timestamp))); + } + } + + /** + * Method that will handle serialization of Dates used as {@link java.util.Map} keys, + * based on {@link SerializationFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS} + * value (and if using textual representation, configured date format) + */ + public void defaultSerializeDateKey(Date date, JsonGenerator gen) throws IOException + { + if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) { + gen.writeFieldName(String.valueOf(date.getTime())); + } else { + gen.writeFieldName(_dateFormat().format(date)); + } + } + + public final void defaultSerializeNull(JsonGenerator jgen) throws IOException + { + if (_stdNullValueSerializer) { // minor perf optimization + jgen.writeNull(); + } else { + _nullValueSerializer.serialize(null, jgen, this); + } + } + + /* + /******************************************************** + /* Error reporting + /******************************************************** + */ + + /** + * @since 2.6 + */ + public JsonMappingException mappingException(String message, Object... args) { + if (args != null && args.length > 0) { + message = String.format(message, args); + } + return JsonMappingException.from(this, message); + } + + /* + /******************************************************** + /* Helper methods + /******************************************************** + */ + + protected void _reportIncompatibleRootType(Object value, JavaType rootType) throws IOException + { + // One special case: allow primitive/wrapper type coercion + if (rootType.isPrimitive()) { + Class wrapperType = ClassUtil.wrapperType(rootType.getRawClass()); + // If it's just difference between wrapper, primitive, let it slide + if (wrapperType.isAssignableFrom(value.getClass())) { + return; + } + } + throw JsonMappingException.from(this, + "Incompatible types: declared root type ("+rootType+") vs " + +value.getClass().getName()); + } + + /** + * Method that will try to find a serializer, either from cache + * or by constructing one; but will not return an "unknown" serializer + * if this can not be done but rather returns null. + * + * @return Serializer if one can be found, null if not. + */ + protected JsonSerializer _findExplicitUntypedSerializer(Class runtimeType) + throws JsonMappingException + { + // Fast lookup from local lookup thingy works? + JsonSerializer ser = _knownSerializers.untypedValueSerializer(runtimeType); + if (ser == null) { + // If not, maybe shared map already has it? + ser = _serializerCache.untypedValueSerializer(runtimeType); + if (ser == null) { + ser = _createAndCacheUntypedSerializer(runtimeType); + } + } + /* 18-Sep-2014, tatu: This is unfortunate patch over related change + * that pushes creation of "unknown type" serializer deeper down + * in BeanSerializerFactory; as a result, we need to "undo" creation + * here. + */ + if (isUnknownTypeSerializer(ser)) { + return null; + } + return ser; + } + + /* + /********************************************************** + /* Low-level methods for actually constructing and initializing + /* serializers + /********************************************************** + */ + + /** + * Method that will try to construct a value serializer; and if + * one is successfully created, cache it for reuse. + */ + protected JsonSerializer _createAndCacheUntypedSerializer(Class rawType) + throws JsonMappingException + { + JavaType fullType = _config.constructType(rawType); + JsonSerializer ser; + try { + ser = _createUntypedSerializer(fullType); + } catch (IllegalArgumentException iae) { + /* We better only expose checked exceptions, since those + * are what caller is expected to handle + */ + throw JsonMappingException.from(this, iae.getMessage(), iae); + } + + if (ser != null) { + // 21-Dec-2015, tatu: Best to cache for both raw and full-type key + _serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this); + } + return ser; + } + + protected JsonSerializer _createAndCacheUntypedSerializer(JavaType type) + throws JsonMappingException + { + JsonSerializer ser; + try { + ser = _createUntypedSerializer(type); + } catch (IllegalArgumentException iae) { + /* We better only expose checked exceptions, since those + * are what caller is expected to handle + */ + throw JsonMappingException.from(this, iae.getMessage(), iae); + } + + if (ser != null) { + // 21-Dec-2015, tatu: Should we also cache using raw key? + _serializerCache.addAndResolveNonTypedSerializer(type, ser, this); + } + return ser; + } + + /** + * @since 2.1 + */ + protected JsonSerializer _createUntypedSerializer(JavaType type) + throws JsonMappingException + { + /* 27-Mar-2015, tatu: Wish I knew exactly why/what, but [databind#738] + * can be prevented by synchronizing on cache (not on 'this', however, + * since there's one instance per serialization). + * Perhaps not-yet-resolved instance might be exposed too early to callers. + */ + synchronized (_serializerCache) { + // 17-Feb-2013, tatu: Used to call deprecated method (that passed property) + return (JsonSerializer)_serializerFactory.createSerializer(this, type); + } + } + + /** + * Helper method called to resolve and contextualize given + * serializer, if and as necessary. + */ + @SuppressWarnings("unchecked") + protected JsonSerializer _handleContextualResolvable(JsonSerializer ser, + BeanProperty property) + throws JsonMappingException + { + if (ser instanceof ResolvableSerializer) { + ((ResolvableSerializer) ser).resolve(this); + } + return (JsonSerializer) handleSecondaryContextualization(ser, property); + } + + @SuppressWarnings("unchecked") + protected JsonSerializer _handleResolvable(JsonSerializer ser) + throws JsonMappingException + { + if (ser instanceof ResolvableSerializer) { + ((ResolvableSerializer) ser).resolve(this); + } + return (JsonSerializer) ser; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected final DateFormat _dateFormat() + { + if (_dateFormat != null) { + return _dateFormat; + } + /* At this point, all timezone configuration should have occured, with respect + * to default dateformat configuration. But we still better clone + * an instance as formatters are stateful, not thread-safe. + */ + DateFormat df = _config.getDateFormat(); + _dateFormat = df = (DateFormat) df.clone(); + // [databind#939]: 26-Sep-2015, tatu: With 2.6, formatter has been (pre)configured + // with TimeZone, so we should NOT try overriding it unlike with earlier versions + /* + TimeZone tz = getTimeZone(); + if (tz != df.getTimeZone()) { + df.setTimeZone(tz); + } + */ + return df; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JacksonStdImpl.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JacksonStdImpl.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JacksonStdImpl.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,23 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.annotation.JacksonAnnotation; + +/** + * Marker interface used to indicate implementation classes + * (serializers, deserializers etc) that are standard ones Jackson + * uses; not custom ones that application has added. It can be + * added in cases where certain optimizations can be made if + * default instances are uses; for example when handling conversions + * of "natural" JSON types like Strings, booleans and numbers. + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotation +public @interface JacksonStdImpl { + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonAppend.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonAppend.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonAppend.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,128 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; + +/** + * Annotation that may be used to add "virtual" properties to be written + * after regular properties (although ordering may be changed using + * both standard @JsonPropertyOrder annotation, and + * properties of this annotation). + * + * @since 2.5 + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonAppend +{ + /** + * Set of attribute-backed properties to include when serializing + * a POJO. + */ + public Attr[] attrs() default { }; + + /** + * Set of general virtual properties to include when serializing a POJO. + */ + public Prop[] props() default { }; + + /** + * Indicator used to determine whether properties defined are to be + * appended before (false) or prepended before (true) regular properties. + * Affects all kinds of properties defined using this annotation. + */ + public boolean prepend() default false; + + /** + * Definition of a single attribute-backed property. + * Attribute-backed properties will be appended after regular properties + * in specified order, although their placement may be further changed + * by the usual property-ordering functionality (alphabetic sorting; + * explicit ordering) + */ + public @interface Attr + { + /** + * Name of attribute of which value to serialize. Is also used as the + * name of external property to write, unless overridden by + * assigning a value for {@link #propName()}. + */ + public String value(); + + /** + * Name to use for serializing value of the attribute; if not defined, + * {@link #value} will be used instead. + */ + public String propName() default ""; + + /** + * Optional namespace to use; only relevant for data formats that use + * namespaces (like XML). + */ + public String propNamespace() default ""; + + /** + * When to include attribute-property. Default value indicates that + * property should only be written if specified attribute has a non-null + * value. + */ + public JsonInclude.Include include() default JsonInclude.Include.NON_NULL; + + /** + * Metadata about property, similar to + * {@link com.fasterxml.jackson.annotation.JsonProperty#required()}. + */ + public boolean required() default false; + } + + /** + * Definition of a single general virtual property. + */ + public @interface Prop + { + /** + * Actual implementation class (a subtype of {@link VirtualBeanPropertyWriter}) + * of the property to instantiate (using the no-argument default constructor). + */ + public Class value(); + + /** + * Name of the property to possibly use for serializing (although implementation + * may choose to not use this information). + */ + public String name() default ""; + + /** + * Optional namespace to use along with {@link #name}; + * only relevant for data formats that use namespaces (like XML). + */ + public String namespace() default ""; + + /** + * When to include value of the property. Default value indicates that + * property should only be written if specified attribute has a non-null + * value. As with other properties, actual property implementation may or may + * not choose to use this inclusion information. + */ + public JsonInclude.Include include() default JsonInclude.Include.NON_NULL; + + /** + * Metadata about property, similar to + * {@link com.fasterxml.jackson.annotation.JsonProperty#required()}. + */ + public boolean required() default false; + + /** + * Nominal type of the property. Passed as type information for related + * virtual objects, and may (or may not be) used by implementation + * for choosing serializer to use. + */ + public Class type() default Object.class; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,138 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Annotation use for configuring deserialization aspects, by attaching + * to "setter" methods or fields, or to value classes. + * When annotating value classes, configuration is used for instances + * of the value class but can be overridden by more specific annotations + * (ones that attach to methods or fields). + *

+ * An example annotation would be: + *

+ *  @JsonDeserialize(using=MySerializer.class,
+ *    as=MyHashMap.class,
+ *    keyAs=MyHashKey.class,
+ *    contentAs=MyHashValue.class
+ *  )
+ *
+ *

+ */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonDeserialize +{ + // // // Annotations for explicitly specifying deserialize/builder + + /** + * Deserializer class to use for deserializing associated value. + * Depending on what is annotated, + * value is either an instance of annotated class (used globablly + * anywhere where class deserializer is needed); or only used for + * deserializing property access via a setter method. + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class using() + default JsonDeserializer.None.class; + + /** + * Deserializer class to use for deserializing contents (elements + * of a Collection/array, values of Maps) of annotated property. + * Can only be used on instances (methods, fields, constructors), + * and not value classes themselves. + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class contentUsing() + default JsonDeserializer.None.class; + + /** + * Deserializer class to use for deserializing Map keys + * of annotated property. + * Can only be used on instances (methods, fields, constructors), + * and not value classes themselves. + */ + public Class keyUsing() + default KeyDeserializer.None.class; + + /** + * Annotation for specifying if an external Builder class is to + * be used for building up deserialized instances of annotated + * class. If so, an instance of referenced class is first constructed + * (possibly using a Creator method; or if none defined, using default + * constructor), and its "with-methods" are used for populating fields; + * and finally "build-method" is invoked to complete deserialization. + */ + public Class builder() default Void.class; + + // // // Annotations for specifying intermediate Converters (2.2+) + + /** + * Which helper object (if any) is to be used to convert from Jackson-bound + * intermediate type (source type of converter) into actual property type + * (which must be same as result type of converter). This is often used + * for two-step deserialization; Jackson binds data into suitable intermediate + * type (like Tree representation), and converter then builds actual property + * type. + * + * @since 2.2 + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class converter() default Converter.None.class; + + /** + * Similar to {@link #converter}, but used for values of structures types + * (List, arrays, Maps). + * + * @since 2.2 + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class contentConverter() default Converter.None.class; + + + // // // Annotations for explicitly specifying deserialization type + // // // (which is used for choosing deserializer, if not explicitly + // // // specified + + /** + * Concrete type to deserialize values as, instead of type otherwise + * declared. Must be a subtype of declared type; otherwise an + * exception may be thrown by deserializer. + *

+ * Bogus type {@link Void} can be used to indicate that declared + * type is used as is (i.e. this annotation property has no setting); + * this since annotation properties are not allowed to have null value. + *

+ * Note: if {@link #using} is also used it has precedence + * (since it directly specified + * deserializer, whereas this would only be used to locate the + * deserializer) + * and value of this annotation property is ignored. + */ + public Class as() default Void.class; + + /** + * Concrete type to deserialize keys of {@link java.util.Map} as, + * instead of type otherwise declared. + * Must be a subtype of declared type; otherwise an exception may be + * thrown by deserializer. + */ + public Class keyAs() default Void.class; + + /** + * Concrete type to deserialize content (elements + * of a Collection/array, values of Maps) values as, + * instead of type otherwise declared. + * Must be a subtype of declared type; otherwise an exception may be + * thrown by deserializer. + */ + public Class contentAs() default Void.class; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonNaming.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonNaming.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonNaming.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.*; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +/** + * Annotation that can be used to indicate a {@link PropertyNamingStrategy} + * to use for annotated class. Overrides the global (default) strategy. + * Note that if the {@link #value} property is omitted, its default value + * means "use default naming" (that is, no alternate naming method is used). + * This can be used as an override with mix-ins. + * + * @since 2.1 + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonNaming +{ + /** + * @return Type of {@link PropertyNamingStrategy} to use, if any; default value of + * PropertyNamingStrategy.class means "no strategy specified" + * (and may also be used for overriding to remove otherwise applicable + * naming strategy) + */ + public Class value() default PropertyNamingStrategy.class; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,74 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.*; + +/** + * Annotation used to configure details of a Builder class: + * instances of which are used as Builders for deserialized + * POJO values, instead of POJOs being instantiated using + * constructors or factory methods. + * Note that this annotation is NOT used to define what is + * the Builder class for a POJO: rather, this is determined + * by {@link JsonDeserialize#builder} property of {@link JsonDeserialize}. + *

+ * Annotation is typically used if the naming convention + * of a Builder class is different from defaults: + *

    + *
+ * + * @since 2.0 + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonPOJOBuilder +{ + /** + * Property to use for re-defining which zero-argument method + * is considered the actual "build-method": method called after + * all data has been bound, and the actual instance needs to + * be instantiated. + *

+ * Default value is "build". + */ + public String buildMethodName() default "build"; + + /** + * Property used for (re)defining name prefix to use for + * auto-detecting "with-methods": methods that are similar to + * "set-methods" (in that they take an argument), but that + * may also return the new builder instance to use + * (which may be 'this', or a new modified builder instance). + * Note that in addition to this prefix, it is also possible + * to use {@link com.fasterxml.jackson.annotation.JsonProperty} + * annotation to indicate "with-methods" (as well as + * {@link com.fasterxml.jackson.annotation.JsonSetter}). + *

+ * Default value is "with", so that method named "withValue()" + * would be used for binding JSON property "value" (using type + * indicated by the argument; or one defined with annotations. + */ + public String withPrefix() default "with"; + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Simple value container for containing values read from + * {@link JsonPOJOBuilder} annotation instance. + */ + public class Value + { + public final String buildMethodName; + public final String withPrefix; + + public Value(JsonPOJOBuilder ann) + { + buildMethodName = ann.buildMethodName(); + withPrefix = ann.withPrefix(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonSerialize.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonSerialize.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonSerialize.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,270 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Annotation used for configuring serialization aspects, by attaching + * to "getter" methods or fields, or to value classes. + * When annotating value classes, configuration is used for instances + * of the value class but can be overridden by more specific annotations + * (ones that attach to methods or fields). + *

+ * An example annotation would be: + *

+ *  @JsonSerialize(using=MySerializer.class,
+ *    as=MySubClass.class,
+ *    typing=JsonSerialize.Typing.STATIC
+ *  )
+ *
+ * (which would be redundant, since some properties block others: + * specifically, 'using' has precedence over 'as', which has precedence + * over 'typing' setting) + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonSerialize +{ + // // // Annotations for explicitly specifying deserializer + + /** + * Serializer class to use for + * serializing associated value. Depending on what is annotated, + * value is either an instance of annotated class (used globablly + * anywhere where class serializer is needed); or only used for + * serializing property access via a getter method. + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class using() default JsonSerializer.None.class; + + /** + * Serializer class to use for serializing contents (elements + * of a Collection/array, values of Maps) of annotated property. + * Can only be used on properties (methods, fields, constructors), + * and not value classes themselves (as they are typically generic) + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class contentUsing() + default JsonSerializer.None.class; + + /** + * Serializer class to use for serializing Map keys + * of annotated property. + * Can only be used on properties (methods, fields, constructors), + * and not value classes themselves. + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class keyUsing() + default JsonSerializer.None.class; + + /** + * Serializer class to use for serializing nulls for properties that + * are annotated, instead of the + * default null serializer. + * Note that using this property when annotation types (classes) has + * no effect currently (it is possible this could be improved in future). + * + * @since 2.3 + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class nullsUsing() + default JsonSerializer.None.class; + + // // // Annotations for type handling, explicit declaration + // // // (type used for choosing deserializer, if not explicitly + // // // specified) + + /** + * Supertype (of declared type, which itself is supertype of runtime type) + * to use as type when locating serializer to use. + *

+ * Bogus type {@link Void} can be used to indicate that declared + * type is used as is (i.e. this annotation property has no setting); + * this since annotation properties are not allowed to have null value. + *

+ * Note: if {@link #using} is also used it has precedence + * (since it directly specifies + * serializer, whereas this would only be used to locate the + * serializer) + * and value of this annotation property is ignored. + */ + public Class as() default Void.class; + + /** + * Concrete type to serialize keys of {@link java.util.Map} as, + * instead of type otherwise declared. + * Must be a supertype of declared type; otherwise an exception may be + * thrown by serializer. + */ + public Class keyAs() default Void.class; + + /** + * Concrete type to serialize content value (elements + * of a Collection/array, values of Maps) as, + * instead of type otherwise declared. + * Must be a supertype of declared type; otherwise an exception may be + * thrown by serializer. + */ + public Class contentAs() default Void.class; + + /** + * Whether type detection used is dynamic or static: that is, + * whether actual runtime type is used (dynamic), or just the + * declared type (static). + *

+ * Note that Jackson 2.3 changed default to DEFAULT_TYPING, + * which is roughly same as saying "whatever". + * This is important as it allows avoiding accidental overrides + * at property level. + */ + public Typing typing() default Typing.DEFAULT_TYPING; + + // // // Annotations for specifying intermediate Converters (2.2+) + + /** + * Which helper object is to be used to convert type into something + * that Jackson knows how to serialize; either because base type + * can not be serialized easily, or just to alter serialization. + * + * @since 2.2 + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class converter() default Converter.None.class; + + /** + * Similar to {@link #converter}, but used for values of structures types + * (List, arrays, Maps). + * Note that this property does NOT have effect when used as Class annotation; + * it can only be used as property annotation: this because association between + * container and value types is loose and as such converters seldom make sense + * for such usage. + * + * @since 2.2 + */ + @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties + public Class contentConverter() default Converter.None.class; + + // // // Annotation(s) for inclusion criteria + + /** + * Which properties of annotated Bean are + * to be included in serialization (has no effect on other types + * like enums, primitives or collections). + * Choices are "all", "properties that have value other than null" + * and "properties that have non-default value" (i.e. default value + * being property setting for a Bean constructed with default no-arg + * constructor, often null). + *

+ * This property has been replaced by special-purpose {@link com.fasterxml.jackson.annotation.JsonInclude} + * annotation, introduced in Jackson 2.0. + *

+ * Note that Jackson 2.3 changed default to DEFAULT_INCLUSION, + * which is roughly same as saying "whatever". This is important because + * it allows hierarchic default values to be used. + * + * @deprecated As of Jackson 2.0, this annotation has been replaced + * by {@link com.fasterxml.jackson.annotation.JsonInclude} + */ + @Deprecated + public Inclusion include() default Inclusion.DEFAULT_INCLUSION; + + /* + /********************************************************** + /* Value enumerations needed + /********************************************************** + */ + + /** + * Enumeration used with {@link JsonSerialize#include} property + * to define which properties + * of Java Beans are to be included in serialization + */ + @Deprecated // since 2.0, marked deprecated in 2.6 + public enum Inclusion + { + /** + * Value that indicates that properties are to be always included, + * independent of value + */ + ALWAYS, + + /** + * Value that indicates that only properties with non-null + * values are to be included. + */ + NON_NULL, + + /** + * Value that indicates that only properties that have values + * that differ from default settings (meaning values they have + * when Bean is constructed with its no-arguments constructor) + * are to be included. Value is generally not useful with + * {@link java.util.Map}s, since they have no default values; + * and if used, works same as {@link #ALWAYS}. + */ + NON_DEFAULT, + + /** + * Value that indicates that only properties that have values + * that values that are null or what is considered empty are + * not to be included. + * Emptiness is defined for following type: + *

    + *
  • For {@link java.util.Collection}s and {@link java.util.Map}s, + * method isEmpty() is called; + *
  • + *
  • For Java arrays, empty arrays are ones with length of 0 + *
  • + *
  • For Java {@link java.lang.String}s, length() is called, + * and return value of 0 indicates empty String + *
  • + *
+ * For other types, non-null values are to be included. + */ + NON_EMPTY, + + /** + * Pseudo-value that is used to indicate + * "use whatever is default used at higher level". + * + * @since 2.3 + */ + DEFAULT_INCLUSION + ; + } + + /** + * Enumeration used with {@link JsonSerialize#typing} property + * to define whether type detection is based on dynamic runtime + * type (DYNAMIC) or declared type (STATIC). + */ + public enum Typing + { + /** + * Value that indicates that the actual dynamic runtime type is to + * be used. + */ + DYNAMIC, + + /** + * Value that indicates that the static declared type is to + * be used. + */ + STATIC, + + /** + * Pseudo-value that is used to indicate + * "use whatever is default used at higher level". + * + * @since 2.3 + */ + DEFAULT_TYPING + ; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,36 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +import com.fasterxml.jackson.annotation.JacksonAnnotation; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Annotation that can be used to plug a custom type identifier handler + * ({@link TypeIdResolver}) + * to be used by + * {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer}s + * and {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}s + * for converting between java types and type id included in JSON content. + * In simplest cases this can be a simple class with static mapping between + * type names and matching classes. + *

+ * NOTE: since 2.4, applicable to properties as well (should have been long time + * ago, but problem only found then) + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotation +public @interface JsonTypeIdResolver +{ + /** + * Defines implementation class of {@link TypeIdResolver} to use for + * converting between external type id (type name) and actual + * type of object. + */ + public Class value(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.*; + +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; + +/** + * Annotation that can be used to explicitly define custom resolver + * used for handling serialization and deserialization of type information, + * needed for handling of polymorphic types (or sometimes just for linking + * abstract types to concrete types) + *

+ * NOTE: since 2.4, applicable to properties as well (should have been long time + * ago, but problem only found then) + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonTypeResolver +{ + /** + * Defines implementation class of {@link TypeResolverBuilder} which is used to construct + * actual {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer} and {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer} + * instances that handle reading and writing addition type information needed to support polymorphic + * deserialization. + */ + public Class> value(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonValueInstantiator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonValueInstantiator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/JsonValueInstantiator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,23 @@ +package com.fasterxml.jackson.databind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.databind.deser.ValueInstantiator; + +/** + * Annotation that can be used to indicate a {@link ValueInstantiator} to use + * for creating instances of specified type. + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@com.fasterxml.jackson.annotation.JacksonAnnotation +public @interface JsonValueInstantiator +{ + /** + * @return {@link ValueInstantiator} to use for annotated type + */ + public Class value(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/NoClass.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/NoClass.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/NoClass.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,18 @@ +package com.fasterxml.jackson.databind.annotation; + +/** + * Marker class used with annotations to indicate "no class". This is + * a silly but necessary work-around -- annotations can not take nulls + * as either default or explicit values. Hence for class values we must + * explicitly use a bogus placeholder to denote equivalent of + * "no class" (for which 'null' is usually the natural choice). + *

+ * Note that since 2.4, most (but not all! + * {@link com.fasterxml.jackson.annotation.JsonTypeInfo#defaultImpl} is + * a notable exception}) usage should start using + * {@link java.lang.Void} instead as the "not defined" marker. + */ +public final class NoClass +{ + private NoClass() { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/annotation/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,7 @@ +/** + * Annotations that directly depend on classes in databinding bundle + * (not just Jackson core) and can not be included + * in Jackson core annotations package (because it can not have any + * external dependencies). + */ +package com.fasterxml.jackson.databind.annotation; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/BaseSettings.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/BaseSettings.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/BaseSettings.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,384 @@ +package com.fasterxml.jackson.databind.cfg; + +import java.text.DateFormat; +import java.util.Locale; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.Base64Variant; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.StdDateFormat; + +/** + * Immutable container class used to store simple configuration + * settings. Since instances are fully immutable, instances can + * be freely shared and used without synchronization. + */ +public final class BaseSettings + implements java.io.Serializable +{ + // for 2.6 + private static final long serialVersionUID = 1L; + + /** + * We will use a default TimeZone as the baseline. + */ + private static final TimeZone DEFAULT_TIMEZONE = + // TimeZone.getDefault() + /* [databind#915] 05-Nov-2015, tatu: Changed to UTC, from earlier + * baseline of GMT (up to 2.6) + */ + TimeZone.getTimeZone("UTC"); + + /* + /********************************************************** + /* Configuration settings; introspection, related + /********************************************************** + */ + + /** + * Introspector used to figure out Bean properties needed for bean serialization + * and deserialization. Overridable so that it is possible to change low-level + * details of introspection, like adding new annotation types. + */ + protected final ClassIntrospector _classIntrospector; + + /** + * Introspector used for accessing annotation value based configuration. + */ + protected final AnnotationIntrospector _annotationIntrospector; + + /** + * Object used for determining whether specific property elements + * (method, constructors, fields) can be auto-detected based on + * their visibility (access modifiers). Can be changed to allow + * different minimum visibility levels for auto-detection. Note + * that this is the global handler; individual types (classes) + * can further override active checker used (using + * {@link JsonAutoDetect} annotation) + */ + protected final VisibilityChecker _visibilityChecker; + + /** + * Custom property naming strategy in use, if any. + */ + protected final PropertyNamingStrategy _propertyNamingStrategy; + + /** + * Specific factory used for creating {@link JavaType} instances; + * needed to allow modules to add more custom type handling + * (mostly to support types of non-Java JVM languages) + */ + protected final TypeFactory _typeFactory; + + /* + /********************************************************** + /* Configuration settings; type resolution + /********************************************************** + */ + + /** + * Type information handler used for "untyped" values (ones declared + * to have type Object.class) + */ + protected final TypeResolverBuilder _typeResolverBuilder; + + /* + /********************************************************** + /* Configuration settings; other + /********************************************************** + */ + + /** + * Custom date format to use for de-serialization. If specified, will be + * used instead of {@link com.fasterxml.jackson.databind.util.StdDateFormat}. + *

+ * Note that the configured format object will be cloned once per + * deserialization process (first time it is needed) + */ + protected final DateFormat _dateFormat; + + /** + * Object used for creating instances of handlers (serializers, deserializers, + * type and type id resolvers), given class to instantiate. This is typically + * used to do additional configuration (with dependency injection, for example) + * beyond simply construction of instances; or to use alternative constructors. + */ + protected final HandlerInstantiator _handlerInstantiator; + + /** + * Default {@link java.util.Locale} used with serialization formats. + * Default value is {@link Locale#getDefault()}. + */ + protected final Locale _locale; + + /** + * Default {@link java.util.TimeZone} used with serialization formats, + * if (and only if!) explicitly set by use; otherwise `null` to indicate + * "use default", which currently (Jackson 2.6) means "GMT" + *

+ * Note that if a new value is set, timezone is also assigned to + * {@link #_dateFormat} of this object. + */ + protected final TimeZone _timeZone; + + /** + * Explicitly default {@link Base64Variant} to use for handling + * binary data (byte[]), used with data formats + * that use base64 encoding (like JSON, CSV). + * + * @since 2.1 + */ + protected final Base64Variant _defaultBase64; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai, + VisibilityChecker vc, PropertyNamingStrategy pns, TypeFactory tf, + TypeResolverBuilder typer, DateFormat dateFormat, HandlerInstantiator hi, + Locale locale, TimeZone tz, Base64Variant defaultBase64) + { + _classIntrospector = ci; + _annotationIntrospector = ai; + _visibilityChecker = vc; + _propertyNamingStrategy = pns; + _typeFactory = tf; + _typeResolverBuilder = typer; + _dateFormat = dateFormat; + _handlerInstantiator = hi; + _locale = locale; + _timeZone = tz; + _defaultBase64 = defaultBase64; + } + + /* + /********************************************************** + /* Factory methods + /********************************************************** + */ + + public BaseSettings withClassIntrospector(ClassIntrospector ci) { + if (_classIntrospector == ci) { + return this; + } + return new BaseSettings(ci, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withAnnotationIntrospector(AnnotationIntrospector ai) { + if (_annotationIntrospector == ai) { + return this; + } + return new BaseSettings(_classIntrospector, ai, _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withInsertedAnnotationIntrospector(AnnotationIntrospector ai) { + return withAnnotationIntrospector(AnnotationIntrospectorPair.create(ai, _annotationIntrospector)); + } + + public BaseSettings withAppendedAnnotationIntrospector(AnnotationIntrospector ai) { + return withAnnotationIntrospector(AnnotationIntrospectorPair.create(_annotationIntrospector, ai)); + } + + public BaseSettings withVisibilityChecker(VisibilityChecker vc) { + if (_visibilityChecker == vc) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, vc, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) { + return new BaseSettings(_classIntrospector, _annotationIntrospector, + _visibilityChecker.withVisibility(forMethod, visibility), + _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withPropertyNamingStrategy(PropertyNamingStrategy pns) { + if (_propertyNamingStrategy == pns) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, pns, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withTypeFactory(TypeFactory tf) { + if (_typeFactory == tf) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, tf, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withTypeResolverBuilder(TypeResolverBuilder typer) { + if (_typeResolverBuilder == typer) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory, + typer, _dateFormat, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withDateFormat(DateFormat df) { + if (_dateFormat == df) { + return this; + } + // 26-Sep-2015, tatu: Related to [databind#939], let's try to force TimeZone if + // (but only if!) it has been set explicitly. + if ((df != null) && hasExplicitTimeZone()) { + df = _force(df, _timeZone); + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, df, _handlerInstantiator, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings withHandlerInstantiator(HandlerInstantiator hi) { + if (_handlerInstantiator == hi) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, hi, _locale, + _timeZone, _defaultBase64); + } + + public BaseSettings with(Locale l) { + if (_locale == l) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, l, + _timeZone, _defaultBase64); + } + + /** + * Fluent factory for constructing a new instance that uses specified TimeZone. + * Note that timezone used with also be assigned to configured {@link DateFormat}, + * changing time formatting defaults. + */ + public BaseSettings with(TimeZone tz) + { + if (tz == null) { + throw new IllegalArgumentException(); + } + if (tz == _timeZone) { + return this; + } + + DateFormat df = _force(_dateFormat, tz); + return new BaseSettings(_classIntrospector, _annotationIntrospector, + _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, df, _handlerInstantiator, _locale, + tz, _defaultBase64); + } + + /** + * @since 2.1 + */ + public BaseSettings with(Base64Variant base64) { + if (base64 == _defaultBase64) { + return this; + } + return new BaseSettings(_classIntrospector, _annotationIntrospector, + _visibilityChecker, _propertyNamingStrategy, _typeFactory, + _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale, + _timeZone, base64); + } + + /* + /********************************************************** + /* API + /********************************************************** + */ + + public ClassIntrospector getClassIntrospector() { + return _classIntrospector; + } + + public AnnotationIntrospector getAnnotationIntrospector() { + return _annotationIntrospector; + } + + public VisibilityChecker getVisibilityChecker() { + return _visibilityChecker; + } + + public PropertyNamingStrategy getPropertyNamingStrategy() { + return _propertyNamingStrategy; + } + + public TypeFactory getTypeFactory() { + return _typeFactory; + } + + public TypeResolverBuilder getTypeResolverBuilder() { + return _typeResolverBuilder; + } + + public DateFormat getDateFormat() { + return _dateFormat; + } + + public HandlerInstantiator getHandlerInstantiator() { + return _handlerInstantiator; + } + + public Locale getLocale() { + return _locale; + } + + public TimeZone getTimeZone() { + TimeZone tz = _timeZone; + return (tz == null) ? DEFAULT_TIMEZONE : tz; + } + + /** + * Accessor that may be called to determine whether this settings object + * has been explicitly configured with a TimeZone (true), or is still + * relying on the default settings (false). + * + * @since 2.7 + */ + public boolean hasExplicitTimeZone() { + return (_timeZone != null); + } + + public Base64Variant getBase64Variant() { + return _defaultBase64; + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + private DateFormat _force(DateFormat df, TimeZone tz) + { + if (df instanceof StdDateFormat) { + return ((StdDateFormat) df).withTimeZone(tz); + } + // we don't know if original format might be shared; better create a clone: + df = (DateFormat) df.clone(); + df.setTimeZone(tz); + return df; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/ConfigFeature.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/ConfigFeature.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/ConfigFeature.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,27 @@ +package com.fasterxml.jackson.databind.cfg; + +/** + * Interface that actual SerializationFeature enumerations used by + * {@link MapperConfig} implementations must implement. + * Necessary since enums can not be extended using normal + * inheritance, but can implement interfaces + */ +public interface ConfigFeature +{ + /** + * Accessor for checking whether this feature is enabled by default. + */ + public boolean enabledByDefault(); + + /** + * Returns bit mask for this feature instance + */ + public int getMask(); + + /** + * Convenience method for checking whether feature is enabled in given bitmask + * + * @since 2.6 + */ + public boolean enabledIn(int flags); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/ContextAttributes.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/ContextAttributes.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/ContextAttributes.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,221 @@ +package com.fasterxml.jackson.databind.cfg; + +import java.util.*; + +/** + * Helper class used for storing and accessing per-call attributes. + * Storage is two-layered: at higher precedence, we have actual per-call + * attributes; and at lower precedence, default attributes that may be + * defined for Object readers and writers. + *

+ * Note that the way mutability is implemented differs between kinds + * of attributes, to account for thread-safety: per-call attributes + * are handled assuming that instances are never shared, whereas + * changes to per-reader/per-writer attributes are made assuming + * sharing, by creating new copies instead of modifying state. + * This allows sharing of default values without per-call copying, but + * requires two-level lookup on access. + * + * @since 2.3 + */ +public abstract class ContextAttributes +{ + public static ContextAttributes getEmpty() { + return Impl.getEmpty(); + } + + /* + /********************************************************** + /* Per-reader/writer access + /********************************************************** + */ + + public abstract ContextAttributes withSharedAttribute(Object key, Object value); + + public abstract ContextAttributes withSharedAttributes(Map attributes); + + public abstract ContextAttributes withoutSharedAttribute(Object key); + + /* + /********************************************************** + /* Per-operation (serialize/deserialize) access + /********************************************************** + */ + + /** + * Accessor for value of specified attribute + */ + public abstract Object getAttribute(Object key); + + /** + * Mutator used during call (via context) to set value of "non-shared" + * part of attribute set. + */ + public abstract ContextAttributes withPerCallAttribute(Object key, Object value); + + /* + /********************************************************** + /* Default implementation + /********************************************************** + */ + + public static class Impl extends ContextAttributes + implements java.io.Serializable // just so ObjectReader/ObjectWriter can retain configs + { + private static final long serialVersionUID = 1L; + + protected final static Impl EMPTY = new Impl(Collections.emptyMap()); + + protected final static Object NULL_SURROGATE = new Object(); + + /** + * Shared attributes that we can not modify in-place. + */ + protected final Map _shared; + + /** + * Per-call attributes that we can directly modify, since they are not + * shared between threads. + *

+ * NOTE: typed as Object-to-Object, unlike {@link #_shared}, because + * we need to be able to modify contents, and wildcard type would + * complicate that access. + */ + protected transient Map _nonShared; + + /* + /********************************************************** + /* Construction, factory methods + /********************************************************** + */ + + protected Impl(Map shared) { + _shared = shared; + _nonShared = null; + } + + protected Impl(Map shared, Map nonShared) { + _shared = shared; + _nonShared = nonShared; + } + + public static ContextAttributes getEmpty() { + return EMPTY; + } + + /* + /********************************************************** + /* Per-reader/writer mutant factories + /********************************************************** + */ + + @Override + public ContextAttributes withSharedAttribute(Object key, Object value) + { + Map m; + // need to cover one special case, since EMPTY uses Immutable map: + if (this == EMPTY) { + m = new HashMap(8); + } else { + m = _copy(_shared); + } + m.put(key, value); + return new Impl(m); + } + + @Override + public ContextAttributes withSharedAttributes(Map shared) { + return new Impl(shared); + } + + @Override + public ContextAttributes withoutSharedAttribute(Object key) + { + // first couple of trivial optimizations + if (_shared.isEmpty()) { + return this; + } + if (_shared.containsKey(key)) { + if (_shared.size() == 1) { + return EMPTY; + } + } else { // if we didn't have it anyway, return as-is + return this; + } + // otherwise make copy, modify + Map m = _copy(_shared); + m.remove(key); + return new Impl(m); + } + + /* + /********************************************************** + /* Per-call access + /********************************************************** + */ + + @Override + public Object getAttribute(Object key) + { + if (_nonShared != null) { + Object ob = _nonShared.get(key); + if (ob != null) { + if (ob == NULL_SURROGATE) { + return null; + } + return ob; + } + } + return _shared.get(key); + } + + @Override + public ContextAttributes withPerCallAttribute(Object key, Object value) + { + // First: null value may need masking + if (value == null) { + // need to mask nulls to ensure default values won't be showing + if (_shared.containsKey(key)) { + value = NULL_SURROGATE; + } else if ((_nonShared == null) || !_nonShared.containsKey(key)) { + // except if non-mutable shared list has no entry, we don't care + return this; + } else { + _nonShared.remove(key); + return this; + } + } + // a special case: create non-shared instance if need be + if (_nonShared == null) { + return nonSharedInstance(key, value); + } + _nonShared.put(key, value); + return this; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * Overridable method that creates initial non-shared instance, + * with the first explicit set value. + */ + protected ContextAttributes nonSharedInstance(Object key, Object value) + { + Map m = new HashMap(); + if (value == null) { + value = NULL_SURROGATE; + } + m.put(key, value); + return new Impl(_shared, m); + } + + private Map _copy(Map src) + { + return new HashMap(src); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,207 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.databind.AbstractTypeResolver; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.ArrayIterator; + +/** + * Configuration settings container class for {@link DeserializerFactory}. + */ +public class DeserializerFactoryConfig + implements java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1L; // since 2.5 + + protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0]; + protected final static BeanDeserializerModifier[] NO_MODIFIERS = new BeanDeserializerModifier[0]; + protected final static AbstractTypeResolver[] NO_ABSTRACT_TYPE_RESOLVERS = new AbstractTypeResolver[0]; + protected final static ValueInstantiators[] NO_VALUE_INSTANTIATORS = new ValueInstantiators[0]; + + /** + * By default we plug default key deserializers using as "just another" set of + * of key deserializers. + * + * @since 2.2 + */ + protected final static KeyDeserializers[] DEFAULT_KEY_DESERIALIZERS = new KeyDeserializers[] { + new StdKeyDeserializers() + }; + + /** + * List of providers for additional deserializers, checked before considering default + * basic or bean deserializers. + */ + protected final Deserializers[] _additionalDeserializers; + + /** + * List of providers for additional key deserializers, checked before considering + * standard key deserializers. + */ + protected final KeyDeserializers[] _additionalKeyDeserializers; + + /** + * List of modifiers that can change the way {@link BeanDeserializer} instances + * are configured and constructed. + */ + protected final BeanDeserializerModifier[] _modifiers; + + /** + * List of objects that may be able to resolve abstract types to + * concrete types. Used by functionality like "mr Bean" to materialize + * types as needed. + */ + protected final AbstractTypeResolver[] _abstractTypeResolvers; + + /** + * List of objects that know how to create instances of POJO types; + * possibly using custom construction (non-annoted constructors; factory + * methods external to value type etc). + * Used to support objects that are created using non-standard methods; + * or to support post-constructor functionality. + */ + protected final ValueInstantiators[] _valueInstantiators; + + /** + * Constructor for creating basic configuration with no additional + * handlers. + */ + public DeserializerFactoryConfig() { + this(null, null, null, null, null); + } + + /** + * Copy-constructor that will create an instance that contains defined + * set of additional deserializer providers. + */ + protected DeserializerFactoryConfig(Deserializers[] allAdditionalDeserializers, + KeyDeserializers[] allAdditionalKeyDeserializers, + BeanDeserializerModifier[] modifiers, + AbstractTypeResolver[] atr, + ValueInstantiators[] vi) + { + _additionalDeserializers = (allAdditionalDeserializers == null) ? + NO_DESERIALIZERS : allAdditionalDeserializers; + _additionalKeyDeserializers = (allAdditionalKeyDeserializers == null) ? + DEFAULT_KEY_DESERIALIZERS : allAdditionalKeyDeserializers; + _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers; + _abstractTypeResolvers = (atr == null) ? NO_ABSTRACT_TYPE_RESOLVERS : atr; + _valueInstantiators = (vi == null) ? NO_VALUE_INSTANTIATORS : vi; + } + + /** + * Fluent/factory method used to construct a configuration object that + * has same deserializer providers as this instance, plus one specified + * as argument. Additional provider will be added before existing ones, + * meaning it has priority over existing definitions. + */ + public DeserializerFactoryConfig withAdditionalDeserializers(Deserializers additional) + { + if (additional == null) { + throw new IllegalArgumentException("Can not pass null Deserializers"); + } + Deserializers[] all = ArrayBuilders.insertInListNoDup(_additionalDeserializers, additional); + return new DeserializerFactoryConfig(all, _additionalKeyDeserializers, _modifiers, + _abstractTypeResolvers, _valueInstantiators); + } + + /** + * Fluent/factory method used to construct a configuration object that + * has same key deserializer providers as this instance, plus one specified + * as argument. Additional provider will be added before existing ones, + * meaning it has priority over existing definitions. + */ + public DeserializerFactoryConfig withAdditionalKeyDeserializers(KeyDeserializers additional) + { + if (additional == null) { + throw new IllegalArgumentException("Can not pass null KeyDeserializers"); + } + KeyDeserializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeyDeserializers, additional); + return new DeserializerFactoryConfig(_additionalDeserializers, all, _modifiers, + _abstractTypeResolvers, _valueInstantiators); + } + + /** + * Fluent/factory method used to construct a configuration object that + * has same configuration as this instance plus one additional + * deserialiazer modifier. Added modifier has the highest priority (that is, it + * gets called before any already registered modifier). + */ + public DeserializerFactoryConfig withDeserializerModifier(BeanDeserializerModifier modifier) + { + if (modifier == null) { + throw new IllegalArgumentException("Can not pass null modifier"); + } + BeanDeserializerModifier[] all = ArrayBuilders.insertInListNoDup(_modifiers, modifier); + return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, all, + _abstractTypeResolvers, _valueInstantiators); + } + + /** + * Fluent/factory method used to construct a configuration object that + * has same configuration as this instance plus one additional + * abstract type resolver. + * Added resolver has the highest priority (that is, it + * gets called before any already registered resolver). + */ + public DeserializerFactoryConfig withAbstractTypeResolver(AbstractTypeResolver resolver) + { + if (resolver == null) { + throw new IllegalArgumentException("Can not pass null resolver"); + } + AbstractTypeResolver[] all = ArrayBuilders.insertInListNoDup(_abstractTypeResolvers, resolver); + return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers, + all, _valueInstantiators); + } + + /** + * Fluent/factory method used to construct a configuration object that + * has same configuration as this instance plus specified additional + * value instantiator provider object. + * Added instantiator provider has the highest priority (that is, it + * gets called before any already registered resolver). + * + * @param instantiators Object that can provide {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s for + * constructing POJO values during deserialization + */ + public DeserializerFactoryConfig withValueInstantiators(ValueInstantiators instantiators) + { + if (instantiators == null) { + throw new IllegalArgumentException("Can not pass null resolver"); + } + ValueInstantiators[] all = ArrayBuilders.insertInListNoDup(_valueInstantiators, instantiators); + return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers, + _abstractTypeResolvers, all); + } + + public boolean hasDeserializers() { return _additionalDeserializers.length > 0; } + + public boolean hasKeyDeserializers() { return _additionalKeyDeserializers.length > 0; } + + public boolean hasDeserializerModifiers() { return _modifiers.length > 0; } + + public boolean hasAbstractTypeResolvers() { return _abstractTypeResolvers.length > 0; } + + public boolean hasValueInstantiators() { return _valueInstantiators.length > 0; } + + public Iterable deserializers() { + return new ArrayIterator(_additionalDeserializers); + } + + public Iterable keyDeserializers() { + return new ArrayIterator(_additionalKeyDeserializers); + } + + public Iterable deserializerModifiers() { + return new ArrayIterator(_modifiers); + } + + public Iterable abstractTypeResolvers() { + return new ArrayIterator(_abstractTypeResolvers); + } + + public Iterable valueInstantiators() { + return new ArrayIterator(_valueInstantiators); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,163 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Helper class used for handling details of creating handler instances (things + * like {@link JsonSerializer}s, {@link JsonDeserializer}s, various type + * handlers) of specific types. Actual handler type has been resolved at this + * point, so instantiator is strictly responsible for providing a configured + * instance by constructing and configuring a new instance, or possibly by + * recycling a shared instance. One use case is that of allowing + * dependency injection, which would otherwise be difficult to do. + *

+ * Custom instances are allowed to return null to indicate that caller should + * use the default instantiation handling (which just means calling no-argument + * constructor via reflection). + *

+ * Care has to be taken to ensure that if instance returned is shared, it will + * be thread-safe; caller will not synchronize access to returned instances. + */ +public abstract class HandlerInstantiator +{ + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + /** + * Method called to get an instance of deserializer of specified type. + * + * @param config Deserialization configuration in effect + * @param annotated Element (Class, Method, Field, constructor parameter) that + * had annotation defining class of deserializer to construct (to allow + * implementation use information from other annotations) + * @param deserClass Class of deserializer instance to return + * + * @return Deserializer instance to use + */ + public abstract JsonDeserializer deserializerInstance(DeserializationConfig config, + Annotated annotated, Class deserClass); + + /** + * Method called to get an instance of key deserializer of specified type. + * + * @param config Deserialization configuration in effect + * @param annotated Element (Class, Method, Field, constructor parameter) that + * had annotation defining class of key deserializer to construct (to allow + * implementation use information from other annotations) + * @param keyDeserClass Class of key deserializer instance to return + * + * @return Key deserializer instance to use + */ + public abstract KeyDeserializer keyDeserializerInstance(DeserializationConfig config, + Annotated annotated, Class keyDeserClass); + + /** + * Method called to get an instance of serializer of specified type. + * + * @param config Serialization configuration in effect + * @param annotated Element (Class, Method, Field) that + * had annotation defining class of serializer to construct (to allow + * implementation use information from other annotations) + * @param serClass Class of serializer instance to return + * + * @return Serializer instance to use + */ + public abstract JsonSerializer serializerInstance(SerializationConfig config, + Annotated annotated, Class serClass); + + /** + * Method called to get an instance of TypeResolverBuilder of specified type. + * + * @param config Mapper configuration in effect (either SerializationConfig or + * DeserializationConfig, depending on when instance is being constructed) + * @param annotated annotated Element (Class, Method, Field) that + * had annotation defining class of builder to construct (to allow + * implementation use information from other annotations) + * @param builderClass Class of builder instance to return + * + * @return TypeResolverBuilder instance to use + */ + public abstract TypeResolverBuilder typeResolverBuilderInstance(MapperConfig config, + Annotated annotated, Class builderClass); + + /** + * Method called to get an instance of TypeIdResolver of specified type. + * + * @param config Mapper configuration in effect (either SerializationConfig or + * DeserializationConfig, depending on when instance is being constructed) + * @param annotated annotated Element (Class, Method, Field) that + * had annotation defining class of resolver to construct (to allow + * implementation use information from other annotations) + * @param resolverClass Class of resolver instance to return + * + * @return TypeResolverBuilder instance to use + */ + public abstract TypeIdResolver typeIdResolverInstance(MapperConfig config, + Annotated annotated, Class resolverClass); + + /** + * Method called to construct an instance of ValueInstantiator of specified type. + */ + public ValueInstantiator valueInstantiatorInstance(MapperConfig config, + Annotated annotated, Class resolverClass) { + return null; + } + + + /** + * Method called to construct a ObjectIdHandler instance of specified type. + * + * @since 2.0 + */ + public ObjectIdGenerator objectIdGeneratorInstance(MapperConfig config, + Annotated annotated, Class implClass) { + return null; + } + + public ObjectIdResolver resolverIdGeneratorInstance(MapperConfig config, Annotated annotated, Class implClass) { + return null; + } + + /** + * Method called to construct a NamingStrategy instance used for specified + * class. + * + * @since 2.1 + */ + public PropertyNamingStrategy namingStrategyInstance(MapperConfig config, + Annotated annotated, Class implClass) { + return null; + } + + /** + * Method called to construct a Converter instance used for specified class. + * + * @since 2.2 + */ + public Converter converterInstance(MapperConfig config, + Annotated annotated, Class implClass) { + return null; + } + + /** + * Method called to construct a {@link VirtualBeanPropertyWriter} instance + * of specified type. + * + * @since 2.5 + */ + public VirtualBeanPropertyWriter virtualPropertyWriterInstance(MapperConfig config, + Class implClass) { + return null; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/MapperConfig.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/MapperConfig.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/MapperConfig.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,488 @@ +package com.fasterxml.jackson.databind.cfg; + +import java.text.DateFormat; +import java.util.Locale; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.*; + +import com.fasterxml.jackson.core.Base64Variant; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.core.type.TypeReference; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Interface that defines functionality accessible through both + * serialization and deserialization configuration objects; + * accessors to mode-independent configuration settings + * and such. + * In addition, shared features are defined + * in {@link MapperFeature}. + *

+ * Small part of implementation is included here by aggregating + * {@link BaseSettings} instance that contains configuration + * that is shared between different types of instances. + */ +public abstract class MapperConfig> + implements ClassIntrospector.MixInResolver, + java.io.Serializable +{ + private static final long serialVersionUID = 1L; // since 2.5 + + /** + * @since 2.7 + */ + protected final static JsonInclude.Value EMPTY_INCLUDE = JsonInclude.Value.empty(); + + /** + * @since 2.7 + */ + protected final static JsonFormat.Value EMPTY_FORMAT = JsonFormat.Value.empty(); + + /** + * Set of shared mapper features enabled. + */ + protected final int _mapperFeatures; + + /** + * Immutable container object for simple configuration settings. + */ + protected final BaseSettings _base; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + protected MapperConfig(BaseSettings base, int mapperFeatures) + { + _base = base; + _mapperFeatures = mapperFeatures; + } + + protected MapperConfig(MapperConfig src, int mapperFeatures) + { + _base = src._base; + _mapperFeatures = mapperFeatures; + } + + protected MapperConfig(MapperConfig src, BaseSettings base) + { + _base = base; + _mapperFeatures = src._mapperFeatures; + } + + protected MapperConfig(MapperConfig src) + { + _base = src._base; + _mapperFeatures = src._mapperFeatures; + } + + /** + * Method that calculates bit set (flags) of all features that + * are enabled by default. + */ + public static & ConfigFeature> int collectFeatureDefaults(Class enumClass) + { + int flags = 0; + for (F value : enumClass.getEnumConstants()) { + if (value.enabledByDefault()) { + flags |= value.getMask(); + } + } + return flags; + } + + /* + /********************************************************** + /* Life-cycle: factory methods + /********************************************************** + */ + + /** + * Method for constructing and returning a new instance with specified + * mapper features enabled. + */ + public abstract T with(MapperFeature... features); + + /** + * Method for constructing and returning a new instance with specified + * mapper features disabled. + */ + public abstract T without(MapperFeature... features); + + /** + * @since 2.3 + */ + public abstract T with(MapperFeature feature, boolean state); + + /* + /********************************************************** + /* Configuration: simple features + /********************************************************** + */ + + /** + * Accessor for simple mapper features (which are shared for + * serialization, deserialization) + */ + public final boolean isEnabled(MapperFeature f) { + return (_mapperFeatures & f.getMask()) != 0; + } + + /** + * "Bulk" access method for checking that all features specified by + * mask are enabled. + * + * @since 2.3 + */ + public final boolean hasMapperFeatures(int featureMask) { + return (_mapperFeatures & featureMask) == featureMask; + } + + /** + * Method for determining whether annotation processing is enabled or not + * (default settings are typically that it is enabled; must explicitly disable). + * + * @return True if annotation processing is enabled; false if not + */ + public final boolean isAnnotationProcessingEnabled() { + return isEnabled(MapperFeature.USE_ANNOTATIONS); + } + + /** + * Accessor for determining whether it is ok to try to force override of access + * modifiers to be able to get or set values of non-public Methods, Fields; + * to invoke non-public Constructors, Methods; or to instantiate non-public + * Classes. By default this is enabled, but on some platforms it needs to be + * prevented since if this would violate security constraints and cause failures. + * + * @return True if access modifier overriding is allowed (and may be done for + * any Field, Method, Constructor or Class); false to prevent any attempts + * to override. + */ + public final boolean canOverrideAccessModifiers() { + return isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS); + } + + /** + * Accessor for checking whether default settings for property handling + * indicate that properties should be alphabetically ordered or not. + */ + public final boolean shouldSortPropertiesAlphabetically() { + return isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); + } + + /** + * Accessor for checking whether configuration indicates that + * "root wrapping" (use of an extra property/name pair at root level) + * is expected or not. + */ + public abstract boolean useRootWrapping(); + + /* + /********************************************************** + /* Configuration: factory methods + /********************************************************** + */ + + /** + * Method for constructing a specialized textual object that can typically + * be serialized faster than basic {@link java.lang.String} (depending + * on escaping needed if any, char-to-byte encoding if needed). + * + * @param src Text to represent + * + * @return Optimized text object constructed + * + * @since 2.4 + */ + public SerializableString compileString(String src) { + /* 20-Jan-2014, tatu: For now we will just construct it directly, but + * for 2.4 need to allow overriding to support non-standard extensions + * to be used by extensions like Afterburner. + */ + return new SerializedString(src); + } + + /* + /********************************************************** + /* Configuration: introspectors, mix-ins + /********************************************************** + */ + + public ClassIntrospector getClassIntrospector() { + return _base.getClassIntrospector(); + } + + /** + * Method for getting {@link AnnotationIntrospector} configured + * to introspect annotation values used for configuration. + *

+ * Non-final since it is actually overridden by sub-classes (for now?) + */ + public AnnotationIntrospector getAnnotationIntrospector() { + return _base.getAnnotationIntrospector(); + } + + /** + * Accessor for object used for determining whether specific property elements + * (method, constructors, fields) can be auto-detected based on + * their visibility (access modifiers). Can be changed to allow + * different minimum visibility levels for auto-detection. Note + * that this is the global handler; individual types (classes) + * can further override active checker used (using + * {@link JsonAutoDetect} annotation) + */ + public VisibilityChecker getDefaultVisibilityChecker() { + return _base.getVisibilityChecker(); + } + + public final PropertyNamingStrategy getPropertyNamingStrategy() { + return _base.getPropertyNamingStrategy(); + } + + public final HandlerInstantiator getHandlerInstantiator() { + return _base.getHandlerInstantiator(); + } + + /* + /********************************************************** + /* Configuration: type and subtype handling + /********************************************************** + */ + + /** + * Method called to locate a type info handler for types that do not have + * one explicitly declared via annotations (or other configuration). + * If such default handler is configured, it is returned; otherwise + * null is returned. + */ + public final TypeResolverBuilder getDefaultTyper(JavaType baseType) { + return _base.getTypeResolverBuilder(); + } + + public abstract SubtypeResolver getSubtypeResolver(); + + public final TypeFactory getTypeFactory() { + return _base.getTypeFactory(); + } + + /** + * Helper method that will construct {@link JavaType} for given + * raw class. + * This is a simple short-cut for: + *

+     *    getTypeFactory().constructType(cls);
+     *
+ */ + public final JavaType constructType(Class cls) { + return getTypeFactory().constructType(cls); + } + + /** + * Helper method that will construct {@link JavaType} for given + * type reference + * This is a simple short-cut for: + *
+     *    getTypeFactory().constructType(valueTypeRef);
+     *
+ */ + public final JavaType constructType(TypeReference valueTypeRef) { + return getTypeFactory().constructType(valueTypeRef.getType()); + } + + public JavaType constructSpecializedType(JavaType baseType, Class subclass) { + return getTypeFactory().constructSpecializedType(baseType, subclass); + } + + /* + /********************************************************** + /* Configuration: introspection support + /********************************************************** + */ + + /** + * Accessor for getting bean description that only contains class + * annotations: useful if no getter/setter/creator information is needed. + */ + public BeanDescription introspectClassAnnotations(Class cls) { + return introspectClassAnnotations(constructType(cls)); + } + + /** + * Accessor for getting bean description that only contains class + * annotations: useful if no getter/setter/creator information is needed. + */ + public abstract BeanDescription introspectClassAnnotations(JavaType type); + + /** + * Accessor for getting bean description that only contains immediate class + * annotations: ones from the class, and its direct mix-in, if any, but + * not from super types. + */ + public BeanDescription introspectDirectClassAnnotations(Class cls) { + return introspectDirectClassAnnotations(constructType(cls)); + } + /** + * Accessor for getting bean description that only contains immediate class + * annotations: ones from the class, and its direct mix-in, if any, but + * not from super types. + */ + public abstract BeanDescription introspectDirectClassAnnotations(JavaType type); + + /* + /********************************************************** + /* Configuration: default settings with per-type overrides + /********************************************************** + */ + + /** + * Accessor for default property inclusion to use for serialization, + * used unless overridden by per-type or per-property overrides. + * + * @since 2.7 + */ + public abstract JsonInclude.Value getDefaultPropertyInclusion(); + + /** + * Accessor for default property inclusion to use for serialization, + * considering possible per-type override for given base type. + * + * @since 2.7 + */ + public abstract JsonInclude.Value getDefaultPropertyInclusion(Class baseType); + + /** + * Accessor for default format settings to use for serialization (and, to a degree + * deserialization), considering baseline settings and per-type defaults + * for given base type (if any). + * + * @since 2.7 + */ + public abstract JsonFormat.Value getDefaultPropertyFormat(Class baseType); + + /* + /********************************************************** + /* Configuration: other + /********************************************************** + */ + + /** + * Method for accessing currently configured (textual) date format + * that will be used for reading or writing date values (in case + * of writing, only if textual output is configured; not if dates + * are to be serialized as time stamps). + *

+ * Note that typically {@link DateFormat} instances are not thread-safe + * (at least ones provided by JDK): + * this means that calling code should clone format instance before + * using it. + *

+ * This method is usually only called by framework itself, since there + * are convenience methods available via + * {@link DeserializationContext} and {@link SerializerProvider} that + * take care of cloning and thread-safe reuse. + */ + public final DateFormat getDateFormat() { return _base.getDateFormat(); } + + /** + * Method for accessing the default {@link java.util.Locale} to use + * for formatting, unless overridden by local annotations. + * Initially set to {@link Locale#getDefault()}. + */ + public final Locale getLocale() { return _base.getLocale(); } + + /** + * Method for accessing the default {@link java.util.TimeZone} to use + * for formatting, unless overridden by local annotations. + * Initially set to {@link TimeZone#getDefault()}. + */ + public final TimeZone getTimeZone() { return _base.getTimeZone(); } + + /** + * Accessor for finding currently active view, if any (null if none) + */ + public abstract Class getActiveView(); + + /** + * Method called during deserialization if Base64 encoded content + * needs to be decoded. Default version just returns default Jackson + * uses, which is modified-mime which does not add linefeeds (because + * those would have to be escaped in JSON strings); but this can + * be configured on {@link ObjectWriter}. + */ + public Base64Variant getBase64Variant() { + return _base.getBase64Variant(); + } + + /** + * Method for accessing per-instance shared (baseline/default) + * attribute values; these are used as the basis for per-call + * attributes. + * + * @since 2.3 + */ + public abstract ContextAttributes getAttributes(); + + /** + * @since 2.6 + */ + public abstract PropertyName findRootName(JavaType rootType); + + /** + * @since 2.6 + */ + public abstract PropertyName findRootName(Class rawRootType); + + /* + /********************************************************** + /* Methods for instantiating handlers + /********************************************************** + */ + + /** + * Method that can be called to obtain an instance of TypeIdResolver of + * specified type. + */ + public TypeResolverBuilder typeResolverBuilderInstance(Annotated annotated, + Class> builderClass) + { + HandlerInstantiator hi = getHandlerInstantiator(); + if (hi != null) { + TypeResolverBuilder builder = hi.typeResolverBuilderInstance(this, annotated, builderClass); + if (builder != null) { + return builder; + } + } + return (TypeResolverBuilder) ClassUtil.createInstance(builderClass, canOverrideAccessModifiers()); + } + + /** + * Method that can be called to obtain an instance of TypeIdResolver of + * specified type. + */ + public TypeIdResolver typeIdResolverInstance(Annotated annotated, + Class resolverClass) + { + HandlerInstantiator hi = getHandlerInstantiator(); + if (hi != null) { + TypeIdResolver builder = hi.typeIdResolverInstance(this, annotated, resolverClass); + if (builder != null) { + return builder; + } + } + return (TypeIdResolver) ClassUtil.createInstance(resolverClass, canOverrideAccessModifiers()); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,497 @@ +package com.fasterxml.jackson.databind.cfg; + +import java.text.DateFormat; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.Base64Variant; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; +import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.RootNameLookup; + +@SuppressWarnings("serial") +public abstract class MapperConfigBase> + extends MapperConfig + implements java.io.Serializable +{ + private final static int DEFAULT_MAPPER_FEATURES = collectFeatureDefaults(MapperFeature.class); + + /* + /********************************************************** + /* Immutable config + /********************************************************** + */ + + /** + * Mix-in annotation mappings to use, if any: immutable, + * can not be changed once defined. + * + * @since 2.6 + */ + protected final SimpleMixInResolver _mixIns; + + /** + * Registered concrete subtypes that can be used instead of (or + * in addition to) ones declared using annotations. + */ + protected final SubtypeResolver _subtypeResolver; + + /** + * Explicitly defined root name to use, if any; if empty + * String, will disable root-name wrapping; if null, will + * use defaults + */ + protected final PropertyName _rootName; + + /** + * View to use for filtering out properties to serialize + * or deserialize. + * Null if none (will also be assigned null if Object.class + * is defined), meaning that all properties are to be included. + */ + protected final Class _view; + + /** + * Contextual attributes accessible (get and set) during processing, + * on per-call basis. + * + * @since 2.3 + */ + protected final ContextAttributes _attributes; + + /** + * @since 2.6 + */ + protected final RootNameLookup _rootNames; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + /** + * Constructor used when creating a new instance (compared to + * that of creating fluent copies) + */ + protected MapperConfigBase(BaseSettings base, + SubtypeResolver str, SimpleMixInResolver mixins, + RootNameLookup rootNames) + { + super(base, DEFAULT_MAPPER_FEATURES); + _mixIns = mixins; + _subtypeResolver = str; + _rootNames = rootNames; + _rootName = null; + _view = null; + // default to "no attributes" + _attributes = ContextAttributes.getEmpty(); + } + + /** + * Pass-through constructor used when no changes are needed to the + * base class. + */ + protected MapperConfigBase(MapperConfigBase src) + { + super(src); + _mixIns = src._mixIns; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = src._attributes; + } + + protected MapperConfigBase(MapperConfigBase src, BaseSettings base) + { + super(src, base); + _mixIns = src._mixIns; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = src._attributes; + } + + protected MapperConfigBase(MapperConfigBase src, int mapperFeatures) + { + super(src, mapperFeatures); + _mixIns = src._mixIns; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = src._attributes; + } + + protected MapperConfigBase(MapperConfigBase src, SubtypeResolver str) { + super(src); + _mixIns = src._mixIns; + _subtypeResolver = str; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = src._attributes; + } + + protected MapperConfigBase(MapperConfigBase src, PropertyName rootName) { + super(src); + _mixIns = src._mixIns; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = rootName; + _view = src._view; + _attributes = src._attributes; + } + + protected MapperConfigBase(MapperConfigBase src, Class view) + { + super(src); + _mixIns = src._mixIns; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = view; + _attributes = src._attributes; + } + + /** + * @since 2.1 + */ + protected MapperConfigBase(MapperConfigBase src, SimpleMixInResolver mixins) + { + super(src); + _mixIns = mixins; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = src._attributes; + } + + /** + * @since 2.3 + */ + protected MapperConfigBase(MapperConfigBase src, ContextAttributes attr) + { + super(src); + _mixIns = src._mixIns; + _subtypeResolver = src._subtypeResolver; + _rootNames = src._rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = attr; + } + + /** + * @since 2.6 + */ + protected MapperConfigBase(MapperConfigBase src, SimpleMixInResolver mixins, + RootNameLookup rootNames) + { + super(src); + _mixIns = mixins; + _subtypeResolver = src._subtypeResolver; + _rootNames = rootNames; + _rootName = src._rootName; + _view = src._view; + _attributes = src._attributes; + } + + /* + /********************************************************** + /* Addition fluent factory methods, common to all sub-types + /********************************************************** + */ + + /** + * Method for constructing and returning a new instance with different + * {@link AnnotationIntrospector} to use (replacing old one). + *

+ * NOTE: make sure to register new instance with ObjectMapper + * if directly calling this method. + */ + public abstract T with(AnnotationIntrospector ai); + + /** + * Method for constructing and returning a new instance with additional + * {@link AnnotationIntrospector} appended (as the lowest priority one) + */ + public abstract T withAppendedAnnotationIntrospector(AnnotationIntrospector introspector); + + /** + * Method for constructing and returning a new instance with additional + * {@link AnnotationIntrospector} inserted (as the highest priority one) + */ + public abstract T withInsertedAnnotationIntrospector(AnnotationIntrospector introspector); + + /** + * Method for constructing and returning a new instance with different + * {@link ClassIntrospector} + * to use. + *

+ * NOTE: make sure to register new instance with ObjectMapper + * if directly calling this method. + */ + public abstract T with(ClassIntrospector ci); + + /** + * Method for constructing and returning a new instance with different + * {@link DateFormat} + * to use. + *

+ * NOTE: make sure to register new instance with ObjectMapper + * if directly calling this method. + */ + public abstract T with(DateFormat df); + + /** + * Method for constructing and returning a new instance with different + * {@link HandlerInstantiator} + * to use. + *

+ * NOTE: make sure to register new instance with ObjectMapper + * if directly calling this method. + */ + public abstract T with(HandlerInstantiator hi); + + /** + * Method for constructing and returning a new instance with different + * {@link PropertyNamingStrategy} + * to use. + *

+ * NOTE: make sure to register new instance with ObjectMapper + * if directly calling this method. + */ + public abstract T with(PropertyNamingStrategy strategy); + + /** + * Method for constructing and returning a new instance with different + * root name to use (none, if null). + *

+ * Note that when a root name is set to a non-Empty String, this will automatically force use + * of root element wrapping with given name. If empty String passed, will + * disable root name wrapping; and if null used, will instead use + * SerializationFeature to determine if to use wrapping, and annotation + * (or default name) for actual root name to use. + * + * @param rootName to use: if null, means "use default" (clear setting); + * if empty String ("") means that no root name wrapping is used; + * otherwise defines root name to use. + * + * @since 2.6 + */ + public abstract T withRootName(PropertyName rootName); + + public T withRootName(String rootName) { + if (rootName == null) { + return withRootName((PropertyName) null); + } + return withRootName(PropertyName.construct(rootName)); + } + + /** + * Method for constructing and returning a new instance with different + * {@link SubtypeResolver} + * to use. + *

+ * NOTE: make sure to register new instance with ObjectMapper + * if directly calling this method. + */ + public abstract T with(SubtypeResolver str); + + /** + * Method for constructing and returning a new instance with different + * {@link TypeFactory} + * to use. + */ + public abstract T with(TypeFactory typeFactory); + + /** + * Method for constructing and returning a new instance with different + * {@link TypeResolverBuilder} to use. + */ + public abstract T with(TypeResolverBuilder trb); + + /** + * Method for constructing and returning a new instance with different + * view to use. + */ + public abstract T withView(Class view); + + /** + * Method for constructing and returning a new instance with different + * {@link VisibilityChecker} + * to use. + */ + public abstract T with(VisibilityChecker vc); + + /** + * Method for constructing and returning a new instance with different + * minimal visibility level for specified property type + */ + public abstract T withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility); + + /** + * Method for constructing and returning a new instance with different + * default {@link java.util.Locale} to use for formatting. + */ + public abstract T with(Locale l); + + /** + * Method for constructing and returning a new instance with different + * default {@link java.util.TimeZone} to use for formatting of date values. + */ + public abstract T with(TimeZone tz); + + /** + * Method for constructing and returning a new instance with different + * default {@link Base64Variant} to use with base64-encoded binary values. + */ + public abstract T with(Base64Variant base64); + + /** + * Method for constructing an instance that has specified + * contextual attributes. + * + * @since 2.3 + */ + public abstract T with(ContextAttributes attrs); + + /** + * Method for constructing an instance that has only specified + * attributes, removing any attributes that exist before the call. + * + * @since 2.3 + */ + public T withAttributes(Map attributes) { + return with(getAttributes().withSharedAttributes(attributes)); + } + + /** + * Method for constructing an instance that has specified + * value for attribute for given key. + * + * @since 2.3 + */ + public T withAttribute(Object key, Object value) { + return with(getAttributes().withSharedAttribute(key, value)); + } + + /** + * Method for constructing an instance that has no + * value for attribute for given key. + * + * @since 2.3 + */ + public T withoutAttribute(Object key) { + return with(getAttributes().withoutSharedAttribute(key)); + } + + /* + /********************************************************** + /* Simple accessors + /********************************************************** + */ + + /** + * Accessor for object used for finding out all reachable subtypes + * for supertypes; needed when a logical type name is used instead + * of class name (or custom scheme). + */ + @Override + public final SubtypeResolver getSubtypeResolver() { + return _subtypeResolver; + } + + /** + * @deprecated Since 2.6 use {@link #getFullRootName} instead. + */ + @Deprecated // since 2.6 + public final String getRootName() { + return (_rootName == null) ? null : _rootName.getSimpleName(); + } + + /** + * @since 2.6 + */ + public final PropertyName getFullRootName() { + return _rootName; + } + + @Override + public final Class getActiveView() { + return _view; + } + + @Override + public final ContextAttributes getAttributes() { + return _attributes; + } + + /* + /********************************************************** + /* Other config access + /********************************************************** + */ + + @Override + public PropertyName findRootName(JavaType rootType) { + if (_rootName != null) { + return _rootName; + } + return _rootNames.findRootName(rootType, this); + } + + @Override + public PropertyName findRootName(Class rawRootType) { + if (_rootName != null) { + return _rootName; + } + return _rootNames.findRootName(rawRootType, this); + } + + /* + /********************************************************** + /* ClassIntrospector.MixInResolver impl: + /********************************************************** + */ + + /** + * Method that will check if there are "mix-in" classes (with mix-in + * annotations) for given class + */ + @Override + public final Class findMixInClassFor(Class cls) { + return _mixIns.findMixInClassFor(cls); + } + + // Not really relevant here (should not get called) + @Override + public MixInResolver copy() { + throw new UnsupportedOperationException(); + } + + /** + * Test-only method -- does not reflect possibly open-ended set that external + * mix-in resolver might provide. + */ + public final int mixInCount() { + return _mixIns.localSize(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/PackageVersion.java.in =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/PackageVersion.java.in (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/PackageVersion.java.in (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,20 @@ +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; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.cfg; + +import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.ArrayIterator; + +/** + * Configuration settings container class for + * {@link SerializerFactory} implementations. + */ +public final class SerializerFactoryConfig + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + /** + * Constant for empty Serializers array (which by definition + * is stateless and reusable) + */ + protected final static Serializers[] NO_SERIALIZERS = new Serializers[0]; + + protected final static BeanSerializerModifier[] NO_MODIFIERS = new BeanSerializerModifier[0]; + + /** + * List of providers for additional serializers, checked before considering default + * basic or bean serialializers. + */ + protected final Serializers[] _additionalSerializers; + + /** + * List of providers for additional key serializers, checked before considering default + * key serialializers. + */ + protected final Serializers[] _additionalKeySerializers; + + /** + * List of modifiers that can change the way {@link BeanSerializer} instances + * are configured and constructed. + */ + protected final BeanSerializerModifier[] _modifiers; + + public SerializerFactoryConfig() { + this(null, null, null); + } + + protected SerializerFactoryConfig(Serializers[] allAdditionalSerializers, + Serializers[] allAdditionalKeySerializers, + BeanSerializerModifier[] modifiers) + { + _additionalSerializers = (allAdditionalSerializers == null) ? + NO_SERIALIZERS : allAdditionalSerializers; + _additionalKeySerializers = (allAdditionalKeySerializers == null) ? + NO_SERIALIZERS : allAdditionalKeySerializers; + _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers; + } + + public SerializerFactoryConfig withAdditionalSerializers(Serializers additional) + { + if (additional == null) { + throw new IllegalArgumentException("Can not pass null Serializers"); + } + Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalSerializers, additional); + return new SerializerFactoryConfig(all, _additionalKeySerializers, _modifiers); + } + + public SerializerFactoryConfig withAdditionalKeySerializers(Serializers additional) + { + if (additional == null) { + throw new IllegalArgumentException("Can not pass null Serializers"); + } + Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeySerializers, additional); + return new SerializerFactoryConfig(_additionalSerializers, all, _modifiers); + } + + public SerializerFactoryConfig withSerializerModifier(BeanSerializerModifier modifier) + { + if (modifier == null) { + throw new IllegalArgumentException("Can not pass null modifier"); + } + BeanSerializerModifier[] modifiers = ArrayBuilders.insertInListNoDup(_modifiers, modifier); + return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, modifiers); + } + + public boolean hasSerializers() { return _additionalSerializers.length > 0; } + public boolean hasKeySerializers() { return _additionalKeySerializers.length > 0; } + public boolean hasSerializerModifiers() { return _modifiers.length > 0; } + public Iterable serializers() { return new ArrayIterator(_additionalSerializers); } + public Iterable keySerializers() { return new ArrayIterator(_additionalKeySerializers); } + public Iterable serializerModifiers() { return new ArrayIterator(_modifiers); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/cfg/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,8 @@ +/** +Package that contains most of configuration-related classes; +exception being couple of most-commonly used configuration +things (like Feature enumerations) that are at the +main level (com.fasterxml.jackson.databind). +*/ + +package com.fasterxml.jackson.databind.cfg; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,214 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Deserializer only used for abstract types used as placeholders during polymorphic + * type handling deserialization. If so, there is no real deserializer associated + * with nominal type, just {@link TypeDeserializer}; and any calls that do not + * pass such resolver will result in an error. + */ +public class AbstractDeserializer + extends JsonDeserializer + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final JavaType _baseType; + + protected final ObjectIdReader _objectIdReader; + + protected final Map _backRefProperties; + + // support for "native" types, which require special care: + + protected final boolean _acceptString; + protected final boolean _acceptBoolean; + protected final boolean _acceptInt; + protected final boolean _acceptDouble; + + public AbstractDeserializer(BeanDeserializerBuilder builder, + BeanDescription beanDesc, Map backRefProps) + { + _baseType = beanDesc.getType(); + _objectIdReader = builder.getObjectIdReader(); + _backRefProperties = backRefProps; + Class cls = _baseType.getRawClass(); + _acceptString = cls.isAssignableFrom(String.class); + _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); + _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); + _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); + } + + protected AbstractDeserializer(BeanDescription beanDesc) + { + _baseType = beanDesc.getType(); + _objectIdReader = null; + _backRefProperties = null; + Class cls = _baseType.getRawClass(); + _acceptString = cls.isAssignableFrom(String.class); + _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); + _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); + _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); + } + + /** + * Factory method used when constructing instances for non-POJO types, like + * {@link java.util.Map}s. + * + * @since 2.3 + */ + public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) { + return new AbstractDeserializer(beanDesc); + } + + /* + /********************************************************** + /* Public accessors + /********************************************************** + */ + + @Override + public Class handledType() { + return _baseType.getRawClass(); + } + + @Override + public boolean isCachable() { return true; } + + /** + * Overridden to return true for those instances that are + * handling value for which Object Identity handling is enabled + * (either via value type or referring property). + */ + @Override + public ObjectIdReader getObjectIdReader() { + return _objectIdReader; + } + + /** + * Method called by BeanDeserializer to resolve back reference + * part of managed references. + */ + @Override + public SettableBeanProperty findBackReference(String logicalName) { + return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName); + } + + /* + /********************************************************** + /* Deserializer implementation + /********************************************************** + */ + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type? + // for now, prefer Object Id: + if (_objectIdReader != null) { + JsonToken t = p.getCurrentToken(); + if (t != null) { + // Most commonly, a scalar (int id, uuid String, ...) + if (t.isScalarValue()) { + return _deserializeFromObjectId(p, ctxt); + } + // but, with 2.5+, a simple Object-wrapped value also legal: + if (t == JsonToken.START_OBJECT) { + t = p.nextToken(); + } + if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() + && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { + return _deserializeFromObjectId(p, ctxt); + } + + } + } + + // First: support "natural" values (which are always serialized without type info!) + Object result = _deserializeIfNatural(p, ctxt); + if (result != null) { + return result; + } + return typeDeserializer.deserializeTypedFromObject(p, ctxt); + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // This method should never be called... + throw ctxt.instantiationException(_baseType.getRawClass(), + "abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information"); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt) throws IOException + { + /* There is a chance we might be "natural" types + * (String, Boolean, Integer, Double), which do not include any type information... + * Care must be taken to only return this if return type matches, however. + * Finally, we may have to consider possibility of custom handlers for + * these values: but for now this should work ok. + */ + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_STRING: + if (_acceptString) { + return p.getText(); + } + break; + case JsonTokenId.ID_NUMBER_INT: + if (_acceptInt) { + return p.getIntValue(); + } + break; + case JsonTokenId.ID_NUMBER_FLOAT: + if (_acceptDouble) { + return Double.valueOf(p.getDoubleValue()); + } + break; + case JsonTokenId.ID_TRUE: + if (_acceptBoolean) { + return Boolean.TRUE; + } + break; + case JsonTokenId.ID_FALSE: + if (_acceptBoolean) { + return Boolean.FALSE; + } + break; + } + return null; + } + + /** + * Method called in cases where it looks like we got an Object Id + * to parse and use as a reference. + */ + protected Object _deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException + { + Object id = _objectIdReader.readObjectReference(p, ctxt); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); + // do we have it resolved? + Object pojo = roid.resolve(); + if (pojo == null) { // not yet; should wait... + throw new UnresolvedForwardReference(p, + "Could not resolve Object Id ["+id+"] -- unresolved forward-reference?", p.getCurrentLocation(), roid); + } + return pojo; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1931 @@ +package com.fasterxml.jackson.databind.deser; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.deser.impl.CreatorCollector; +import com.fasterxml.jackson.databind.deser.std.*; +import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.type.*; +import com.fasterxml.jackson.databind.util.*; + +/** + * Abstract factory base class that can provide deserializers for standard + * JDK classes, including collection classes and simple heuristics for + * "upcasting" common collection interface types + * (such as {@link java.util.Collection}). + *

+ * Since all simple deserializers are eagerly instantiated, and there is + * no additional introspection or customizability of these types, + * this factory is stateless. + */ +@SuppressWarnings("serial") +public abstract class BasicDeserializerFactory + extends DeserializerFactory + implements java.io.Serializable +{ + private final static Class CLASS_OBJECT = Object.class; + private final static Class CLASS_STRING = String.class; + private final static Class CLASS_CHAR_BUFFER = CharSequence.class; + private final static Class CLASS_ITERABLE = Iterable.class; + private final static Class CLASS_MAP_ENTRY = Map.Entry.class; + + /** + * We need a placeholder for creator properties that don't have name + * but are marked with `@JsonWrapped` annotation. + */ + protected final static PropertyName UNWRAPPED_CREATOR_PARAM_NAME = new PropertyName("@JsonUnwrapped"); + + /* We do some defaulting for abstract Map classes and + * interfaces, to avoid having to use exact types or annotations in + * cases where the most common concrete Maps will do. + */ + @SuppressWarnings("rawtypes") + final static HashMap> _mapFallbacks = + new HashMap>(); + static { + _mapFallbacks.put(Map.class.getName(), LinkedHashMap.class); + _mapFallbacks.put(ConcurrentMap.class.getName(), ConcurrentHashMap.class); + _mapFallbacks.put(SortedMap.class.getName(), TreeMap.class); + + _mapFallbacks.put(java.util.NavigableMap.class.getName(), TreeMap.class); + _mapFallbacks.put(java.util.concurrent.ConcurrentNavigableMap.class.getName(), + java.util.concurrent.ConcurrentSkipListMap.class); + } + + /* We do some defaulting for abstract Collection classes and + * interfaces, to avoid having to use exact types or annotations in + * cases where the most common concrete Collection will do. + */ + @SuppressWarnings("rawtypes") + final static HashMap> _collectionFallbacks = + new HashMap>(); + static { + _collectionFallbacks.put(Collection.class.getName(), ArrayList.class); + _collectionFallbacks.put(List.class.getName(), ArrayList.class); + _collectionFallbacks.put(Set.class.getName(), HashSet.class); + _collectionFallbacks.put(SortedSet.class.getName(), TreeSet.class); + _collectionFallbacks.put(Queue.class.getName(), LinkedList.class); + + // then JDK 1.6 types: + /* 17-May-2013, tatu: [databind#216] Should be fine to use straight Class references EXCEPT + * that some god-forsaken platforms (... looking at you, Android) do not + * include these. So, use "soft" references... + */ + _collectionFallbacks.put("java.util.Deque", LinkedList.class); + _collectionFallbacks.put("java.util.NavigableSet", TreeSet.class); + } + + /* + /********************************************************** + /* Config + /********************************************************** + */ + + /** + * Configuration settings for this factory; immutable instance (just like this + * factory), new version created via copy-constructor (fluent-style) + */ + protected final DeserializerFactoryConfig _factoryConfig; + + /* + /********************************************************** + /* Life cycle + /********************************************************** + */ + + protected BasicDeserializerFactory(DeserializerFactoryConfig config) { + _factoryConfig = config; + } + + /** + * Method for getting current {@link DeserializerFactoryConfig}. + *

+ * Note that since instances are immutable, you can NOT change settings + * by accessing an instance and calling methods: this will simply create + * new instance of config object. + */ + public DeserializerFactoryConfig getFactoryConfig() { + return _factoryConfig; + } + + protected abstract DeserializerFactory withConfig(DeserializerFactoryConfig config); + + /* + /******************************************************** + /* Configuration handling: fluent factories + /******************************************************** + */ + + /** + * Convenience method for creating a new factory instance with additional deserializer + * provider. + */ + @Override + public final DeserializerFactory withAdditionalDeserializers(Deserializers additional) { + return withConfig(_factoryConfig.withAdditionalDeserializers(additional)); + } + + /** + * Convenience method for creating a new factory instance with additional + * {@link KeyDeserializers}. + */ + @Override + public final DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional) { + return withConfig(_factoryConfig.withAdditionalKeyDeserializers(additional)); + } + + /** + * Convenience method for creating a new factory instance with additional + * {@link BeanDeserializerModifier}. + */ + @Override + public final DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier) { + return withConfig(_factoryConfig.withDeserializerModifier(modifier)); + } + + /** + * Convenience method for creating a new factory instance with additional + * {@link AbstractTypeResolver}. + */ + @Override + public final DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver) { + return withConfig(_factoryConfig.withAbstractTypeResolver(resolver)); + } + + /** + * Convenience method for creating a new factory instance with additional + * {@link ValueInstantiators}. + */ + @Override + public final DeserializerFactory withValueInstantiators(ValueInstantiators instantiators) { + return withConfig(_factoryConfig.withValueInstantiators(instantiators)); + } + + /* + /********************************************************** + /* DeserializerFactory impl (partial): type mappings + /********************************************************** + */ + + @Override + public JavaType mapAbstractType(DeserializationConfig config, JavaType type) throws JsonMappingException + { + // first, general mappings + while (true) { + JavaType next = _mapAbstractType2(config, type); + if (next == null) { + return type; + } + // Should not have to worry about cycles; but better verify since they will invariably occur... :-) + // (also: guard against invalid resolution to a non-related type) + Class prevCls = type.getRawClass(); + Class nextCls = next.getRawClass(); + if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) { + throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former"); + } + type = next; + } + } + + /** + * Method that will find abstract type mapping for specified type, doing a single + * lookup through registered abstract type resolvers; will not do recursive lookups. + */ + private JavaType _mapAbstractType2(DeserializationConfig config, JavaType type) + throws JsonMappingException + { + Class currClass = type.getRawClass(); + if (_factoryConfig.hasAbstractTypeResolvers()) { + for (AbstractTypeResolver resolver : _factoryConfig.abstractTypeResolvers()) { + JavaType concrete = resolver.findTypeMapping(config, type); + if (concrete != null && concrete.getRawClass() != currClass) { + return concrete; + } + } + } + return null; + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl (partial): ValueInstantiators + /********************************************************** + */ + + /** + * Value instantiator is created both based on creator annotations, + * and on optional externally provided instantiators (registered through + * module interface). + */ + @Override + public ValueInstantiator findValueInstantiator(DeserializationContext ctxt, + BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + + ValueInstantiator instantiator = null; + // [JACKSON-633] Check @JsonValueInstantiator before anything else + AnnotatedClass ac = beanDesc.getClassInfo(); + Object instDef = ctxt.getAnnotationIntrospector().findValueInstantiator(ac); + if (instDef != null) { + instantiator = _valueInstantiatorInstance(config, ac, instDef); + } + if (instantiator == null) { + /* Second: see if some of standard Jackson/JDK types might provide value + * instantiators. + */ + instantiator = _findStdValueInstantiator(config, beanDesc); + if (instantiator == null) { + instantiator = _constructDefaultValueInstantiator(ctxt, beanDesc); + } + } + + // finally: anyone want to modify ValueInstantiator? + if (_factoryConfig.hasValueInstantiators()) { + for (ValueInstantiators insts : _factoryConfig.valueInstantiators()) { + instantiator = insts.findValueInstantiator(config, beanDesc, instantiator); + // let's do sanity check; easier to spot buggy handlers + if (instantiator == null) { + throw JsonMappingException.from(ctxt.getParser(), + "Broken registered ValueInstantiators (of type "+insts.getClass().getName()+"): returned null ValueInstantiator"); + } + } + } + + // Sanity check: does the chosen instantatior have incomplete creators? + if (instantiator.getIncompleteParameter() != null) { + final AnnotatedParameter nonAnnotatedParam = instantiator.getIncompleteParameter(); + final AnnotatedWithParams ctor = nonAnnotatedParam.getOwner(); + throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex()+" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator"); + } + + return instantiator; + } + + private ValueInstantiator _findStdValueInstantiator(DeserializationConfig config, + BeanDescription beanDesc) + throws JsonMappingException + { + if (beanDesc.getBeanClass() == JsonLocation.class) { + return new JsonLocationInstantiator(); + } + return null; + } + + /** + * Method that will construct standard default {@link ValueInstantiator} + * using annotations (like @JsonCreator) and visibility rules + */ + protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationContext ctxt, + BeanDescription beanDesc) + throws JsonMappingException + { + CreatorCollector creators = new CreatorCollector(beanDesc, ctxt.getConfig()); + AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + + // need to construct suitable visibility checker: + final DeserializationConfig config = ctxt.getConfig(); + VisibilityChecker vchecker = config.getDefaultVisibilityChecker(); + vchecker = intr.findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker); + + /* 24-Sep-2014, tatu: Tricky part first; need to merge resolved property information + * (which has creator parameters sprinkled around) with actual creator + * declarations (which are needed to access creator annotation, amongst other things). + * Easiest to combine that info first, then pass it to remaining processing. + */ + /* 15-Mar-2015, tatu: Alas, this won't help with constructors that only have implicit + * names. Those will need to be resolved later on. + */ + Map creatorDefs = _findCreatorsFromProperties(ctxt, + beanDesc); + + /* Important: first add factory methods; then constructors, so + * latter can override former! + */ + _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators, creatorDefs); + // constructors only usable on concrete types: + if (beanDesc.getType().isConcrete()) { + _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators, creatorDefs); + } + return creators.constructValueInstantiator(config); + } + + protected Map _findCreatorsFromProperties(DeserializationContext ctxt, + BeanDescription beanDesc) throws JsonMappingException + { + Map result = Collections.emptyMap(); + for (BeanPropertyDefinition propDef : beanDesc.findProperties()) { + Iterator it = propDef.getConstructorParameters(); + while (it.hasNext()) { + AnnotatedParameter param = it.next(); + AnnotatedWithParams owner = param.getOwner(); + BeanPropertyDefinition[] defs = result.get(owner); + final int index = param.getIndex(); + + if (defs == null) { + if (result.isEmpty()) { // since emptyMap is immutable need to create a 'real' one + result = new LinkedHashMap(); + } + defs = new BeanPropertyDefinition[owner.getParameterCount()]; + result.put(owner, defs); + } else { + if (defs[index] != null) { + throw new IllegalStateException("Conflict: parameter #"+index+" of "+owner + +" bound to more than one property; "+defs[index]+" vs "+propDef); + } + } + defs[index] = propDef; + } + } + return result; + } + + public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config, + Annotated annotated, Object instDef) + throws JsonMappingException + { + if (instDef == null) { + return null; + } + + ValueInstantiator inst; + + if (instDef instanceof ValueInstantiator) { + return (ValueInstantiator) instDef; + } + if (!(instDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type " + +instDef.getClass().getName() + +"; expected type KeyDeserializer or Class instead"); + } + Class instClass = (Class)instDef; + if (ClassUtil.isBogusClass(instClass)) { + return null; + } + if (!ValueInstantiator.class.isAssignableFrom(instClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class "+instClass.getName() + +"; expected Class"); + } + HandlerInstantiator hi = config.getHandlerInstantiator(); + if (hi != null) { + inst = hi.valueInstantiatorInstance(config, annotated, instClass); + if (inst != null) { + return inst; + } + } + return (ValueInstantiator) ClassUtil.createInstance(instClass, + config.canOverrideAccessModifiers()); + } + + protected void _addDeserializerConstructors + (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + Map creatorParams) + throws JsonMappingException + { + // First things first: the "default constructor" (zero-arg + // constructor; whether implicit or explicit) is NOT included + // in list of constructors, so needs to be handled separately. + AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor(); + if (defaultCtor != null) { + if (!creators.hasDefaultCreator() || intr.hasCreatorAnnotation(defaultCtor)) { + creators.setDefaultCreator(defaultCtor); + } + } + + // may need to keep track for [#725] + List implicitCtors = null; + for (AnnotatedConstructor ctor : beanDesc.getConstructors()) { + final boolean isCreator = intr.hasCreatorAnnotation(ctor); + BeanPropertyDefinition[] propDefs = creatorParams.get(ctor); + final int argCount = ctor.getParameterCount(); + + // some single-arg factory methods (String, number) are auto-detected + if (argCount == 1) { + BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0]; + boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, argDef); + + if (useProps) { + SettableBeanProperty[] properties = new SettableBeanProperty[1]; + PropertyName name = (argDef == null) ? null : argDef.getFullName(); + AnnotatedParameter arg = ctor.getParameter(0); + properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, arg, + intr.findInjectableValueId(arg)); + creators.addPropertyCreator(ctor, isCreator, properties); + } else { + /*boolean added = */ _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators, + ctor, isCreator, + vchecker.isCreatorVisible(ctor)); + // one more thing: sever link to creator property, to avoid possible later + // problems with "unresolved" constructor property + if (argDef != null) { + ((POJOPropertyBuilder) argDef).removeConstructors(); + } + } + // regardless, fully handled + continue; + } + + // 2 or more args; all params must have names or be injectable + // 14-Mar-2015, tatu (2.6): Or, as per [#725], implicit names will also + // do, with some constraints. But that will require bit post processing... + + AnnotatedParameter nonAnnotatedParam = null; + SettableBeanProperty[] properties = new SettableBeanProperty[argCount]; + int explicitNameCount = 0; + int implicitWithCreatorCount = 0; + int injectCount = 0; + + for (int i = 0; i < argCount; ++i) { + final AnnotatedParameter param = ctor.getParameter(i); + BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i]; + Object injectId = intr.findInjectableValueId(param); + final PropertyName name = (propDef == null) ? null : propDef.getFullName(); + + if (propDef != null && propDef.isExplicitlyNamed()) { + ++explicitNameCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId); + continue; + } + if (injectId != null) { + ++injectCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId); + continue; + } + NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param); + if (unwrapper != null) { + properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null); + ++explicitNameCount; + continue; + } + // One more thing: implicit names are ok iff ctor has creator annotation + if (isCreator && (name != null && !name.isEmpty())) { + ++implicitWithCreatorCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId); + continue; + } + if (nonAnnotatedParam == null) { + nonAnnotatedParam = param; + } + } + + final int namedCount = explicitNameCount + implicitWithCreatorCount; + // Ok: if named or injectable, we have more work to do + if (isCreator || (explicitNameCount > 0) || (injectCount > 0)) { + // simple case; everything covered: + if ((namedCount + injectCount) == argCount) { + creators.addPropertyCreator(ctor, isCreator, properties); + continue; + } + if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) { + // Secondary: all but one injectable, one un-annotated (un-named) + creators.addDelegatingCreator(ctor, isCreator, properties); + continue; + } + // otherwise, epic fail? + // 16-Mar-2015, tatu: due to [#725], need to be more permissive. For now let's + // only report problem if there's no implicit name + PropertyName impl = _findImplicitParamName(nonAnnotatedParam, intr); + if (impl == null || impl.isEmpty()) { + // Let's consider non-static inner class as a special case... + int ix = nonAnnotatedParam.getIndex(); + if ((ix == 0) && ClassUtil.isNonStaticInnerClass(ctor.getDeclaringClass())) { + throw new IllegalArgumentException("Non-static inner classes like " + +ctor.getDeclaringClass().getName()+" can not use @JsonCreator for constructors"); + } + throw new IllegalArgumentException("Argument #"+ix + +" of constructor "+ctor+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator"); + } + } + // [#725]: as a fallback, all-implicit names may work as well + if (!creators.hasDefaultCreator()) { + if (implicitCtors == null) { + implicitCtors = new LinkedList(); + } + implicitCtors.add(ctor); + } + } + // last option, as per [#725]: consider implicit-names-only, visible constructor, + // if just one found + if ((implicitCtors != null) && !creators.hasDelegatingCreator() + && !creators.hasPropertyBasedCreator()) { + _checkImplicitlyNamedConstructors(ctxt, beanDesc, vchecker, intr, + creators, implicitCtors); + } + } + + protected void _checkImplicitlyNamedConstructors(DeserializationContext ctxt, + BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + List implicitCtors) throws JsonMappingException + { + AnnotatedConstructor found = null; + SettableBeanProperty[] foundProps = null; + + // Further checks: (a) must have names for all parameters, (b) only one visible + // Also, since earlier matching of properties and creators relied on existence of + // `@JsonCreator` (or equivalent) annotation, we need to do bit more re-inspection... + + main_loop: + for (AnnotatedConstructor ctor : implicitCtors) { + if (!vchecker.isCreatorVisible(ctor)) { + continue; + } + // as per earlier notes, only end up here if no properties associated with creator + final int argCount = ctor.getParameterCount(); + SettableBeanProperty[] properties = new SettableBeanProperty[argCount]; + for (int i = 0; i < argCount; ++i) { + final AnnotatedParameter param = ctor.getParameter(i); + final PropertyName name = _findParamName(param, intr); + + // must have name (implicit fine) + if (name == null || name.isEmpty()) { + continue main_loop; + } + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, param.getIndex(), + param, /*injectId*/ null); + } + if (found != null) { // only one allowed + found = null; + break; + } + found = ctor; + foundProps = properties; + } + // found one and only one visible? Ship it! + if (found != null) { + creators.addPropertyCreator(found, /*isCreator*/ false, foundProps); + BasicBeanDescription bbd = (BasicBeanDescription) beanDesc; + // Also: add properties, to keep error messages complete wrt known properties... + for (SettableBeanProperty prop : foundProps) { + PropertyName pn = prop.getFullName(); + if (!bbd.hasProperty(pn)) { + BeanPropertyDefinition newDef = SimpleBeanPropertyDefinition.construct( + ctxt.getConfig(), prop.getMember(), pn); + bbd.addProperty(newDef); + } + } + } + } + + protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr, + AnnotatedWithParams creator, BeanPropertyDefinition propDef) + { + JsonCreator.Mode mode = intr.findCreatorBinding(creator); + + if (mode == JsonCreator.Mode.PROPERTIES) { + return true; + } + if (mode == JsonCreator.Mode.DELEGATING) { + return false; + } + // If explicit name, or inject id, property-based + if (((propDef != null) && propDef.isExplicitlyNamed()) + || (intr.findInjectableValueId(creator.getParameter(0)) != null)) { + return true; + } + if (propDef != null) { + // One more thing: if implicit name matches property with a getter + // or field, we'll consider it property-based as well + String implName = propDef.getName(); + if (implName != null && !implName.isEmpty()) { + if (propDef.couldSerialize()) { + return true; + } + } + } + // in absence of everything else, default to delegating + return false; + } + + protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt, + BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + AnnotatedConstructor ctor, boolean isCreator, boolean isVisible) + throws JsonMappingException + { + // otherwise either 'simple' number, String, or general delegate: + Class type = ctor.getRawParameterType(0); + if (type == String.class || type == CharSequence.class) { + if (isCreator || isVisible) { + creators.addStringCreator(ctor, isCreator); + } + return true; + } + if (type == int.class || type == Integer.class) { + if (isCreator || isVisible) { + creators.addIntCreator(ctor, isCreator); + } + return true; + } + if (type == long.class || type == Long.class) { + if (isCreator || isVisible) { + creators.addLongCreator(ctor, isCreator); + } + return true; + } + if (type == double.class || type == Double.class) { + if (isCreator || isVisible) { + creators.addDoubleCreator(ctor, isCreator); + } + return true; + } + if (type == boolean.class || type == Boolean.class) { + if (isCreator || isVisible) { + creators.addBooleanCreator(ctor, isCreator); + } + return true; + } + // Delegating Creator ok iff it has @JsonCreator (etc) + if (isCreator) { + creators.addDelegatingCreator(ctor, isCreator, null); + return true; + } + return false; + } + + protected void _addDeserializerFactoryMethods + (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + Map creatorParams) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) { + final boolean isCreator = intr.hasCreatorAnnotation(factory); + final int argCount = factory.getParameterCount(); + // zero-arg methods must be annotated; if so, are "default creators" [JACKSON-850] + if (argCount == 0) { + if (isCreator) { + creators.setDefaultCreator(factory); + } + continue; + } + + final BeanPropertyDefinition[] propDefs = creatorParams.get(factory); + // some single-arg factory methods (String, number) are auto-detected + if (argCount == 1) { + BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0]; + boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef); + if (!useProps) { // not property based but delegating + /*boolean added=*/ _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators, + factory, isCreator); + // otherwise just ignored + continue; + } + // fall through if there's name + } else { + // more than 2 args, must have @JsonCreator + if (!isCreator) { + continue; + } + } + // 1 or more args; all params must have name annotations + AnnotatedParameter nonAnnotatedParam = null; + SettableBeanProperty[] properties = new SettableBeanProperty[argCount]; + int implicitNameCount = 0; + int explicitNameCount = 0; + int injectCount = 0; + + for (int i = 0; i < argCount; ++i) { + final AnnotatedParameter param = factory.getParameter(i); + BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[i]; + Object injectId = intr.findInjectableValueId(param); + final PropertyName name = (propDef == null) ? null : propDef.getFullName(); + + if (propDef != null && propDef.isExplicitlyNamed()) { + ++explicitNameCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId); + continue; + } + if (injectId != null) { + ++injectCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId); + continue; + } + NameTransformer unwrapper = intr.findUnwrappingNameTransformer(param); + if (unwrapper != null) { + properties[i] = constructCreatorProperty(ctxt, beanDesc, UNWRAPPED_CREATOR_PARAM_NAME, i, param, null); + ++implicitNameCount; + continue; + } + // One more thing: implicit names are ok iff ctor has creator annotation + if (isCreator) { + if (name != null && !name.isEmpty()) { + ++implicitNameCount; + properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId); + continue; + } + } + /* 25-Sep-2014, tatu: Actually, we may end up "losing" naming due to higher-priority constructor + * (see TestCreators#testConstructorCreator() test). And just to avoid running into that problem, + * let's add one more work around + */ + /* + PropertyName name2 = _findExplicitParamName(param, intr); + if (name2 != null && !name2.isEmpty()) { + // Hmmh. Ok, fine. So what are we to do with it... ? + // For now... skip. May need to revisit this, should this become problematic + continue main_loop; + } + */ + if (nonAnnotatedParam == null) { + nonAnnotatedParam = param; + } + } + final int namedCount = explicitNameCount + implicitNameCount; + + // Ok: if named or injectable, we have more work to do + if (isCreator || explicitNameCount > 0 || injectCount > 0) { + // simple case; everything covered: + if ((namedCount + injectCount) == argCount) { + creators.addPropertyCreator(factory, isCreator, properties); + } else if ((explicitNameCount == 0) && ((injectCount + 1) == argCount)) { + // [712] secondary: all but one injectable, one un-annotated (un-named) + creators.addDelegatingCreator(factory, isCreator, properties); + } else { // otherwise, epic fail + throw new IllegalArgumentException("Argument #"+nonAnnotatedParam.getIndex() + +" of factory method "+factory+" has no property name annotation; must have name when multiple-parameter constructor annotated as Creator"); + } + } + } + } + + protected boolean _handleSingleArgumentFactory(DeserializationConfig config, + BeanDescription beanDesc, VisibilityChecker vchecker, + AnnotationIntrospector intr, CreatorCollector creators, + AnnotatedMethod factory, boolean isCreator) + throws JsonMappingException + { + Class type = factory.getRawParameterType(0); + + if (type == String.class || type == CharSequence.class) { + if (isCreator || vchecker.isCreatorVisible(factory)) { + creators.addStringCreator(factory, isCreator); + } + return true; + } + if (type == int.class || type == Integer.class) { + if (isCreator || vchecker.isCreatorVisible(factory)) { + creators.addIntCreator(factory, isCreator); + } + return true; + } + if (type == long.class || type == Long.class) { + if (isCreator || vchecker.isCreatorVisible(factory)) { + creators.addLongCreator(factory, isCreator); + } + return true; + } + if (type == double.class || type == Double.class) { + if (isCreator || vchecker.isCreatorVisible(factory)) { + creators.addDoubleCreator(factory, isCreator); + } + return true; + } + if (type == boolean.class || type == Boolean.class) { + if (isCreator || vchecker.isCreatorVisible(factory)) { + creators.addBooleanCreator(factory, isCreator); + } + return true; + } + if (isCreator) { + creators.addDelegatingCreator(factory, isCreator, null); + return true; + } + return false; + } + + /** + * Method that will construct a property object that represents + * a logical property passed via Creator (constructor or static + * factory method) + */ + protected SettableBeanProperty constructCreatorProperty(DeserializationContext ctxt, + BeanDescription beanDesc, PropertyName name, int index, + AnnotatedParameter param, + Object injectableValueId) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + PropertyMetadata metadata; + { + if (intr == null) { + metadata = PropertyMetadata.STD_REQUIRED_OR_OPTIONAL; + } else { + Boolean b = intr.hasRequiredMarker(param); + boolean req = (b != null && b.booleanValue()); + String desc = intr.findPropertyDescription(param); + Integer idx = intr.findPropertyIndex(param); + String def = intr.findPropertyDefaultValue(param); + metadata = PropertyMetadata.construct(req, desc, idx, def); + } + } + // 15-Oct-2015, tatu: Not 100% if context needed; removing it does not make any + // existing unit tests fail. Still seems like the right thing to do. + JavaType t0 = beanDesc.resolveType(param.getParameterType()); + BeanProperty.Std property = new BeanProperty.Std(name, t0, + intr.findWrapperName(param), + beanDesc.getClassAnnotations(), param, metadata); + JavaType type = resolveType(ctxt, beanDesc, t0, param); + if (type != t0) { + property = property.withType(type); + } + // Is there an annotation that specifies exact deserializer? + JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, param); + + // If yes, we are mostly done: + type = modifyTypeByAnnotation(ctxt, param, type); + + // Type deserializer: either comes from property (and already resolved) + TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler(); + // or if not, based on type being referenced: + if (typeDeser == null) { + typeDeser = findTypeDeserializer(config, type); + } + // Note: contextualization of typeDeser _should_ occur in constructor of CreatorProperty + // so it is not called directly here + SettableBeanProperty prop = new CreatorProperty(name, type, property.getWrapperName(), + typeDeser, beanDesc.getClassAnnotations(), param, index, injectableValueId, + metadata); + if (deser != null) { + // As per [Issue#462] need to ensure we contextualize deserializer before passing it on + deser = ctxt.handlePrimaryContextualization(deser, prop, type); + prop = prop.withValueDeserializer(deser); + } + return prop; + } + + protected PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospector intr) + { + if (param != null && intr != null) { + PropertyName name = intr.findNameForDeserialization(param); + if (name != null) { + return name; + } + // 14-Apr-2014, tatu: Need to also consider possible implicit name + // (for JDK8, or via paranamer) + + String str = intr.findImplicitPropertyName(param); + if (str != null && !str.isEmpty()) { + return PropertyName.construct(str); + } + } + return null; + } + + protected PropertyName _findImplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr) + { + String str = intr.findImplicitPropertyName(param); + if (str != null && !str.isEmpty()) { + return PropertyName.construct(str); + } + return null; + } + + @Deprecated // in 2.6, remove from 2.7 + protected PropertyName _findExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr) + { + if (param != null && intr != null) { + return intr.findNameForDeserialization(param); + } + return null; + } + + @Deprecated // in 2.6, remove from 2.7 + protected boolean _hasExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr) + { + if (param != null && intr != null) { + PropertyName n = intr.findNameForDeserialization(param); + return (n != null) && n.hasSimpleName(); + } + return false; + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl: array deserializers + /********************************************************** + */ + + @Override + public JsonDeserializer createArrayDeserializer(DeserializationContext ctxt, + ArrayType type, final BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + JavaType elemType = type.getContentType(); + + // Very first thing: is deserializer hard-coded for elements? + JsonDeserializer contentDeser = elemType.getValueHandler(); + // Then optional type info: if type has been resolved, we may already know type deserializer: + TypeDeserializer elemTypeDeser = elemType.getTypeHandler(); + // but if not, may still be possible to find: + if (elemTypeDeser == null) { + elemTypeDeser = findTypeDeserializer(config, elemType); + } + // 23-Nov-2010, tatu: Custom array deserializer? + JsonDeserializer deser = _findCustomArrayDeserializer(type, + config, beanDesc, elemTypeDeser, contentDeser); + if (deser == null) { + if (contentDeser == null) { + Class raw = elemType.getRawClass(); + if (elemType.isPrimitive()) { + return PrimitiveArrayDeserializers.forType(raw); + } else if (raw == String.class) { + return StringArrayDeserializer.instance; + } + } + deser = new ObjectArrayDeserializer(type, contentDeser, elemTypeDeser); + } + // and then new with 2.2: ability to post-process it too (Issue#120) + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyArrayDeserializer(config, type, beanDesc, deser); + } + } + return deser; + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl: Collection(-like) deserializers + /********************************************************** + */ + + @Override + public JsonDeserializer createCollectionDeserializer(DeserializationContext ctxt, + CollectionType type, BeanDescription beanDesc) + throws JsonMappingException + { + JavaType contentType = type.getContentType(); + // Very first thing: is deserializer hard-coded for elements? + JsonDeserializer contentDeser = contentType.getValueHandler(); + final DeserializationConfig config = ctxt.getConfig(); + + // Then optional type info: if type has been resolved, we may already know type deserializer: + TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); + // but if not, may still be possible to find: + if (contentTypeDeser == null) { + contentTypeDeser = findTypeDeserializer(config, contentType); + } + // 23-Nov-2010, tatu: Custom deserializer? + JsonDeserializer deser = _findCustomCollectionDeserializer(type, + config, beanDesc, contentTypeDeser, contentDeser); + if (deser == null) { + Class collectionClass = type.getRawClass(); + if (contentDeser == null) { // not defined by annotation + // One special type: EnumSet: + if (EnumSet.class.isAssignableFrom(collectionClass)) { + deser = new EnumSetDeserializer(contentType, null); + } + } + } + + /* One twist: if we are being asked to instantiate an interface or + * abstract Collection, we need to either find something that implements + * the thing, or give up. + * + * Note that we do NOT try to guess based on secondary interfaces + * here; that would probably not work correctly since casts would + * fail later on (as the primary type is not the interface we'd + * be implementing) + */ + if (deser == null) { + if (type.isInterface() || type.isAbstract()) { + CollectionType implType = _mapAbstractCollectionType(type, config); + if (implType == null) { + // [databind#292]: Actually, may be fine, but only if polymorphich deser enabled + if (type.getTypeHandler() == null) { + throw new IllegalArgumentException("Can not find a deserializer for non-concrete Collection type "+type); + } + deser = AbstractDeserializer.constructForNonPOJO(beanDesc); + } else { + type = implType; + // But if so, also need to re-check creators... + beanDesc = config.introspectForCreation(type); + } + } + if (deser == null) { + ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc); + if (!inst.canCreateUsingDefault()) { + // [databind#161]: No default constructor for ArrayBlockingQueue... + if (type.getRawClass() == ArrayBlockingQueue.class) { + return new ArrayBlockingQueueDeserializer(type, contentDeser, contentTypeDeser, inst); + } + } + // Can use more optimal deserializer if content type is String, so: + if (contentType.getRawClass() == String.class) { + // no value type deserializer because Strings are one of natural/native types: + deser = new StringCollectionDeserializer(type, contentDeser, inst); + } else { + deser = new CollectionDeserializer(type, contentDeser, contentTypeDeser, inst); + } + } + } + // allow post-processing it too + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyCollectionDeserializer(config, type, beanDesc, deser); + } + } + return deser; + } + + protected CollectionType _mapAbstractCollectionType(JavaType type, DeserializationConfig config) + { + Class collectionClass = type.getRawClass(); + collectionClass = _collectionFallbacks.get(collectionClass.getName()); + if (collectionClass == null) { + return null; + } + return (CollectionType) config.constructSpecializedType(type, collectionClass); + } + + // Copied almost verbatim from "createCollectionDeserializer" -- should try to share more code + @Override + public JsonDeserializer createCollectionLikeDeserializer(DeserializationContext ctxt, + CollectionLikeType type, final BeanDescription beanDesc) + throws JsonMappingException + { + JavaType contentType = type.getContentType(); + // Very first thing: is deserializer hard-coded for elements? + JsonDeserializer contentDeser = contentType.getValueHandler(); + final DeserializationConfig config = ctxt.getConfig(); + + // Then optional type info (1.5): if type has been resolved, we may already know type deserializer: + TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); + // but if not, may still be possible to find: + if (contentTypeDeser == null) { + contentTypeDeser = findTypeDeserializer(config, contentType); + } + JsonDeserializer deser = _findCustomCollectionLikeDeserializer(type, config, beanDesc, + contentTypeDeser, contentDeser); + if (deser != null) { + // and then new with 2.2: ability to post-process it too (Issue#120) + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyCollectionLikeDeserializer(config, type, beanDesc, deser); + } + } + } + return deser; + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl: Map(-like) deserializers + /********************************************************** + */ + + @Override + public JsonDeserializer createMapDeserializer(DeserializationContext ctxt, + MapType type, BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + JavaType keyType = type.getKeyType(); + JavaType contentType = type.getContentType(); + + // First: is there annotation-specified deserializer for values? + @SuppressWarnings("unchecked") + JsonDeserializer contentDeser = (JsonDeserializer) contentType.getValueHandler(); + + // Ok: need a key deserializer (null indicates 'default' here) + KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler(); + // Then optional type info (1.5); either attached to type, or resolved separately: + TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); + // but if not, may still be possible to find: + if (contentTypeDeser == null) { + contentTypeDeser = findTypeDeserializer(config, contentType); + } + + // 23-Nov-2010, tatu: Custom deserializer? + JsonDeserializer deser = _findCustomMapDeserializer(type, config, beanDesc, + keyDes, contentTypeDeser, contentDeser); + + if (deser == null) { + // Value handling is identical for all, but EnumMap requires special handling for keys + Class mapClass = type.getRawClass(); + if (EnumMap.class.isAssignableFrom(mapClass)) { + Class kt = keyType.getRawClass(); + if (kt == null || !kt.isEnum()) { + throw new IllegalArgumentException("Can not construct EnumMap; generic (key) type not available"); + } + deser = new EnumMapDeserializer(type, null, contentDeser, contentTypeDeser); + } + + // Otherwise, generic handler works ok. + + /* But there is one more twist: if we are being asked to instantiate + * an interface or abstract Map, we need to either find something + * that implements the thing, or give up. + * + * Note that we do NOT try to guess based on secondary interfaces + * here; that would probably not work correctly since casts would + * fail later on (as the primary type is not the interface we'd + * be implementing) + */ + if (deser == null) { + if (type.isInterface() || type.isAbstract()) { + @SuppressWarnings("rawtypes") + Class fallback = _mapFallbacks.get(mapClass.getName()); + if (fallback != null) { + mapClass = fallback; + type = (MapType) config.constructSpecializedType(type, mapClass); + // But if so, also need to re-check creators... + beanDesc = config.introspectForCreation(type); + } else { + // [Issue#292]: Actually, may be fine, but only if polymorphich deser enabled + if (type.getTypeHandler() == null) { + throw new IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+type); + } + deser = AbstractDeserializer.constructForNonPOJO(beanDesc); + } + } + if (deser == null) { + ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc); + MapDeserializer md = new MapDeserializer(type, inst, keyDes, contentDeser, contentTypeDeser); + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + md.setIgnorableProperties(ai.findPropertiesToIgnore(beanDesc.getClassInfo(), false)); + deser = md; + } + } + } + // and then new with 2.2: ability to post-process it too (Issue#120) + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyMapDeserializer(config, type, beanDesc, deser); + } + } + return deser; + } + + // Copied almost verbatim from "createMapDeserializer" -- should try to share more code + @Override + public JsonDeserializer createMapLikeDeserializer(DeserializationContext ctxt, + MapLikeType type, final BeanDescription beanDesc) + throws JsonMappingException + { + JavaType keyType = type.getKeyType(); + JavaType contentType = type.getContentType(); + final DeserializationConfig config = ctxt.getConfig(); + + // First: is there annotation-specified deserializer for values? + @SuppressWarnings("unchecked") + JsonDeserializer contentDeser = (JsonDeserializer) contentType.getValueHandler(); + + // Ok: need a key deserializer (null indicates 'default' here) + KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler(); + /* !!! 24-Jan-2012, tatu: NOTE: impls MUST use resolve() to find key deserializer! + if (keyDes == null) { + keyDes = p.findKeyDeserializer(config, keyType, property); + } + */ + // Then optional type info (1.5); either attached to type, or resolve separately: + TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); + // but if not, may still be possible to find: + if (contentTypeDeser == null) { + contentTypeDeser = findTypeDeserializer(config, contentType); + } + JsonDeserializer deser = _findCustomMapLikeDeserializer(type, config, + beanDesc, keyDes, contentTypeDeser, contentDeser); + if (deser != null) { + // and then new with 2.2: ability to post-process it too (Issue#120) + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyMapLikeDeserializer(config, type, beanDesc, deser); + } + } + } + return deser; + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl: other types + /********************************************************** + */ + + /** + * Factory method for constructing serializers of {@link Enum} types. + */ + @Override + public JsonDeserializer createEnumDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + final Class enumClass = type.getRawClass(); + // 23-Nov-2010, tatu: Custom deserializer? + JsonDeserializer deser = _findCustomEnumDeserializer(enumClass, config, beanDesc); + if (deser == null) { + // May have @JsonCreator for static factory method: + for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) { + if (ctxt.getAnnotationIntrospector().hasCreatorAnnotation(factory)) { + int argCount = factory.getParameterCount(); + if (argCount == 1) { + Class returnType = factory.getRawReturnType(); + // usually should be class, but may be just plain Enum (for Enum.valueOf()?) + if (returnType.isAssignableFrom(enumClass)) { + deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory); + break; + } + } + throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type " + +enumClass.getName()+")"); + } + } + // Need to consider @JsonValue if one found + if (deser == null) { + deser = new EnumDeserializer(constructEnumResolver(enumClass, + config, beanDesc.findJsonValueMethod())); + } + } + + // and then post-process it too + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyEnumDeserializer(config, type, beanDesc, deser); + } + } + return deser; + } + + @Override + public JsonDeserializer createTreeDeserializer(DeserializationConfig config, + JavaType nodeType, BeanDescription beanDesc) + throws JsonMappingException + { + @SuppressWarnings("unchecked") + Class nodeClass = (Class) nodeType.getRawClass(); + // 23-Nov-2010, tatu: Custom deserializer? + JsonDeserializer custom = _findCustomTreeNodeDeserializer(nodeClass, config, + beanDesc); + if (custom != null) { + return custom; + } + return JsonNodeDeserializer.getDeserializer(nodeClass); + } + + @Override + public JsonDeserializer createReferenceDeserializer(DeserializationContext ctxt, + ReferenceType type, BeanDescription beanDesc) + throws JsonMappingException + { + JavaType contentType = type.getContentType(); + // Very first thing: is deserializer hard-coded for elements? + JsonDeserializer contentDeser = contentType.getValueHandler(); + final DeserializationConfig config = ctxt.getConfig(); + // Then optional type info: if type has been resolved, we may already know type deserializer: + TypeDeserializer contentTypeDeser = contentType.getTypeHandler(); + if (contentTypeDeser == null) { // or if not, may be able to find: + contentTypeDeser = findTypeDeserializer(config, contentType); + } + JsonDeserializer deser = _findCustomReferenceDeserializer(type, config, beanDesc, + contentTypeDeser, contentDeser); + + if (deser == null) { + // Just one referential type as of JDK 1.7 / Java 7: AtomicReference (Java 8 adds Optional) + if (AtomicReference.class.isAssignableFrom(type.getRawClass())) { + // 19-Apr-2016, tatu: By default we'd get something that expect to see an + // AtomicReference... but what we need is something else, so... + return new AtomicReferenceDeserializer(contentType, contentTypeDeser, contentDeser); + } + } + if (deser != null) { + // and then post-process + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyReferenceDeserializer(config, type, beanDesc, deser); + } + } + } + return deser; + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl (partial): type deserializers + /********************************************************** + */ + + @Override + public TypeDeserializer findTypeDeserializer(DeserializationConfig config, + JavaType baseType) + throws JsonMappingException + { + BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass()); + AnnotatedClass ac = bean.getClassInfo(); + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + TypeResolverBuilder b = ai.findTypeResolver(config, ac, baseType); + + /* Ok: if there is no explicit type info handler, we may want to + * use a default. If so, config object knows what to use. + */ + Collection subtypes = null; + if (b == null) { + b = config.getDefaultTyper(baseType); + if (b == null) { + return null; + } + } else { + subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac); + } + // May need to figure out default implementation, if none found yet + // (note: check for abstract type is not 100% mandatory, more of an optimization) + if ((b.getDefaultImpl() == null) && baseType.isAbstract()) { + JavaType defaultType = mapAbstractType(config, baseType); + if (defaultType != null && defaultType.getRawClass() != baseType.getRawClass()) { + b = b.defaultImpl(defaultType.getRawClass()); + } + } + return b.buildTypeDeserializer(config, baseType, subtypes); + } + + /** + * Overridable method called after checking all other types. + * + * @since 2.2 + */ + protected JsonDeserializer findOptionalStdDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + return OptionalHandlerFactory.instance.findDeserializer(type, ctxt.getConfig(), beanDesc); + } + + /* + /********************************************************** + /* JsonDeserializerFactory impl (partial): key deserializers + /********************************************************** + */ + + @Override + public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt, + JavaType type) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + KeyDeserializer deser = null; + if (_factoryConfig.hasKeyDeserializers()) { + BeanDescription beanDesc = config.introspectClassAnnotations(type.getRawClass()); + for (KeyDeserializers d : _factoryConfig.keyDeserializers()) { + deser = d.findKeyDeserializer(type, config, beanDesc); + if (deser != null) { + break; + } + } + } + // the only non-standard thing is this: + if (deser == null) { + if (type.isEnumType()) { + return _createEnumKeyDeserializer(ctxt, type); + } + deser = StdKeyDeserializers.findStringBasedKeyDeserializer(config, type); + } + + // and then new with 2.2: ability to post-process it too (Issue#120) + if (deser != null) { + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyKeyDeserializer(config, type, deser); + } + } + } + return deser; + } + + private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt, + JavaType type) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + Class enumClass = type.getRawClass(); + + BeanDescription beanDesc = config.introspect(type); + // 24-Sep-2015, bim: a key deserializer is the preferred thing. + KeyDeserializer des = findKeyDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo()); + if (des != null) { + return des; + } else { + // 24-Sep-2015, bim: if no key deser, look for enum deserializer first, then a plain deser. + JsonDeserializer custom = _findCustomEnumDeserializer(enumClass, config, beanDesc); + if (custom != null) { + return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, custom); + } + JsonDeserializer valueDesForKey = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo()); + if (valueDesForKey != null) { + return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, valueDesForKey); + } + } + EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod()); + // May have @JsonCreator for static factory method: + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) { + if (ai.hasCreatorAnnotation(factory)) { + int argCount = factory.getParameterCount(); + if (argCount == 1) { + Class returnType = factory.getRawReturnType(); + // usually should be class, but may be just plain Enum (for Enum.valueOf()?) + if (returnType.isAssignableFrom(enumClass)) { + // note: mostly copied from 'EnumDeserializer.deserializerForCreator(...)' + if (factory.getRawParameterType(0) != String.class) { + throw new IllegalArgumentException("Parameter #0 type for factory method ("+factory+") not suitable, must be java.lang.String"); + } + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(factory.getMember(), + ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return StdKeyDeserializers.constructEnumKeyDeserializer(enumRes, factory); + } + } + throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type " + +enumClass.getName()+")"); + } + } + // [JACKSON-749] Also, need to consider @JsonValue, if one found + return StdKeyDeserializers.constructEnumKeyDeserializer(enumRes); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Method called to create a type information deserializer for values of + * given non-container property, if one is needed. + * If not needed (no polymorphic handling configured for property), should return null. + *

+ * Note that this method is only called for non-container bean properties, + * and not for values in container types or root values (or container properties) + * + * @param baseType Declared base type of the value to deserializer (actual + * deserializer type will be this type or its subtype) + * + * @return Type deserializer to use for given base type, if one is needed; null if not. + */ + public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig config, + JavaType baseType, AnnotatedMember annotated) + throws JsonMappingException + { + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + TypeResolverBuilder b = ai.findPropertyTypeResolver(config, annotated, baseType); + // Defaulting: if no annotations on member, check value class + if (b == null) { + return findTypeDeserializer(config, baseType); + } + // but if annotations found, may need to resolve subtypes: + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId( + config, annotated, baseType); + return b.buildTypeDeserializer(config, baseType, subtypes); + } + + /** + * Method called to find and create a type information deserializer for values of + * given container (list, array, map) property, if one is needed. + * If not needed (no polymorphic handling configured for property), should return null. + *

+ * Note that this method is only called for container bean properties, + * and not for values in container types or root values (or non-container properties) + * + * @param containerType Type of property; must be a container type + * @param propertyEntity Field or method that contains container property + */ + public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConfig config, + JavaType containerType, AnnotatedMember propertyEntity) + throws JsonMappingException + { + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + TypeResolverBuilder b = ai.findPropertyContentTypeResolver(config, propertyEntity, containerType); + JavaType contentType = containerType.getContentType(); + // Defaulting: if no annotations on member, check class + if (b == null) { + return findTypeDeserializer(config, contentType); + } + // but if annotations found, may need to resolve subtypes: + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId( + config, propertyEntity, contentType); + return b.buildTypeDeserializer(config, contentType, subtypes); + } + + /** + * Helper method called to find one of default serializers for "well-known" + * platform types: JDK-provided types, and small number of public Jackson + * API types. + * + * @since 2.2 + */ + public JsonDeserializer findDefaultDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + Class rawType = type.getRawClass(); + // Object ("untyped"), String equivalents: + if (rawType == CLASS_OBJECT) { + // 11-Feb-2015, tatu: As per [databind#700] need to be careful wrt non-default Map, List. + DeserializationConfig config = ctxt.getConfig(); + JavaType lt, mt; + + if (_factoryConfig.hasAbstractTypeResolvers()) { + lt = _findRemappedType(config, List.class); + mt = _findRemappedType(config, Map.class); + } else { + lt = mt = null; + } + return new UntypedObjectDeserializer(lt, mt); + } + if (rawType == CLASS_STRING || rawType == CLASS_CHAR_BUFFER) { + return StringDeserializer.instance; + } + if (rawType == CLASS_ITERABLE) { + // [Issue#199]: Can and should 'upgrade' to a Collection type: + TypeFactory tf = ctxt.getTypeFactory(); + JavaType[] tps = tf.findTypeParameters(type, CLASS_ITERABLE); + JavaType elemType = (tps == null || tps.length != 1) ? TypeFactory.unknownType() : tps[0]; + CollectionType ct = tf.constructCollectionType(Collection.class, elemType); + // Should we re-introspect beanDesc? For now let's not... + return createCollectionDeserializer(ctxt, ct, beanDesc); + } + if (rawType == CLASS_MAP_ENTRY) { + // 28-Apr-2015, tatu: TypeFactory does it all for us already so + JavaType kt = type.containedType(0); + if (kt == null) { + kt = TypeFactory.unknownType(); + } + JavaType vt = type.containedType(1); + if (vt == null) { + vt = TypeFactory.unknownType(); + } + TypeDeserializer vts = (TypeDeserializer) vt.getTypeHandler(); + if (vts == null) { + vts = findTypeDeserializer(ctxt.getConfig(), vt); + } + JsonDeserializer valueDeser = vt.getValueHandler(); + KeyDeserializer keyDes = (KeyDeserializer) kt.getValueHandler(); + return new MapEntryDeserializer(type, keyDes, valueDeser, vts); + } + String clsName = rawType.getName(); + if (rawType.isPrimitive() || clsName.startsWith("java.")) { + // Primitives/wrappers, other Numbers: + JsonDeserializer deser = NumberDeserializers.find(rawType, clsName); + if (deser == null) { + deser = DateDeserializers.find(rawType, clsName); + } + if (deser != null) { + return deser; + } + } + // and a few Jackson types as well: + if (rawType == TokenBuffer.class) { + return new TokenBufferDeserializer(); + } + JsonDeserializer deser = findOptionalStdDeserializer(ctxt, type, beanDesc); + if (deser != null) { + return deser; + } + return JdkDeserializers.find(rawType, clsName); + } + + protected JavaType _findRemappedType(DeserializationConfig config, Class rawType) throws JsonMappingException { + JavaType type = mapAbstractType(config, config.constructType(rawType)); + return (type == null || type.hasRawClass(rawType)) ? null : type; + } + + /* + /********************************************************** + /* Helper methods, finding custom deserializers + /********************************************************** + */ + + protected JsonDeserializer _findCustomTreeNodeDeserializer(Class type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findTreeNodeDeserializer(type, config, beanDesc); + if (deser != null) { + return deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomReferenceDeserializer(ReferenceType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer contentTypeDeserializer, JsonDeserializer contentDeserializer) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findReferenceDeserializer(type, config, beanDesc, + contentTypeDeserializer, contentDeserializer); + if (deser != null) { + return deser; + } + } + return null; + } + + @SuppressWarnings("unchecked") + protected JsonDeserializer _findCustomBeanDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findBeanDeserializer(type, config, beanDesc); + if (deser != null) { + return (JsonDeserializer) deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomArrayDeserializer(ArrayType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findArrayDeserializer(type, config, + beanDesc, elementTypeDeserializer, elementDeserializer); + if (deser != null) { + return deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomCollectionDeserializer(CollectionType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findCollectionDeserializer(type, config, beanDesc, + elementTypeDeserializer, elementDeserializer); + if (deser != null) { + return deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomCollectionLikeDeserializer(CollectionLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findCollectionLikeDeserializer(type, config, beanDesc, + elementTypeDeserializer, elementDeserializer); + if (deser != null) { + return deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomEnumDeserializer(Class type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findEnumDeserializer(type, config, beanDesc); + if (deser != null) { + return deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomMapDeserializer(MapType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findMapDeserializer(type, config, beanDesc, + keyDeserializer, elementTypeDeserializer, elementDeserializer); + if (deser != null) { + return deser; + } + } + return null; + } + + protected JsonDeserializer _findCustomMapLikeDeserializer(MapLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + for (Deserializers d : _factoryConfig.deserializers()) { + JsonDeserializer deser = d.findMapLikeDeserializer(type, config, beanDesc, + keyDeserializer, elementTypeDeserializer, elementDeserializer); + if (deser != null) { + return deser; + } + } + return null; + } + + /* + /********************************************************** + /* Helper methods, value/content/key type introspection + /********************************************************** + */ + + /** + * Helper method called to check if a class or method + * has annotation that tells which class to use for deserialization. + * Returns null if no such annotation found. + */ + protected JsonDeserializer findDeserializerFromAnnotation(DeserializationContext ctxt, + Annotated ann) + throws JsonMappingException + { + Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann); + if (deserDef == null) { + return null; + } + return ctxt.deserializerInstance(ann, deserDef); + } + + /** + * Helper method called to check if a class or method + * has annotation that tells which class to use for deserialization. + * Returns null if no such annotation found. + */ + protected KeyDeserializer findKeyDeserializerFromAnnotation(DeserializationContext ctxt, + Annotated ann) + throws JsonMappingException + { + Object deserDef = ctxt.getAnnotationIntrospector().findKeyDeserializer(ann); + if (deserDef == null) { + return null; + } + return ctxt.keyDeserializerInstance(ann, deserDef); + } + + /** + * Method called to see if given method has annotations that indicate + * a more specific type than what the argument specifies. + * If annotations are present, they must specify compatible Class; + * instance of which can be assigned using the method. This means + * that the Class has to be raw class of type, or its sub-class + * (or, implementing class if original Class instance is an interface). + * + * @param a Method or field that the type is associated with + * @param type Type of field, or the setter argument + * + * @return Original type if no annotations are present; or a more + * specific type derived from it if type annotation(s) was found + * + * @throws JsonMappingException if invalid annotation is found + */ + @SuppressWarnings({ "unchecked" }) + protected T modifyTypeByAnnotation(DeserializationContext ctxt, + Annotated a, T type) + throws JsonMappingException + { + AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + if (intr == null) { + return type; + } + + // First, deserializers for key/value types? + if (type.isMapLikeType()) { + JavaType keyType = type.getKeyType(); + // 21-Mar-2011, tatu: ... and associated deserializer too (unless already assigned) + // (not 100% why or how, but this does seem to get called more than once, which + // is not good: for now, let's just avoid errors) + if (keyType != null && keyType.getValueHandler() == null) { + Object kdDef = intr.findKeyDeserializer(a); + KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef); + if (kd != null) { + type = (T) ((MapLikeType) type).withKeyValueHandler(kd); + keyType = type.getKeyType(); // just in case it's used below + } + } + } + JavaType contentType = type.getContentType(); + if (contentType != null) { + // ... as well as deserializer for contents: + if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception) + Object cdDef = intr.findContentDeserializer(a); + JsonDeserializer cd = ctxt.deserializerInstance(a, cdDef); + if (cd != null) { + type = (T) type.withContentValueHandler(cd); + } + } + } + // then: type refinement(s)? + type = (T) intr.refineDeserializationType(ctxt.getConfig(), a, type); + return type; + } + + /** + * Helper method used to resolve method return types and field + * types. The main trick here is that the containing bean may + * have type variable binding information (when deserializing + * using generic type passed as type reference), which is + * needed in some cases. + */ + protected JavaType resolveType(DeserializationContext ctxt, + BeanDescription beanDesc, JavaType type, AnnotatedMember member) + throws JsonMappingException + { + AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + if (intr == null) { + return type; + } + + // Also need to handle keyUsing, contentUsing + + if (type.isMapLikeType()) { + JavaType keyType = type.getKeyType(); + if (keyType != null) { + Object kdDef = intr.findKeyDeserializer(member); + KeyDeserializer kd = ctxt.keyDeserializerInstance(member, kdDef); + if (kd != null) { + type = ((MapLikeType) type).withKeyValueHandler(kd); + keyType = type.getKeyType(); // just in case it's used below + } + } + } + + if (type.isContainerType() || type.isReferenceType()) { + Object cdDef = intr.findContentDeserializer(member); + JsonDeserializer cd = ctxt.deserializerInstance(member, cdDef); + if (cd != null) { + type = type.withContentValueHandler(cd); + } + /* 04-Feb-2010, tatu: Need to figure out JAXB annotations that indicate type + * information to use for polymorphic members; and specifically types for + * collection values (contents). + * ... but only applies to members (fields, methods), not classes + */ + if (member instanceof AnnotatedMember) { + TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer( + ctxt.getConfig(), type, (AnnotatedMember) member); + if (contentTypeDeser != null) { + type = type.withContentTypeHandler(contentTypeDeser); + } + } + } + TypeDeserializer valueTypeDeser; + + if (member instanceof AnnotatedMember) { // JAXB allows per-property annotations + valueTypeDeser = findPropertyTypeDeserializer(ctxt.getConfig(), + type, (AnnotatedMember) member); + } else { // classes just have Jackson annotations + // probably only occurs if 'property' is null anyway + valueTypeDeser = findTypeDeserializer(ctxt.getConfig(), type); + } + if (valueTypeDeser != null) { + type = type.withTypeHandler(valueTypeDeser); + } + return type; + } + + protected EnumResolver constructEnumResolver(Class enumClass, + DeserializationConfig config, AnnotatedMethod jsonValueMethod) + { + if (jsonValueMethod != null) { + Method accessor = jsonValueMethod.getAnnotated(); + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(accessor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor); + } + // 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING` + // here, but that won't do: it must be dynamically changeable... + return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector()); + } + + protected AnnotatedMethod _findJsonValueFor(DeserializationConfig config, JavaType enumType) + { + if (enumType == null) { + return null; + } + BeanDescription beanDesc = config.introspect(enumType); + return beanDesc.findJsonValueMethod(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,896 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.impl.*; +import com.fasterxml.jackson.databind.util.NameTransformer; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Deserializer class that can deserialize instances of + * arbitrary bean objects, usually from JSON Object structs, + */ +public class BeanDeserializer + extends BeanDeserializerBase + implements java.io.Serializable +{ + /* TODOs for future versions: + * + * For 2.8? + * + * - New method in JsonDeserializer (deserializeNext()) to allow use of more + * efficient 'nextXxx()' method `JsonParser` provides. + * + * Also: need to ensure efficient impl of those methods for Smile, CBOR + * at least (in addition to JSON) + */ + + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Life-cycle, construction, initialization + /********************************************************** + */ + + /** + * Constructor used by {@link BeanDeserializerBuilder}. + */ + public BeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, + BeanPropertyMap properties, Map backRefs, + HashSet ignorableProps, boolean ignoreAllUnknown, + boolean hasViews) + { + super(builder, beanDesc, properties, backRefs, + ignorableProps, ignoreAllUnknown, hasViews); + } + + /** + * Copy-constructor that can be used by sub-classes to allow + * copy-on-write style copying of settings of an existing instance. + */ + protected BeanDeserializer(BeanDeserializerBase src) { + super(src, src._ignoreAllUnknown); + } + + protected BeanDeserializer(BeanDeserializerBase src, boolean ignoreAllUnknown) { + super(src, ignoreAllUnknown); + } + + protected BeanDeserializer(BeanDeserializerBase src, NameTransformer unwrapper) { + super(src, unwrapper); + } + + public BeanDeserializer(BeanDeserializerBase src, ObjectIdReader oir) { + super(src, oir); + } + + public BeanDeserializer(BeanDeserializerBase src, HashSet ignorableProps) { + super(src, ignorableProps); + } + + @Override + public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + { + /* bit kludgy but we don't want to accidentally change type; sub-classes + * MUST override this method to support unwrapped properties... + */ + if (getClass() != BeanDeserializer.class) { + return this; + } + /* main thing really is to just enforce ignoring of unknown + * properties; since there may be multiple unwrapped values + * and properties for all may be interleaved... + */ + return new BeanDeserializer(this, unwrapper); + } + + @Override + public BeanDeserializer withObjectIdReader(ObjectIdReader oir) { + return new BeanDeserializer(this, oir); + } + + @Override + public BeanDeserializer withIgnorableProperties(HashSet ignorableProps) { + return new BeanDeserializer(this, ignorableProps); + } + + @Override + protected BeanDeserializerBase asArrayDeserializer() { + SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder(); + return new BeanAsArrayDeserializer(this, props); + } + + /* + /********************************************************** + /* JsonDeserializer implementation + /********************************************************** + */ + + /** + * Main deserialization method for bean-based objects (POJOs). + *

+ * NOTE: was declared 'final' in 2.2; should NOT be to let extensions + * like Afterburner change definition. + */ + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // common case first + if (p.isExpectedStartObjectToken()) { + if (_vanillaProcessing) { + return vanillaDeserialize(p, ctxt, p.nextToken()); + } + // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is + // what it is, including "expected behavior". + p.nextToken(); + if (_objectIdReader != null) { + return deserializeWithObjectId(p, ctxt); + } + return deserializeFromObject(p, ctxt); + } + return _deserializeOther(p, ctxt, p.getCurrentToken()); + } + + protected final Object _deserializeOther(JsonParser p, DeserializationContext ctxt, + JsonToken t) throws IOException + { + // and then others, generally requiring use of @JsonCreator + switch (t) { + case VALUE_STRING: + return deserializeFromString(p, ctxt); + case VALUE_NUMBER_INT: + return deserializeFromNumber(p, ctxt); + case VALUE_NUMBER_FLOAT: + return deserializeFromDouble(p, ctxt); + case VALUE_EMBEDDED_OBJECT: + return deserializeFromEmbedded(p, ctxt); + case VALUE_TRUE: + case VALUE_FALSE: + return deserializeFromBoolean(p, ctxt); + + case VALUE_NULL: + return deserializeFromNull(p, ctxt); + case START_ARRAY: + // these only work if there's a (delegating) creator... + return deserializeFromArray(p, ctxt); + case FIELD_NAME: + case END_OBJECT: // added to resolve [JACKSON-319], possible related issues + if (_vanillaProcessing) { + return vanillaDeserialize(p, ctxt, t); + } + if (_objectIdReader != null) { + return deserializeWithObjectId(p, ctxt); + } + return deserializeFromObject(p, ctxt); + default: + } + throw ctxt.mappingException(handledType()); + } + + protected Object _missingToken(JsonParser p, DeserializationContext ctxt) throws IOException { + throw ctxt.endOfInputException(handledType()); + } + + /** + * Secondary deserialization method, called in cases where POJO + * instance is created as part of deserialization, potentially + * after collecting some or all of the properties to set. + */ + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException + { + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + if (_injectables != null) { + injectValues(ctxt, bean); + } + if (_unwrappedPropertyHandler != null) { + return deserializeWithUnwrapped(p, ctxt, bean); + } + if (_externalTypeIdHandler != null) { + return deserializeWithExternalTypeId(p, ctxt, bean); + } + String propName; + + // 23-Mar-2010, tatu: In some cases, we start with full JSON object too... + if (p.isExpectedStartObjectToken()) { + propName = p.nextFieldName(); + if (propName == null) { + return bean; + } + } else { + if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { + propName = p.getCurrentName(); + } else { + return bean; + } + } + if (_needViewProcesing) { + Class view = ctxt.getActiveView(); + if (view != null) { + return deserializeWithView(p, ctxt, bean, view); + } + } + do { + p.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + + if (prop != null) { // normal case + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + handleUnknownVanilla(p, ctxt, bean, propName); + } while ((propName = p.nextFieldName()) != null); + return bean; + } + + /* + /********************************************************** + /* Concrete deserialization methods + /********************************************************** + */ + + /** + * Streamlined version that is only used when no "special" + * features are enabled. + */ + private final Object vanillaDeserialize(JsonParser p, + DeserializationContext ctxt, JsonToken t) + throws IOException + { + final Object bean = _valueInstantiator.createUsingDefault(ctxt); + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { + String propName = p.getCurrentName(); + do { + p.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + + if (prop != null) { // normal case + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + handleUnknownVanilla(p, ctxt, bean, propName); + } while ((propName = p.nextFieldName()) != null); + } + return bean; + } + + /** + * General version used when handling needs more advanced features. + */ + @Override + public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException + { + /* 09-Dec-2014, tatu: As per [#622], we need to allow Object Id references + * to come in as JSON Objects as well; but for now assume they will + * be simple, single-property references, which means that we can + * recognize them without having to buffer anything. + * Once again, if we must, we can do more complex handling with buffering, + * but let's only do that if and when that becomes necessary. + */ + if (_objectIdReader != null && _objectIdReader.maySerializeAsObject()) { + if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME) + && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { + return deserializeFromObjectId(p, ctxt); + } + } + if (_nonStandardCreation) { + if (_unwrappedPropertyHandler != null) { + return deserializeWithUnwrapped(p, ctxt); + } + if (_externalTypeIdHandler != null) { + return deserializeWithExternalTypeId(p, ctxt); + } + Object bean = deserializeFromObjectUsingNonDefault(p, ctxt); + if (_injectables != null) { + injectValues(ctxt, bean); + } + /* 27-May-2014, tatu: I don't think view processing would work + * at this point, so commenting it out; but leaving in place + * just in case I forgot something fundamental... + */ + /* + if (_needViewProcesing) { + Class view = ctxt.getActiveView(); + if (view != null) { + return deserializeWithView(p, ctxt, bean, view); + } + } + */ + return bean; + } + final Object bean = _valueInstantiator.createUsingDefault(ctxt); + // [databind#631]: Assign current value, to be accessible by custom deserializers + p.setCurrentValue(bean); + if (p.canReadObjectId()) { + Object id = p.getObjectId(); + if (id != null) { + _handleTypedObjectId(p, ctxt, bean, id); + } + } + if (_injectables != null) { + injectValues(ctxt, bean); + } + if (_needViewProcesing) { + Class view = ctxt.getActiveView(); + if (view != null) { + return deserializeWithView(p, ctxt, bean, view); + } + } + if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { + String propName = p.getCurrentName(); + do { + p.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + handleUnknownVanilla(p, ctxt, bean, propName); + } while ((propName = p.nextFieldName()) != null); + } + return bean; + } + + /** + * Method called to deserialize bean using "property-based creator": + * this means that a non-default constructor or factory method is + * called, and then possibly other setters. The trick is that + * values for creator method need to be buffered, first; and + * due to non-guaranteed ordering possibly some other properties + * as well. + */ + @Override + @SuppressWarnings("resource") + protected Object _deserializeUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt) + throws IOException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); + + // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases + TokenBuffer unknown = null; + + JsonToken t = p.getCurrentToken(); + for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.getCurrentName(); + p.nextToken(); // to point to value + // creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, + _deserializeWithErrorWrapping(p, ctxt, creatorProp))) { + p.nextToken(); // to move to following FIELD_NAME/END_OBJECT + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + bean = null; // never gets here + } + if (bean == null) { + throw ctxt.instantiationException(_beanType.getRawClass(), "JSON Creator returned null"); + } + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + + // polymorphic? + if (bean.getClass() != _beanType.getRawClass()) { + return handlePolymorphic(p, ctxt, bean, unknown); + } + if (unknown != null) { // nope, just extra unknown stuff... + bean = handleUnknownProperties(ctxt, bean, unknown); + } + // or just clean? + return deserialize(p, ctxt, bean); + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); + continue; + } + // As per [JACKSON-313], things marked as ignorable should not be + // passed to any setter + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, handledType(), propName); + continue; + } + // "any property"? + if (_anySetter != null) { + try { + buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + } + continue; + } + // Ok then, let's collect the whole field; name and value + if (unknown == null) { + unknown = new TokenBuffer(p, ctxt); + } + unknown.writeFieldName(propName); + unknown.copyCurrentStructure(p); + } + + // We hit END_OBJECT, so: + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + bean = null; // never gets here + } + if (unknown != null) { + // polymorphic? + if (bean.getClass() != _beanType.getRawClass()) { + return handlePolymorphic(null, ctxt, bean, unknown); + } + // no, just some extra unknown properties + return handleUnknownProperties(ctxt, bean, unknown); + } + return bean; + } + + protected final Object _deserializeWithErrorWrapping(JsonParser p, + DeserializationContext ctxt, SettableBeanProperty prop) + throws IOException + { + try { + return prop.deserialize(p, ctxt); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), prop.getName(), ctxt); + // never gets here, unless caller declines to throw an exception + return null; + } + } + + /** + * Helper method called for rare case of pointing to {@link JsonToken#VALUE_NULL} + * token. While this is most often an erroneous condition, there is one specific + * case with XML handling where polymorphic type with no properties is exposed + * as such, and should be handled same as empty Object. + * + * @since 2.7 + */ + protected Object deserializeFromNull(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // 17-Dec-2015, tatu: Highly specialized case, mainly to support polymorphic + // "empty" POJOs deserialized from XML, where empty XML tag synthesizes a + // `VALUE_NULL` token. + if (p.requiresCustomCodec()) { // not only XML module, but mostly it... + @SuppressWarnings("resource") + TokenBuffer tb = new TokenBuffer(p, ctxt); + tb.writeEndObject(); + JsonParser p2 = tb.asParser(p); + p2.nextToken(); // to point to END_OBJECT + // note: don't have ObjectId to consider at this point, so: + Object ob = _vanillaProcessing ? vanillaDeserialize(p2, ctxt, JsonToken.END_OBJECT) + : deserializeFromObject(p2, ctxt); + p2.close(); + return ob; + } + throw ctxt.mappingException(handledType()); + } + + /* + /********************************************************** + /* Deserializing when we have to consider an active View + /********************************************************** + */ + + protected final Object deserializeWithView(JsonParser p, DeserializationContext ctxt, + Object bean, Class activeView) + throws IOException + { + if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { + String propName = p.getCurrentName(); + do { + p.nextToken(); + // TODO: 06-Jan-2015, tatu: try streamlining call sequences here as well + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + if (!prop.visibleInView(activeView)) { + p.skipChildren(); + continue; + } + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + handleUnknownVanilla(p, ctxt, bean, propName); + } while ((propName = p.nextFieldName()) != null); + } + return bean; + } + + /* + /********************************************************** + /* Handling for cases where we have "unwrapped" values + /********************************************************** + */ + + /** + * Method called when there are declared "unwrapped" properties + * which need special handling + */ + @SuppressWarnings("resource") + protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + } + if (_propertyBasedCreator != null) { + return deserializeUsingPropertyBasedWithUnwrapped(p, ctxt); + } + TokenBuffer tokens = new TokenBuffer(p, ctxt); + tokens.writeStartObject(); + final Object bean = _valueInstantiator.createUsingDefault(ctxt); + + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + + if (_injectables != null) { + injectValues(ctxt, bean); + } + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + String propName = p.hasTokenId(JsonTokenId.ID_FIELD_NAME) ? p.getCurrentName() : null; + + for (; propName != null; propName = p.nextFieldName()) { + p.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + if (activeView != null && !prop.visibleInView(activeView)) { + p.skipChildren(); + continue; + } + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + // Things marked as ignorable should not be passed to any setter + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, bean, propName); + continue; + } + // but... others should be passed to unwrapped property deserializers + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(p); + // how about any setter? We'll get copies but... + if (_anySetter != null) { + try { + _anySetter.deserializeAndSet(p, ctxt, bean, propName); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + } + tokens.writeEndObject(); + _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); + return bean; + } + + @SuppressWarnings("resource") + protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt, Object bean) + throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.START_OBJECT) { + t = p.nextToken(); + } + TokenBuffer tokens = new TokenBuffer(p, ctxt); + tokens.writeStartObject(); + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.getCurrentName(); + SettableBeanProperty prop = _beanProperties.find(propName); + p.nextToken(); + if (prop != null) { // normal case + if (activeView != null && !prop.visibleInView(activeView)) { + p.skipChildren(); + continue; + } + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, bean, propName); + continue; + } + // but... others should be passed to unwrapped property deserializers + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(p); + // how about any setter? We'll get copies but... + if (_anySetter != null) { + _anySetter.deserializeAndSet(p, ctxt, bean, propName); + } + } + tokens.writeEndObject(); + _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); + return bean; + } + + @SuppressWarnings("resource") + protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, DeserializationContext ctxt) + throws IOException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); + + TokenBuffer tokens = new TokenBuffer(p, ctxt); + tokens.writeStartObject(); + + JsonToken t = p.getCurrentToken(); + for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.getCurrentName(); + p.nextToken(); // to point to value + // creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp))) { + t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + continue; // never gets here + } + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + // if so, need to copy all remaining tokens into buffer + while (t == JsonToken.FIELD_NAME) { + p.nextToken(); // to skip name + tokens.copyCurrentStructure(p); + t = p.nextToken(); + } + tokens.writeEndObject(); + if (bean.getClass() != _beanType.getRawClass()) { + // !!! 08-Jul-2011, tatu: Could probably support; but for now + // it's too complicated, so bail out + tokens.close(); + throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values"); + } + return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); + continue; + } + // Things marked as ignorable should not be passed to any setter + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, handledType(), propName); + continue; + } + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(p); + // "any property"? + if (_anySetter != null) { + try { + buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + } + } + } + + // We hit END_OBJECT, so: + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; // never gets here + } + return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); + } + + /* + /********************************************************** + /* Handling for cases where we have property/-ies with + /* external type id + /********************************************************** + */ + + protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_propertyBasedCreator != null) { + return deserializeUsingPropertyBasedWithExternalTypeId(p, ctxt); + } + if (_delegateDeserializer != null) { + /* 24-Nov-2015, tatu: Use of delegating creator needs to have precedence, and basically + * external type id handling just has to be ignored, as they would relate to target + * type and not delegate type. Whether this works as expected is another story, but + * there's no other way to really mix these conflicting features. + */ + return _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + + return deserializeWithExternalTypeId(p, ctxt, _valueInstantiator.createUsingDefault(ctxt)); + } + + protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt, + Object bean) + throws IOException + { + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + final ExternalTypeHandler ext = _externalTypeIdHandler.start(); + + for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.getCurrentName(); + t = p.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + // [JACKSON-831]: may have property AND be used as external type id: + if (t.isScalarValue()) { + ext.handleTypePropertyValue(p, ctxt, propName, bean); + } + if (activeView != null && !prop.visibleInView(activeView)) { + p.skipChildren(); + continue; + } + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + // ignorable things should be ignored + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, bean, propName); + continue; + } + // but others are likely to be part of external type id thingy... + if (ext.handlePropertyValue(p, ctxt, propName, bean)) { + continue; + } + // if not, the usual fallback handling: + if (_anySetter != null) { + try { + _anySetter.deserializeAndSet(p, ctxt, bean, propName); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + // Unknown: let's call handler method + handleUnknownProperty(p, ctxt, bean, propName); + } + // and when we get this far, let's try finalizing the deal: + return ext.complete(p, ctxt, bean); + } + + @SuppressWarnings("resource") + protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, DeserializationContext ctxt) + throws IOException + { + final ExternalTypeHandler ext = _externalTypeIdHandler.start(); + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); + + TokenBuffer tokens = new TokenBuffer(p, ctxt); + tokens.writeStartObject(); + + JsonToken t = p.getCurrentToken(); + for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.getCurrentName(); + p.nextToken(); // to point to value + // creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // first: let's check to see if this might be part of value with external type id: + // 11-Sep-2015, tatu: Important; do NOT pass buffer as last arg, but null, + // since it is not the bean + if (ext.handlePropertyValue(p, ctxt, propName, null)) { + ; + } else { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp))) { + t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + continue; // never gets here + } + // if so, need to copy all remaining tokens into buffer + while (t == JsonToken.FIELD_NAME) { + p.nextToken(); // to skip name + tokens.copyCurrentStructure(p); + t = p.nextToken(); + } + if (bean.getClass() != _beanType.getRawClass()) { + // !!! 08-Jul-2011, tatu: Could theoretically support; but for now + // it's too complicated, so bail out + throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values"); + } + return ext.complete(p, ctxt, bean); + } + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); + continue; + } + // external type id (or property that depends on it)? + if (ext.handlePropertyValue(p, ctxt, propName, null)) { + continue; + } + // Things marked as ignorable should not be passed to any setter + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, handledType(), propName); + continue; + } + // "any property"? + if (_anySetter != null) { + buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); + } + } + + // We hit END_OBJECT; resolve the pieces: + try { + return ext.complete(p, ctxt, buffer, creator); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; // never gets here + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1569 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonParser.NumberType; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.impl.*; +import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.IgnoredPropertyException; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.type.ClassKey; +import com.fasterxml.jackson.databind.util.*; + +/** + * Base class for BeanDeserializer. + */ +public abstract class BeanDeserializerBase + extends StdDeserializer + implements ContextualDeserializer, ResolvableDeserializer, + java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1; + + protected final static PropertyName TEMP_PROPERTY_NAME = new PropertyName("#temporary-name"); + + /* + /********************************************************** + /* Information regarding type being deserialized + /********************************************************** + */ + + /** + * Annotations from the bean class: used for accessing + * annotations during resolution + * (see {@link #resolve}) and + * contextualization (see {@link #createContextual}) + *

+ * Transient since annotations only used during construction. + */ + final private transient Annotations _classAnnotations; + + /** + * Declared type of the bean this deserializer handles. + */ + final protected JavaType _beanType; + + /** + * Requested shape from bean class annotations. + */ + final protected JsonFormat.Shape _serializationShape; + + /* + /********************************************************** + /* Configuration for creating value instance + /********************************************************** + */ + + /** + * Object that handles details of constructing initial + * bean value (to which bind data to), unless instance + * is passed (via updateValue()) + */ + protected final ValueInstantiator _valueInstantiator; + + /** + * Deserializer that is used iff delegate-based creator is + * to be used for deserializing from JSON Object. + */ + protected JsonDeserializer _delegateDeserializer; + + /** + * Deserializer that is used iff array-delegate-based creator + * is to be used for deserializing from JSON Object. + */ + protected JsonDeserializer _arrayDelegateDeserializer; + + /** + * If the bean needs to be instantiated using constructor + * or factory method + * that takes one or more named properties as argument(s), + * this creator is used for instantiation. + * This value gets resolved during general resolution. + */ + protected PropertyBasedCreator _propertyBasedCreator; + + /** + * Flag that is set to mark "non-standard" cases; where either + * we use one of non-default creators, or there are unwrapped + * values to consider. + */ + protected boolean _nonStandardCreation; + + /** + * Flag that indicates that no "special features" whatsoever + * are enabled, so the simplest processing is possible. + */ + protected boolean _vanillaProcessing; + + /* + /********************************************************** + /* Property information, setters + /********************************************************** + */ + + /** + * Mapping of property names to properties, built when all properties + * to use have been successfully resolved. + */ + final protected BeanPropertyMap _beanProperties; + + /** + * List of {@link ValueInjector}s, if any injectable values are + * expected by the bean; otherwise null. + * This includes injectors used for injecting values via setters + * and fields, but not ones passed through constructor parameters. + */ + final protected ValueInjector[] _injectables; + + /** + * Fallback setter used for handling any properties that are not + * mapped to regular setters. If setter is not null, it will be + * called once for each such property. + */ + protected SettableAnyProperty _anySetter; + + /** + * In addition to properties that are set, we will also keep + * track of recognized but ignorable properties: these will + * be skipped without errors or warnings. + */ + final protected HashSet _ignorableProps; + + /** + * Flag that can be set to ignore and skip unknown properties. + * If set, will not throw an exception for unknown properties. + */ + final protected boolean _ignoreAllUnknown; + + /** + * Flag that indicates that some aspect of deserialization depends + * on active view used (if any) + */ + final protected boolean _needViewProcesing; + + /** + * We may also have one or more back reference fields (usually + * zero or one). + */ + final protected Map _backRefs; + + /* + /********************************************************** + /* Related handlers + /********************************************************** + */ + + /** + * Lazily constructed map used to contain deserializers needed + * for polymorphic subtypes. + * Note that this is only needed for polymorphic types, + * that is, when the actual type is not statically known. + * For other types this remains null. + */ + protected transient HashMap> _subDeserializers; + + /** + * If one of properties has "unwrapped" value, we need separate + * helper object + */ + protected UnwrappedPropertyHandler _unwrappedPropertyHandler; + + /** + * Handler that we need iff any of properties uses external + * type id. + */ + protected ExternalTypeHandler _externalTypeIdHandler; + + /** + * If an Object Id is to be used for value handled by this + * deserializer, this reader is used for handling. + */ + protected final ObjectIdReader _objectIdReader; + + /* + /********************************************************** + /* Life-cycle, construction, initialization + /********************************************************** + */ + + /** + * Constructor used when initially building a deserializer + * instance, given a {@link BeanDeserializerBuilder} that + * contains configuration. + */ + protected BeanDeserializerBase(BeanDeserializerBuilder builder, + BeanDescription beanDesc, + BeanPropertyMap properties, Map backRefs, + HashSet ignorableProps, boolean ignoreAllUnknown, + boolean hasViews) + { + super(beanDesc.getType()); + + AnnotatedClass ac = beanDesc.getClassInfo(); + _classAnnotations = ac.getAnnotations(); + _beanType = beanDesc.getType(); + _valueInstantiator = builder.getValueInstantiator(); + + _beanProperties = properties; + _backRefs = backRefs; + _ignorableProps = ignorableProps; + _ignoreAllUnknown = ignoreAllUnknown; + + _anySetter = builder.getAnySetter(); + List injectables = builder.getInjectables(); + _injectables = (injectables == null || injectables.isEmpty()) ? null + : injectables.toArray(new ValueInjector[injectables.size()]); + _objectIdReader = builder.getObjectIdReader(); + _nonStandardCreation = (_unwrappedPropertyHandler != null) + || _valueInstantiator.canCreateUsingDelegate() + || _valueInstantiator.canCreateFromObjectWith() + || !_valueInstantiator.canCreateUsingDefault() + ; + + // Any transformation we may need to apply? + JsonFormat.Value format = beanDesc.findExpectedFormat(null); + _serializationShape = (format == null) ? null : format.getShape(); + + _needViewProcesing = hasViews; + _vanillaProcessing = !_nonStandardCreation + && (_injectables == null) + && !_needViewProcesing + // also, may need to reorder stuff if we expect Object Id: + && (_objectIdReader == null) + ; + } + + protected BeanDeserializerBase(BeanDeserializerBase src) { + this(src, src._ignoreAllUnknown); + } + + protected BeanDeserializerBase(BeanDeserializerBase src, boolean ignoreAllUnknown) + { + super(src._beanType); + + _classAnnotations = src._classAnnotations; + _beanType = src._beanType; + + _valueInstantiator = src._valueInstantiator; + _delegateDeserializer = src._delegateDeserializer; + _propertyBasedCreator = src._propertyBasedCreator; + + _beanProperties = src._beanProperties; + _backRefs = src._backRefs; + _ignorableProps = src._ignorableProps; + _ignoreAllUnknown = ignoreAllUnknown; + _anySetter = src._anySetter; + _injectables = src._injectables; + _objectIdReader = src._objectIdReader; + + _nonStandardCreation = src._nonStandardCreation; + _unwrappedPropertyHandler = src._unwrappedPropertyHandler; + _needViewProcesing = src._needViewProcesing; + _serializationShape = src._serializationShape; + + _vanillaProcessing = src._vanillaProcessing; + } + + protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapper) + { + super(src._beanType); + + _classAnnotations = src._classAnnotations; + _beanType = src._beanType; + + _valueInstantiator = src._valueInstantiator; + _delegateDeserializer = src._delegateDeserializer; + _propertyBasedCreator = src._propertyBasedCreator; + + _backRefs = src._backRefs; + _ignorableProps = src._ignorableProps; + _ignoreAllUnknown = (unwrapper != null) || src._ignoreAllUnknown; + _anySetter = src._anySetter; + _injectables = src._injectables; + _objectIdReader = src._objectIdReader; + + _nonStandardCreation = src._nonStandardCreation; + UnwrappedPropertyHandler uph = src._unwrappedPropertyHandler; + + if (unwrapper != null) { + // delegate further unwraps, if any + if (uph != null) { // got handler, delegate + uph = uph.renameAll(unwrapper); + } + // and handle direct unwrapping as well: + _beanProperties = src._beanProperties.renameAll(unwrapper); + } else { + _beanProperties = src._beanProperties; + } + _unwrappedPropertyHandler = uph; + _needViewProcesing = src._needViewProcesing; + _serializationShape = src._serializationShape; + + // probably adds a twist, so: + _vanillaProcessing = false; + } + + public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir) + { + super(src._beanType); + + _classAnnotations = src._classAnnotations; + _beanType = src._beanType; + + _valueInstantiator = src._valueInstantiator; + _delegateDeserializer = src._delegateDeserializer; + _propertyBasedCreator = src._propertyBasedCreator; + + _backRefs = src._backRefs; + _ignorableProps = src._ignorableProps; + _ignoreAllUnknown = src._ignoreAllUnknown; + _anySetter = src._anySetter; + _injectables = src._injectables; + + _nonStandardCreation = src._nonStandardCreation; + _unwrappedPropertyHandler = src._unwrappedPropertyHandler; + _needViewProcesing = src._needViewProcesing; + _serializationShape = src._serializationShape; + + // then actual changes: + _objectIdReader = oir; + + if (oir == null) { + _beanProperties = src._beanProperties; + _vanillaProcessing = src._vanillaProcessing; + } else { + /* 18-Nov-2012, tatu: May or may not have annotations for id property; + * but no easy access. But hard to see id property being optional, + * so let's consider required at this point. + */ + ObjectIdValueProperty idProp = new ObjectIdValueProperty(oir, PropertyMetadata.STD_REQUIRED); + _beanProperties = src._beanProperties.withProperty(idProp); + _vanillaProcessing = false; + } + } + + public BeanDeserializerBase(BeanDeserializerBase src, HashSet ignorableProps) + { + super(src._beanType); + + _classAnnotations = src._classAnnotations; + _beanType = src._beanType; + + _valueInstantiator = src._valueInstantiator; + _delegateDeserializer = src._delegateDeserializer; + _propertyBasedCreator = src._propertyBasedCreator; + + _backRefs = src._backRefs; + _ignorableProps = ignorableProps; + _ignoreAllUnknown = src._ignoreAllUnknown; + _anySetter = src._anySetter; + _injectables = src._injectables; + + _nonStandardCreation = src._nonStandardCreation; + _unwrappedPropertyHandler = src._unwrappedPropertyHandler; + _needViewProcesing = src._needViewProcesing; + _serializationShape = src._serializationShape; + + _vanillaProcessing = src._vanillaProcessing; + _objectIdReader = src._objectIdReader; + _beanProperties = src._beanProperties; + } + + @Override + public abstract JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper); + + public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir); + + public abstract BeanDeserializerBase withIgnorableProperties(HashSet ignorableProps); + + /** + * Fluent factory for creating a variant that can handle + * POJO output as a JSON Array. Implementations may ignore this request + * if no such input is possible. + * + * @since 2.1 + */ + protected abstract BeanDeserializerBase asArrayDeserializer(); + + /* + /********************************************************** + /* Validation, post-processing + /********************************************************** + */ + + /** + * Method called to finalize setup of this deserializer, + * after deserializer itself has been registered. + * This is needed to handle recursive and transitive dependencies. + */ + @Override + public void resolve(DeserializationContext ctxt) + throws JsonMappingException + { + ExternalTypeHandler.Builder extTypes = null; + // if ValueInstantiator can use "creator" approach, need to resolve it here... + SettableBeanProperty[] creatorProps; + if (_valueInstantiator.canCreateFromObjectWith()) { + creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig()); + // also: need to try to resolve 'external' type ids... + for (SettableBeanProperty prop : creatorProps) { + if (prop.hasValueTypeDeserializer()) { + TypeDeserializer typeDeser = prop.getValueTypeDeserializer(); + if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) { + if (extTypes == null) { + extTypes = new ExternalTypeHandler.Builder(); + } + extTypes.addExternal(prop, typeDeser); + } + } + } + } else { + creatorProps = null; + } + + UnwrappedPropertyHandler unwrapped = null; + + for (SettableBeanProperty origProp : _beanProperties) { + SettableBeanProperty prop = origProp; + + // May already have deserializer from annotations, if so, skip: + if (!prop.hasValueDeserializer()) { + // [databind#125]: allow use of converters + JsonDeserializer deser = findConvertingDeserializer(ctxt, prop); + if (deser == null) { + deser = findDeserializer(ctxt, prop.getType(), prop); + } + prop = prop.withValueDeserializer(deser); + } else { // may need contextual version + JsonDeserializer deser = prop.getValueDeserializer(); + /* Important! This is the only place where actually handle "primary" + * property deserializers -- call is different from other places. + */ + JsonDeserializer cd = ctxt.handlePrimaryContextualization(deser, prop, + prop.getType()); + if (cd != deser) { + prop = prop.withValueDeserializer(cd); + } + } + + // Need to link managed references with matching back references + prop = _resolveManagedReferenceProperty(ctxt, prop); + + // [databind#351[: need to wrap properties that require object id resolution. + if (!(prop instanceof ManagedReferenceProperty)) { + prop = _resolvedObjectIdProperty(ctxt, prop); + } + // Support unwrapped values (via @JsonUnwrapped) + SettableBeanProperty u = _resolveUnwrappedProperty(ctxt, prop); + if (u != null) { + prop = u; + if (unwrapped == null) { + unwrapped = new UnwrappedPropertyHandler(); + } + unwrapped.addProperty(prop); + /* 12-Dec-2014, tatu: As per [databind#647], we will have problems if + * the original property is left in place. So let's remove it now. + */ + _beanProperties.remove(prop); + continue; + } + // non-static inner classes too: + prop = _resolveInnerClassValuedProperty(ctxt, prop); + if (prop != origProp) { + _beanProperties.replace(prop); + // [databind#795]: Make sure PropertyBasedCreator's properties stay in sync + if (creatorProps != null) { + // 18-May-2015, tatu: _Should_ start with consistent set. But can we really + // fully count on this? May need to revisit in future; seems to hold for now. + for (int i = 0, len = creatorProps.length; i < len; ++i) { + if (creatorProps[i] == origProp) { + creatorProps[i] = prop; + break; + } + // ... as per above, it is possible we'd need to add this as fallback + // if (but only if) identity check fails? + /* + if (creatorProps[i].getName().equals(prop.getName())) { + creatorProps[i] = prop; + break; + } + */ + } + } + } + // one more thing: if this property uses "external property" type inclusion, + // it needs different handling altogether + if (prop.hasValueTypeDeserializer()) { + TypeDeserializer typeDeser = prop.getValueTypeDeserializer(); + if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) { + if (extTypes == null) { + extTypes = new ExternalTypeHandler.Builder(); + } + extTypes.addExternal(prop, typeDeser); + // In fact, remove from list of known properties to simplify later handling + _beanProperties.remove(prop); + continue; + } + } + } + + // "any setter" may also need to be resolved now + if (_anySetter != null && !_anySetter.hasValueDeserializer()) { + _anySetter = _anySetter.withValueDeserializer(findDeserializer(ctxt, + _anySetter.getType(), _anySetter.getProperty())); + } + + // as well as delegate-based constructor: + if (_valueInstantiator.canCreateUsingDelegate()) { + JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig()); + if (delegateType == null) { + throw new IllegalArgumentException("Invalid delegate-creator definition for "+_beanType + +": value instantiator ("+_valueInstantiator.getClass().getName() + +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'"); + } + _delegateDeserializer = _findDelegateDeserializer(ctxt, delegateType, + _valueInstantiator.getDelegateCreator()); + } + + // and array-delegate-based constructor: + if (_valueInstantiator.canCreateUsingArrayDelegate()) { + JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig()); + if (delegateType == null) { + throw new IllegalArgumentException("Invalid array-delegate-creator definition for "+_beanType + +": value instantiator ("+_valueInstantiator.getClass().getName() + +") returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'"); + } + _arrayDelegateDeserializer = _findDelegateDeserializer(ctxt, delegateType, + _valueInstantiator.getArrayDelegateCreator()); + } + + // And now that we know CreatorProperty instances are also resolved can finally create the creator: + if (creatorProps != null) { + _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps); + } + + if (extTypes != null) { + _externalTypeIdHandler = extTypes.build(); + // we consider this non-standard, to offline handling + _nonStandardCreation = true; + } + + _unwrappedPropertyHandler = unwrapped; + if (unwrapped != null) { // we consider this non-standard, to offline handling + _nonStandardCreation = true; + } + + // may need to disable vanilla processing, if unwrapped handling was enabled... + _vanillaProcessing = _vanillaProcessing && !_nonStandardCreation; + } + + private JsonDeserializer _findDelegateDeserializer(DeserializationContext ctxt, JavaType delegateType, + AnnotatedWithParams delegateCreator) throws JsonMappingException { + // Need to create a temporary property to allow contextual deserializers: + BeanProperty.Std property = new BeanProperty.Std(TEMP_PROPERTY_NAME, + delegateType, null, _classAnnotations, delegateCreator, + PropertyMetadata.STD_OPTIONAL); + + TypeDeserializer td = delegateType.getTypeHandler(); + if (td == null) { + td = ctxt.getConfig().findTypeDeserializer(delegateType); + } + JsonDeserializer dd = findDeserializer(ctxt, delegateType, property); + if (td != null) { + td = td.forProperty(property); + return new TypeWrappedDeserializer(td, dd); + } + return dd; + } + + + /** + * Helper method that can be used to see if specified property is annotated + * to indicate use of a converter for property value (in case of container types, + * it is container type itself, not key or content type). + * + * @since 2.2 + */ + protected JsonDeserializer findConvertingDeserializer(DeserializationContext ctxt, + SettableBeanProperty prop) + throws JsonMappingException + { + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + if (intr != null) { + Object convDef = intr.findDeserializationConverter(prop.getMember()); + if (convDef != null) { + Converter conv = ctxt.converterInstance(prop.getMember(), convDef); + JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); + JsonDeserializer ser = ctxt.findContextualValueDeserializer(delegateType, prop); + return new StdDelegatingDeserializer(conv, delegateType, ser); + } + } + return null; + } + + /** + * Although most of post-processing is done in resolve(), we only get + * access to referring property's annotations here; and this is needed + * to support per-property ObjectIds. + * We will also consider Shape transformations (read from Array) at this + * point, since it may come from either Class definition or property. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + ObjectIdReader oir = _objectIdReader; + + // First: may have an override for Object Id: + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + final AnnotatedMember accessor = (property == null || intr == null) + ? null : property.getMember(); + if (accessor != null && intr != null) { + ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); + if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory) + // 2.1: allow modifications by "id ref" annotations as well: + objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); + + Class implClass = objectIdInfo.getGeneratorType(); + // Property-based generator is trickier + JavaType idType; + SettableBeanProperty idProp; + ObjectIdGenerator idGen; + ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); + if (implClass == ObjectIdGenerators.PropertyGenerator.class) { + PropertyName propName = objectIdInfo.getPropertyName(); + idProp = findProperty(propName); + if (idProp == null) { + throw new IllegalArgumentException("Invalid Object Id definition for " + +handledType().getName()+": can not find property with name '"+propName+"'"); + } + idType = idProp.getType(); + idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); + } else { // other types need to be simpler + JavaType type = ctxt.constructType(implClass); + idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; + idProp = null; + idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo); + } + JsonDeserializer deser = ctxt.findRootValueDeserializer(idType); + oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), + idGen, deser, idProp, resolver); + } + } + // either way, need to resolve serializer: + BeanDeserializerBase contextual = this; + if (oir != null && oir != _objectIdReader) { + contextual = contextual.withObjectIdReader(oir); + } + // And possibly add more properties to ignore + if (accessor != null) { + String[] ignorals = intr.findPropertiesToIgnore(accessor, false); + if (ignorals != null && ignorals.length != 0) { + HashSet newIgnored = ArrayBuilders.setAndArray(contextual._ignorableProps, ignorals); + contextual = contextual.withIgnorableProperties(newIgnored); + } + } + + // One more thing: are we asked to serialize POJO as array? + JsonFormat.Shape shape = null; + if (accessor != null) { + JsonFormat.Value format = intr.findFormat((Annotated) accessor); + + if (format != null) { + shape = format.getShape(); + } + } + if (shape == null) { + shape = _serializationShape; + } + if (shape == JsonFormat.Shape.ARRAY) { + contextual = contextual.asArrayDeserializer(); + } + return contextual; + } + + /** + * Helper method called to see if given property is part of 'managed' property + * pair (managed + back reference), and if so, handle resolution details. + */ + protected SettableBeanProperty _resolveManagedReferenceProperty(DeserializationContext ctxt, + SettableBeanProperty prop) + { + String refName = prop.getManagedReferenceName(); + if (refName == null) { + return prop; + } + JsonDeserializer valueDeser = prop.getValueDeserializer(); + SettableBeanProperty backProp = valueDeser.findBackReference(refName); + if (backProp == null) { + throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': no back reference property found from type " + +prop.getType()); + } + // also: verify that type is compatible + JavaType referredType = _beanType; + JavaType backRefType = backProp.getType(); + boolean isContainer = prop.getType().isContainerType(); + if (!backRefType.getRawClass().isAssignableFrom(referredType.getRawClass())) { + throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': back reference type (" + +backRefType.getRawClass().getName()+") not compatible with managed type (" + +referredType.getRawClass().getName()+")"); + } + return new ManagedReferenceProperty(prop, refName, backProp, + _classAnnotations, isContainer); + } + + /** + * Method that wraps given property with {@link ObjectIdReferenceProperty} + * in case where object id resolution is required. + */ + protected SettableBeanProperty _resolvedObjectIdProperty(DeserializationContext ctxt, + SettableBeanProperty prop) + { + ObjectIdInfo objectIdInfo = prop.getObjectIdInfo(); + JsonDeserializer valueDeser = prop.getValueDeserializer(); + ObjectIdReader objectIdReader = valueDeser.getObjectIdReader(); + if (objectIdInfo == null && objectIdReader == null) { + return prop; + } + return new ObjectIdReferenceProperty(prop, objectIdInfo); + } + + /** + * Helper method called to see if given property might be so-called unwrapped + * property: these require special handling. + */ + protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationContext ctxt, + SettableBeanProperty prop) + { + AnnotatedMember am = prop.getMember(); + if (am != null) { + NameTransformer unwrapper = ctxt.getAnnotationIntrospector().findUnwrappingNameTransformer(am); + if (unwrapper != null) { + JsonDeserializer orig = prop.getValueDeserializer(); + JsonDeserializer unwrapping = orig.unwrappingDeserializer(unwrapper); + if (unwrapping != orig && unwrapping != null) { + // might be cleaner to create new instance; but difficult to do reliably, so: + return prop.withValueDeserializer(unwrapping); + } + } + } + return null; + } + + /** + * Helper method that will handle gruesome details of dealing with properties + * that have non-static inner class as value... + */ + protected SettableBeanProperty _resolveInnerClassValuedProperty(DeserializationContext ctxt, + SettableBeanProperty prop) + { + /* Should we encounter a property that has non-static inner-class + * as value, we need to add some more magic to find the "hidden" constructor... + */ + JsonDeserializer deser = prop.getValueDeserializer(); + // ideally wouldn't rely on it being BeanDeserializerBase; but for now it'll have to do + if (deser instanceof BeanDeserializerBase) { + BeanDeserializerBase bd = (BeanDeserializerBase) deser; + ValueInstantiator vi = bd.getValueInstantiator(); + if (!vi.canCreateUsingDefault()) { // no default constructor + Class valueClass = prop.getType().getRawClass(); + Class enclosing = ClassUtil.getOuterClass(valueClass); + // and is inner class of the bean class... + if (enclosing != null && enclosing == _beanType.getRawClass()) { + for (Constructor ctor : valueClass.getConstructors()) { + Class[] paramTypes = ctor.getParameterTypes(); + if (paramTypes.length == 1 && paramTypes[0] == enclosing) { + if (ctxt.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(ctor, ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return new InnerClassProperty(prop, ctor); + } + } + } + } + } + return prop; + } + + /* + /********************************************************** + /* Public accessors + /********************************************************** + */ + + @Override + public boolean isCachable() { return true; } + + @Override + public Class handledType() { + return _beanType.getRawClass(); + } + + /** + * Overridden to return true for those instances that are + * handling value for which Object Identity handling is enabled + * (either via value type or referring property). + */ + @Override + public ObjectIdReader getObjectIdReader() { + return _objectIdReader; + } + + public boolean hasProperty(String propertyName) { + return _beanProperties.find(propertyName) != null; + } + + public boolean hasViews() { + return _needViewProcesing; + } + + /** + * Accessor for checking number of deserialized properties. + */ + public int getPropertyCount() { + return _beanProperties.size(); + } + + @Override + public Collection getKnownPropertyNames() { + ArrayList names = new ArrayList(); + for (SettableBeanProperty prop : _beanProperties) { + names.add(prop.getName()); + } + return names; + } + + /** + * @deprecated Since 2.3, use {@link #handledType()} instead + */ + @Deprecated + public final Class getBeanClass() { return _beanType.getRawClass(); } + + @Override + public JavaType getValueType() { return _beanType; } + + /** + * Accessor for iterating over properties this deserializer uses; with + * the exception that properties passed via Creator methods + * (specifically, "property-based constructor") are not included, + * but can be accessed separate by calling + * {@link #creatorProperties} + */ + public Iterator properties() + { + if (_beanProperties == null) { + throw new IllegalStateException("Can only call after BeanDeserializer has been resolved"); + } + return _beanProperties.iterator(); + } + + /** + * Accessor for finding properties that represents values to pass + * through property-based creator method (constructor or + * factory method) + * + * @since 2.0 + */ + public Iterator creatorProperties() + { + if (_propertyBasedCreator == null) { + return Collections.emptyList().iterator(); + } + return _propertyBasedCreator.properties().iterator(); + } + + public SettableBeanProperty findProperty(PropertyName propertyName) + { + // TODO: start matching full name? + return findProperty(propertyName.getSimpleName()); + } + + /** + * Accessor for finding the property with given name, if POJO + * has one. Name used is the external name, i.e. name used + * in external data representation (JSON). + * + * @since 2.0 + */ + public SettableBeanProperty findProperty(String propertyName) + { + SettableBeanProperty prop = (_beanProperties == null) ? + null : _beanProperties.find(propertyName); + if (prop == null && _propertyBasedCreator != null) { + prop = _propertyBasedCreator.findCreatorProperty(propertyName); + } + return prop; + } + + /** + * Alternate find method that tries to locate a property with given + * property index. + * Note that access by index is not necessarily faster than by name, + * since properties are not directly indexable; however, for most + * instances difference is not significant as number of properties + * is low. + * + * @since 2.3 + */ + public SettableBeanProperty findProperty(int propertyIndex) + { + SettableBeanProperty prop = (_beanProperties == null) ? + null : _beanProperties.find(propertyIndex); + if (prop == null && _propertyBasedCreator != null) { + prop = _propertyBasedCreator.findCreatorProperty(propertyIndex); + } + return prop; + } + + /** + * Method needed by {@link BeanDeserializerFactory} to properly link + * managed- and back-reference pairs. + */ + @Override + public SettableBeanProperty findBackReference(String logicalName) + { + if (_backRefs == null) { + return null; + } + return _backRefs.get(logicalName); + } + + public ValueInstantiator getValueInstantiator() { + return _valueInstantiator; + } + + /* + /********************************************************** + /* Mutators + /********************************************************** + */ + + /** + * Method that can be used to replace an existing property with + * a modified one. + *

+ * NOTE: only ever use this method if you know what you are doing; + * incorrect usage can break deserializer. + * + * @param original Property to replace + * @param replacement Property to replace it with + * + * @since 2.1 + */ + public void replaceProperty(SettableBeanProperty original, + SettableBeanProperty replacement) + { + _beanProperties.replace(replacement); + } + + /* + /********************************************************** + /* Partial deserializer implementation + /********************************************************** + */ + + /** + * General version used when handling needs more advanced + * features. + */ + public abstract Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) + throws IOException; + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + // 16-Feb-2012, tatu: ObjectId may be used as well... need to check that first + if (_objectIdReader != null) { + // 05-Aug-2013, tatu: May use native Object Id + if (p.canReadObjectId()) { + Object id = p.getObjectId(); + if (id != null) { + Object ob = typeDeserializer.deserializeTypedFromObject(p, ctxt); + return _handleTypedObjectId(p, ctxt, ob, id); + } + } + // or, Object Ids Jackson explicitly sets + JsonToken t = p.getCurrentToken(); + if (t != null) { + // Most commonly, a scalar (int id, uuid String, ...) + if (t.isScalarValue()) { + return deserializeFromObjectId(p, ctxt); + } + // but, with 2.5+, a simple Object-wrapped value also legal: + if (t == JsonToken.START_OBJECT) { + t = p.nextToken(); + } + if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() + && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { + return deserializeFromObjectId(p, ctxt); + } + } + } + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromObject(p, ctxt); + } + + /** + * Offlined method called to handle "native" Object Id that has been read + * and known to be associated with given deserialized POJO. + * + * @since 2.3 + */ + protected Object _handleTypedObjectId(JsonParser p, DeserializationContext ctxt, + Object pojo, Object rawId) + throws IOException + { + // One more challenge: type of id may not be type of property we are expecting + // later on; specifically, numeric ids vs Strings. + JsonDeserializer idDeser = _objectIdReader.getDeserializer(); + final Object id; + + // Ok, this is bit ridiculous; let's see if conversion is needed: + if (idDeser.handledType() == rawId.getClass()) { + // nope: already same type + id = rawId; + } else { + id = _convertObjectId(p, ctxt, rawId, idDeser); + } + + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); + roid.bindItem(pojo); + // also: may need to set a property value as well + SettableBeanProperty idProp = _objectIdReader.idProperty; + if (idProp != null) { + return idProp.setAndReturn(pojo, id); + } + return pojo; + } + + /** + * Helper method we need to do necessary conversion from whatever native object id + * type is, into declared type that Jackson internals expect. This may be + * simple cast (for String ids), or something more complicated; in latter + * case we may need to create bogus content buffer to allow use of + * id deserializer. + * + * @since 2.3 + */ + @SuppressWarnings("resource") // TokenBuffers don't need close, nor parser thereof + protected Object _convertObjectId(JsonParser p, DeserializationContext ctxt, + Object rawId, JsonDeserializer idDeser) throws IOException + { + TokenBuffer buf = new TokenBuffer(p, ctxt); + if (rawId instanceof String) { + buf.writeString((String) rawId); + } else if (rawId instanceof Long) { + buf.writeNumber(((Long) rawId).longValue()); + } else if (rawId instanceof Integer) { + buf.writeNumber(((Integer) rawId).intValue()); + } else { + // should we worry about UUIDs? They should be fine, right? + // 07-Aug-2014, tatu: Maybe, but not necessarily; had issues with + // Smile format; [Smile#19], possibly related. + buf.writeObject(rawId); + } + JsonParser bufParser = buf.asParser(); + bufParser.nextToken(); + return idDeser.deserialize(bufParser, ctxt); + } + + // NOTE: currently only used by standard BeanDeserializer (not Builder-based) + /** + * Alternative deserialization method used when we expect to see Object Id; + * if so, we will need to ensure that the Id is seen before anything + * else, to ensure that it is available for solving references, + * even if JSON itself is not ordered that way. This may require + * buffering in some cases, but usually just a simple lookup to ensure + * that ordering is correct. + */ + protected Object deserializeWithObjectId(JsonParser p, DeserializationContext ctxt) throws IOException { + return deserializeFromObject(p, ctxt); + } + + /** + * Method called in cases where it looks like we got an Object Id + * to parse and use as a reference. + */ + protected Object deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException + { + Object id = _objectIdReader.readObjectReference(p, ctxt); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); + // do we have it resolved? + Object pojo = roid.resolve(); + if (pojo == null) { // not yet; should wait... + throw new UnresolvedForwardReference(p, + "Could not resolve Object Id ["+id+"] (for "+_beanType+").", + p.getCurrentLocation(), roid); + } + return pojo; + } + + protected Object deserializeFromObjectUsingNonDefault(JsonParser p, + DeserializationContext ctxt) throws IOException + { + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + if (_propertyBasedCreator != null) { + return _deserializeUsingPropertyBased(p, ctxt); + } + // should only occur for abstract types... + if (_beanType.isAbstract()) { + throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType + +" (need to add/enable type information?)"); + } + throw JsonMappingException.from(p, "No suitable constructor found for type " + +_beanType+": can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)"); + } + + protected abstract Object _deserializeUsingPropertyBased(final JsonParser p, + final DeserializationContext ctxt) + throws IOException, JsonProcessingException; + + @SuppressWarnings("incomplete-switch") + public Object deserializeFromNumber(JsonParser p, DeserializationContext ctxt) throws IOException + { + // First things first: id Object Id is used, most likely that's it + if (_objectIdReader != null) { + return deserializeFromObjectId(p, ctxt); + } + + switch (p.getNumberType()) { + case INT: + if (_delegateDeserializer != null) { + if (!_valueInstantiator.canCreateFromInt()) { + Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } + } + return _valueInstantiator.createFromInt(ctxt, p.getIntValue()); + case LONG: + if (_delegateDeserializer != null) { + if (!_valueInstantiator.canCreateFromInt()) { + Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } + } + return _valueInstantiator.createFromLong(ctxt, p.getLongValue()); + } + // actually, could also be BigInteger, so: + if (_delegateDeserializer != null) { + Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } + throw ctxt.instantiationException(handledType(), "no suitable creator method found to deserialize from JSON integer number"); + } + + public Object deserializeFromString(JsonParser p, DeserializationContext ctxt) throws IOException + { + // First things first: id Object Id is used, most likely that's it + if (_objectIdReader != null) { + return deserializeFromObjectId(p, ctxt); + } + + /* Bit complicated if we have delegating creator; may need to use it, + * or might not... + */ + if (_delegateDeserializer != null) { + if (!_valueInstantiator.canCreateFromString()) { + Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } + } + return _valueInstantiator.createFromString(ctxt, p.getText()); + } + + /** + * Method called to deserialize POJO value from a JSON floating-point + * number. + */ + @SuppressWarnings("incomplete-switch") + public Object deserializeFromDouble(JsonParser p, DeserializationContext ctxt) throws IOException + { + NumberType t = p.getNumberType(); + // no separate methods for taking float... + if ((t == NumberType.DOUBLE) || (t == NumberType.FLOAT)) { + if (_delegateDeserializer != null) { + if (!_valueInstantiator.canCreateFromDouble()) { + Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } + } + return _valueInstantiator.createFromDouble(ctxt, p.getDoubleValue()); + } + // actually, could also be BigDecimal, so: + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + } + throw ctxt.instantiationException(handledType(), "no suitable creator method found to deserialize from JSON floating-point number"); + } + + /** + * Method called to deserialize POJO value from a JSON boolean value (true, false) + */ + public Object deserializeFromBoolean(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (_delegateDeserializer != null) { + if (!_valueInstantiator.canCreateFromBoolean()) { + Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } + } + boolean value = (p.getCurrentToken() == JsonToken.VALUE_TRUE); + return _valueInstantiator.createFromBoolean(ctxt, value); + } + + public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (_arrayDelegateDeserializer != null) { + try { + Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, _arrayDelegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + } + } + // fallback to non-array delegate + if (_delegateDeserializer != null) { + try { + Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + if (_injectables != null) { + injectValues(ctxt, bean); + } + return bean; + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + } + } + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + JsonToken t = p.nextToken(); + if (t == JsonToken.END_ARRAY && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { + return null; + } + final Object value = deserialize(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array"); + } + return value; + } + if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { + JsonToken t = p.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY); + } + throw ctxt.mappingException(handledType()); + } + + public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // First things first: id Object Id is used, most likely that's it; specifically, + // true for UUIDs when written as binary (with Smile, other binary formats) + if (_objectIdReader != null) { + return deserializeFromObjectId(p, ctxt); + } + + // TODO: maybe add support for ValueInstantiator, embedded? + + return p.getEmbeddedObject(); + } + + /* + /********************************************************** + /* Overridable helper methods + /********************************************************** + */ + + protected void injectValues(DeserializationContext ctxt, Object bean) + throws IOException + { + for (ValueInjector injector : _injectables) { + injector.inject(ctxt, bean); + } + } + + /** + * Method called to handle set of one or more unknown properties, + * stored in their entirety in given {@link TokenBuffer} + * (as field entries, name and value). + */ + @SuppressWarnings("resource") + protected Object handleUnknownProperties(DeserializationContext ctxt, + Object bean, TokenBuffer unknownTokens) + throws IOException + { + // First: add closing END_OBJECT as marker + unknownTokens.writeEndObject(); + + // note: buffer does NOT have starting START_OBJECT + JsonParser bufferParser = unknownTokens.asParser(); + while (bufferParser.nextToken() != JsonToken.END_OBJECT) { + String propName = bufferParser.getCurrentName(); + // Unknown: let's call handler method + bufferParser.nextToken(); + handleUnknownProperty(bufferParser, ctxt, bean, propName); + } + return bean; + } + + /** + * Helper method called for an unknown property, when using "vanilla" + * processing. + */ + protected void handleUnknownVanilla(JsonParser p, DeserializationContext ctxt, + Object bean, String propName) + throws IOException + { + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, bean, propName); + } else if (_anySetter != null) { + try { + // should we consider return type of any setter? + _anySetter.deserializeAndSet(p, ctxt, bean, propName); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + } else { + // Unknown: let's call handler method + handleUnknownProperty(p, ctxt, bean, propName); + } + } + + /** + * Method called when a JSON property is encountered that has not matching + * setter, any-setter or field, and thus can not be assigned. + */ + @Override + protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, + Object beanOrClass, String propName) + throws IOException + { + if (_ignoreAllUnknown) { + p.skipChildren(); + return; + } + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, beanOrClass, propName); + } + // Otherwise use default handling (call handler(s); if not + // handled, throw exception or skip depending on settings) + super.handleUnknownProperty(p, ctxt, beanOrClass, propName); + } + + /** + * Method called when an explicitly ignored property (one specified with a + * name to match, either by property annotation or class annotation) is encountered. + * + * @since 2.3 + */ + protected void handleIgnoredProperty(JsonParser p, DeserializationContext ctxt, + Object beanOrClass, String propName) + throws IOException + { + if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)) { + throw IgnoredPropertyException.from(p, beanOrClass, propName, getKnownPropertyNames()); + } + p.skipChildren(); + } + + /** + * Method called in cases where we may have polymorphic deserialization + * case: that is, type of Creator-constructed bean is not the type + * of deserializer itself. It should be a sub-class or implementation + * class; either way, we may have more specific deserializer to use + * for handling it. + * + * @param p (optional) If not null, parser that has more properties to handle + * (in addition to buffered properties); if null, all properties are passed + * in buffer + */ + @SuppressWarnings("resource") + protected Object handlePolymorphic(JsonParser p, DeserializationContext ctxt, + Object bean, TokenBuffer unknownTokens) + throws IOException + { + // First things first: maybe there is a more specific deserializer available? + JsonDeserializer subDeser = _findSubclassDeserializer(ctxt, bean, unknownTokens); + if (subDeser != null) { + if (unknownTokens != null) { + // need to add END_OBJECT marker first + unknownTokens.writeEndObject(); + JsonParser p2 = unknownTokens.asParser(); + p2.nextToken(); // to get to first data field + bean = subDeser.deserialize(p2, ctxt, bean); + } + // Original parser may also have some leftovers + if (p != null) { + bean = subDeser.deserialize(p, ctxt, bean); + } + return bean; + } + // nope; need to use this deserializer. Unknowns we've seen so far? + if (unknownTokens != null) { + bean = handleUnknownProperties(ctxt, bean, unknownTokens); + } + // and/or things left to process via main parser? + if (p != null) { + bean = deserialize(p, ctxt, bean); + } + return bean; + } + + /** + * Helper method called to (try to) locate deserializer for given sub-type of + * type that this deserializer handles. + */ + protected JsonDeserializer _findSubclassDeserializer(DeserializationContext ctxt, + Object bean, TokenBuffer unknownTokens) + throws IOException + { + JsonDeserializer subDeser; + + // First: maybe we have already created sub-type deserializer? + synchronized (this) { + subDeser = (_subDeserializers == null) ? null : _subDeserializers.get(new ClassKey(bean.getClass())); + } + if (subDeser != null) { + return subDeser; + } + // If not, maybe we can locate one. First, need provider + JavaType type = ctxt.constructType(bean.getClass()); + /* 30-Jan-2012, tatu: Ideally we would be passing referring + * property; which in theory we could keep track of via + * ResolvableDeserializer (if we absolutely must...). + * But for now, let's not bother. + */ +// subDeser = ctxt.findValueDeserializer(type, _property); + subDeser = ctxt.findRootValueDeserializer(type); + // Also, need to cache it + if (subDeser != null) { + synchronized (this) { + if (_subDeserializers == null) { + _subDeserializers = new HashMap>();; + } + _subDeserializers.put(new ClassKey(bean.getClass()), subDeser); + } + } + return subDeser; + } + + /* + /********************************************************** + /* Helper methods for error reporting + /********************************************************** + */ + + /** + * Method that will modify caught exception (passed in as argument) + * as necessary to include reference information, and to ensure it + * is a subtype of {@link IOException}, or an unchecked exception. + *

+ * Rules for wrapping and unwrapping are bit complicated; essentially: + *

    + *
  • Errors are to be passed as is (if uncovered via unwrapping) + *
  • "Plain" IOExceptions (ones that are not of type + * {@link JsonMappingException} are to be passed as is + *
+ */ + public void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) + throws IOException + { + // [JACKSON-55] Need to add reference information + throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, fieldName); + } + + @Deprecated // since 2.4, not used by core Jackson; only relevant for arrays/Collections + public void wrapAndThrow(Throwable t, Object bean, int index, DeserializationContext ctxt) throws IOException { + // [JACKSON-55] Need to add reference information + throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, index); + } + + private Throwable throwOrReturnThrowable(Throwable t, DeserializationContext ctxt) + throws IOException + { + /* 05-Mar-2009, tatu: But one nasty edge is when we get + * StackOverflow: usually due to infinite loop. But that + * often gets hidden within an InvocationTargetException... + */ + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors to be passed as is + if (t instanceof Error) { + throw (Error) t; + } + boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); + // Ditto for IOExceptions; except we may want to wrap JSON exceptions + if (t instanceof IOException) { + if (!wrap || !(t instanceof JsonProcessingException)) { + throw (IOException) t; + } + } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + } + return t; + } + + protected void wrapInstantiationProblem(Throwable t, DeserializationContext ctxt) + throws IOException + { + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors and "plain" IOExceptions to be passed as is + if (t instanceof Error) { + throw (Error) t; + } + boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); + if (t instanceof IOException) { + // Since we have no more information to add, let's not actually wrap.. + throw (IOException) t; + } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + } + throw ctxt.instantiationException(_beanType.getRawClass(), t); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,419 @@ +package com.fasterxml.jackson.databind.deser; + +import java.util.*; + + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap; +import com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty; +import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; +import com.fasterxml.jackson.databind.deser.impl.ValueInjector; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * Builder class used for aggregating deserialization information about + * a POJO, in order to build a {@link JsonDeserializer} for deserializing + * instances. + */ +public class BeanDeserializerBuilder +{ + /* + /********************************************************** + /* General information about POJO + /********************************************************** + */ + + final protected BeanDescription _beanDesc; + + final protected boolean _defaultViewInclusion; + + final protected boolean _caseInsensitivePropertyComparison; + + /* + /********************************************************** + /* Accumulated information about properties + /********************************************************** + */ + + /** + * Properties to deserialize collected so far. + */ + final protected Map _properties + = new LinkedHashMap(); + + /** + * Value injectors for deserialization + */ + protected List _injectables; + + /** + * Back-reference properties this bean contains (if any) + */ + protected HashMap _backRefProperties; + + /** + * Set of names of properties that are recognized but are to be ignored for deserialization + * purposes (meaning no exception is thrown, value is just skipped). + */ + protected HashSet _ignorableProps; + + /** + * Object that will handle value instantiation for the bean type. + */ + protected ValueInstantiator _valueInstantiator; + + protected ObjectIdReader _objectIdReader; + + /** + * Fallback setter used for handling any properties that are not + * mapped to regular setters. If setter is not null, it will be + * called once for each such property. + */ + protected SettableAnyProperty _anySetter; + + /** + * Flag that can be set to ignore and skip unknown properties. + * If set, will not throw an exception for unknown properties. + */ + protected boolean _ignoreAllUnknown; + + /** + * When creating Builder-based deserializers, this indicates + * method to call on builder to finalize value. + */ + protected AnnotatedMethod _buildMethod; + + /** + * In addition, Builder may have additional configuration + */ + protected JsonPOJOBuilder.Value _builderConfig; + + /* + /********************************************************** + /* Life-cycle: construction + /********************************************************** + */ + + public BeanDeserializerBuilder(BeanDescription beanDesc, + DeserializationConfig config) + { + _beanDesc = beanDesc; + _defaultViewInclusion = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); + _caseInsensitivePropertyComparison = config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); + } + + /** + * Copy constructor for sub-classes to use, when constructing + * custom builder instances + */ + protected BeanDeserializerBuilder(BeanDeserializerBuilder src) + { + _beanDesc = src._beanDesc; + _defaultViewInclusion = src._defaultViewInclusion; + _caseInsensitivePropertyComparison = src._caseInsensitivePropertyComparison; + + // let's make copy of properties + _properties.putAll(src._properties); + _injectables = _copy(src._injectables); + _backRefProperties = _copy(src._backRefProperties); + // Hmmh. Should we create defensive copies here? For now, not yet + _ignorableProps = src._ignorableProps; + _valueInstantiator = src._valueInstantiator; + _objectIdReader = src._objectIdReader; + + _anySetter = src._anySetter; + _ignoreAllUnknown = src._ignoreAllUnknown; + + _buildMethod = src._buildMethod; + _builderConfig = src._builderConfig; + } + + private static HashMap _copy(HashMap src) { + return (src == null) ? null + : new HashMap(src); + } + + private static List _copy(List src) { + return (src == null) ? null : new ArrayList(src); + } + + /* + /********************************************************** + /* Life-cycle: state modification (adders, setters) + /********************************************************** + */ + + /** + * Method for adding a new property or replacing a property. + */ + public void addOrReplaceProperty(SettableBeanProperty prop, boolean allowOverride) { + _properties.put(prop.getName(), prop); + } + + /** + * Method to add a property setter. Will ensure that there is no + * unexpected override; if one is found will throw a + * {@link IllegalArgumentException}. + */ + public void addProperty(SettableBeanProperty prop) + { + SettableBeanProperty old = _properties.put(prop.getName(), prop); + if (old != null && old != prop) { // should never occur... + throw new IllegalArgumentException("Duplicate property '"+prop.getName()+"' for "+_beanDesc.getType()); + } + } + + /** + * Method called to add a property that represents so-called back reference; + * reference that "points back" to object that has forward reference to + * currently built bean. + */ + public void addBackReferenceProperty(String referenceName, SettableBeanProperty prop) + { + if (_backRefProperties == null) { + _backRefProperties = new HashMap(4); + } + _backRefProperties.put(referenceName, prop); + // also: if we had property with same name, actually remove it + if (_properties != null) { + _properties.remove(prop.getName()); + } + // ??? 23-Jul-2012, tatu: Should it be included in list of all properties? + // For now, won't add, since it is inferred, not explicit... + } + + public void addInjectable(PropertyName propName, JavaType propType, + Annotations contextAnnotations, AnnotatedMember member, + Object valueId) + { + if (_injectables == null) { + _injectables = new ArrayList(); + } + _injectables.add(new ValueInjector(propName, propType, + contextAnnotations, member, valueId)); + } + + /** + * Method that will add property name as one of properties that can + * be ignored if not recognized. + */ + public void addIgnorable(String propName) + { + if (_ignorableProps == null) { + _ignorableProps = new HashSet(); + } + _ignorableProps.add(propName); + } + + /** + * Method called by deserializer factory, when a "creator property" + * (something that is passed via constructor- or factory method argument; + * instead of setter or field). + *

+ * Default implementation does not do anything; we may need to revisit this + * decision if these properties need to be available through accessors. + * For now, however, we just have to ensure that we don't try to resolve + * types that masked setter/field has (see [JACKSON-700] for details). + */ + public void addCreatorProperty(SettableBeanProperty prop) + { + addProperty(prop); + } + + public void setAnySetter(SettableAnyProperty s) + { + if (_anySetter != null && s != null) { + throw new IllegalStateException("_anySetter already set to non-null"); + } + _anySetter = s; + } + + public void setIgnoreUnknownProperties(boolean ignore) { + _ignoreAllUnknown = ignore; + } + + public void setValueInstantiator(ValueInstantiator inst) { + _valueInstantiator = inst; + } + + public void setObjectIdReader(ObjectIdReader r) { + _objectIdReader = r; + } + + public void setPOJOBuilder(AnnotatedMethod buildMethod, JsonPOJOBuilder.Value config) { + _buildMethod = buildMethod; + _builderConfig = config; + } + + /* + /********************************************************** + /* Public accessors + /********************************************************** + */ + + /** + * Method that allows accessing all properties that this + * builder currently contains. + *

+ * Note that properties are returned in order that properties + * are ordered (explictly, or by rule), which is the serialization + * order. + */ + public Iterator getProperties() { + return _properties.values().iterator(); + } + + public SettableBeanProperty findProperty(PropertyName propertyName) { + return _properties.get(propertyName.getSimpleName()); + } + + public boolean hasProperty(PropertyName propertyName) { + return findProperty(propertyName) != null; + } + + public SettableBeanProperty removeProperty(PropertyName name) { + return _properties.remove(name.getSimpleName()); + } + + public SettableAnyProperty getAnySetter() { + return _anySetter; + } + + public ValueInstantiator getValueInstantiator() { + return _valueInstantiator; + } + + public List getInjectables() { + return _injectables; + } + + public ObjectIdReader getObjectIdReader() { + return _objectIdReader; + } + + public AnnotatedMethod getBuildMethod() { + return _buildMethod; + } + + public JsonPOJOBuilder.Value getBuilderConfig() { + return _builderConfig; + } + + /* + /********************************************************** + /* Build method(s) + /********************************************************** + */ + + /** + * Method for constructing a {@link BeanDeserializer}, given all + * information collected. + *

+ * NOTE: Signature of this method did unfortunately change between Jackson 2.1 + * and Jackson 2.2 + */ + public JsonDeserializer build() + { + Collection props = _properties.values(); + BeanPropertyMap propertyMap = BeanPropertyMap.construct(props, _caseInsensitivePropertyComparison); + propertyMap.assignIndexes(); + + // view processing must be enabled if: + // (a) fields are not included by default (when deserializing with view), OR + // (b) one of properties has view(s) to included in defined + boolean anyViews = !_defaultViewInclusion; + + if (!anyViews) { + for (SettableBeanProperty prop : props) { + if (prop.hasViews()) { + anyViews = true; + break; + } + } + } + + // one more thing: may need to create virtual ObjectId property: + if (_objectIdReader != null) { + /* 18-Nov-2012, tatu: May or may not have annotations for id property; + * but no easy access. But hard to see id property being optional, + * so let's consider required at this point. + */ + ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED); + propertyMap = propertyMap.withProperty(prop); + } + + return new BeanDeserializer(this, + _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, + anyViews); + } + + /** + * Alternate build method used when we must be using some form of + * abstract resolution, usually by using addition Type Id + * ("polymorphic deserialization") + * + * @since 2.0 + */ + public AbstractDeserializer buildAbstract() { + return new AbstractDeserializer(this, _beanDesc, _backRefProperties); + } + + /** + * Method for constructing a specialized deserializer that uses + * additional external Builder object during data binding. + */ + public JsonDeserializer buildBuilderBased(JavaType valueType, + String expBuildMethodName) + { + // First: validation; must have build method that returns compatible type + if (_buildMethod == null) { + // as per [databind#777], allow empty name + if (!expBuildMethodName.isEmpty()) { + throw new IllegalArgumentException("Builder class "+_beanDesc.getBeanClass().getName() + +" does not have build method (name: '"+expBuildMethodName+"')"); + } + } else { + // also: type of the method must be compatible + Class rawBuildType = _buildMethod.getRawReturnType(); + Class rawValueType = valueType.getRawClass(); + if ((rawBuildType != rawValueType) + && !rawBuildType.isAssignableFrom(rawValueType) + && !rawValueType.isAssignableFrom(rawBuildType)) { + throw new IllegalArgumentException("Build method '"+_buildMethod.getFullName() + +" has bad return type ("+rawBuildType.getName() + +"), not compatible with POJO type ("+valueType.getRawClass().getName()+")"); + } + } + // And if so, we can try building the deserializer + Collection props = _properties.values(); + BeanPropertyMap propertyMap = BeanPropertyMap.construct(props, _caseInsensitivePropertyComparison); + propertyMap.assignIndexes(); + + boolean anyViews = !_defaultViewInclusion; + + if (!anyViews) { + for (SettableBeanProperty prop : props) { + if (prop.hasViews()) { + anyViews = true; + break; + } + } + } + + if (_objectIdReader != null) { + /* 18-Nov-2012, tatu: May or may not have annotations for id property; + * but no easy access. But hard to see id property being optional, + * so let's consider required at this point. + */ + ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, + PropertyMetadata.STD_REQUIRED); + propertyMap = propertyMap.withProperty(prop); + } + + return new BuilderBasedDeserializer(this, + _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, + anyViews); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,831 @@ +package com.fasterxml.jackson.databind.deser; + +import java.util.*; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig; +import com.fasterxml.jackson.databind.deser.impl.*; +import com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition; + +/** + * Concrete deserializer factory class that adds full Bean deserializer + * construction logic using class introspection. + * Note that factories specifically do not implement any form of caching: + * aside from configuration they are stateless; caching is implemented + * by other components. + *

+ * Instances of this class are fully immutable as all configuration is + * done by using "fluent factories" (methods that construct new factory + * instances with different configuration, instead of modifying instance). + */ +public class BeanDeserializerFactory + extends BasicDeserializerFactory + implements java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1; + + /** + * Signature of Throwable.initCause method. + */ + private final static Class[] INIT_CAUSE_PARAMS = new Class[] { Throwable.class }; + + private final static Class[] NO_VIEWS = new Class[0]; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Globally shareable thread-safe instance which has no additional custom deserializers + * registered + */ + public final static BeanDeserializerFactory instance = new BeanDeserializerFactory( + new DeserializerFactoryConfig()); + + public BeanDeserializerFactory(DeserializerFactoryConfig config) { + super(config); + } + + /** + * Method used by module registration functionality, to construct a new bean + * deserializer factory + * with different configuration settings. + */ + @Override + public DeserializerFactory withConfig(DeserializerFactoryConfig config) + { + if (_factoryConfig == config) { + return this; + } + /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor; + * and we pretty much have to here either choose between losing subtype instance + * when registering additional deserializers, or losing deserializers. + * Instead, let's actually just throw an error if this method is called when subtype + * has not properly overridden this method; this to indicate problem as soon as possible. + */ + if (getClass() != BeanDeserializerFactory.class) { + throw new IllegalStateException("Subtype of BeanDeserializerFactory ("+getClass().getName() + +") has not properly overridden method 'withAdditionalDeserializers': can not instantiate subtype with " + +"additional deserializer definitions"); + } + return new BeanDeserializerFactory(config); + } + + /* + /********************************************************** + /* DeserializerFactory API implementation + /********************************************************** + */ + + /** + * Method that {@link DeserializerCache}s call to create a new + * deserializer for types other than Collections, Maps, arrays and + * enums. + */ + @Override + public JsonDeserializer createBeanDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + // We may also have custom overrides: + JsonDeserializer custom = _findCustomBeanDeserializer(type, config, beanDesc); + if (custom != null) { + return custom; + } + /* One more thing to check: do we have an exception type + * (Throwable or its sub-classes)? If so, need slightly + * different handling. + */ + if (type.isThrowable()) { + return buildThrowableDeserializer(ctxt, type, beanDesc); + } + /* Or, for abstract types, may have alternate means for resolution + * (defaulting, materialization) + */ + // 29-Nov-2015, tatu: Also, filter out calls to primitive types, they are + // not something we could materialize anything for + if (type.isAbstract() && !type.isPrimitive()) { + // Let's make it possible to materialize abstract types. + JavaType concreteType = materializeAbstractType(ctxt, type, beanDesc); + if (concreteType != null) { + /* important: introspect actual implementation (abstract class or + * interface doesn't have constructors, for one) + */ + beanDesc = config.introspect(concreteType); + return buildBeanDeserializer(ctxt, concreteType, beanDesc); + } + } + + // Otherwise, may want to check handlers for standard types, from superclass: + @SuppressWarnings("unchecked") + JsonDeserializer deser = (JsonDeserializer) findStdDeserializer(ctxt, type, beanDesc); + if (deser != null) { + return deser; + } + + // Otherwise: could the class be a Bean class? If not, bail out + if (!isPotentialBeanType(type.getRawClass())) { + return null; + } + // Use generic bean introspection to build deserializer + return buildBeanDeserializer(ctxt, type, beanDesc); + } + + @Override + public JsonDeserializer createBuilderBasedDeserializer( + DeserializationContext ctxt, JavaType valueType, BeanDescription beanDesc, + Class builderClass) + throws JsonMappingException + { + // First: need a BeanDescription for builder class + JavaType builderType = ctxt.constructType(builderClass); + BeanDescription builderDesc = ctxt.getConfig().introspectForBuilder(builderType); + return buildBuilderBasedDeserializer(ctxt, valueType, builderDesc); + } + + /** + * Method called by {@link BeanDeserializerFactory} to see if there might be a standard + * deserializer registered for given type. + */ + protected JsonDeserializer findStdDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + // note: we do NOT check for custom deserializers here, caller has already + // done that + JsonDeserializer deser = findDefaultDeserializer(ctxt, type, beanDesc); + // Also: better ensure these are post-processable? + if (deser != null) { + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deser = mod.modifyDeserializer(ctxt.getConfig(), beanDesc, deser); + } + } + } + return deser; + } + + protected JavaType materializeAbstractType(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + // May have multiple resolvers, call in precedence order until one returns non-null + for (AbstractTypeResolver r : _factoryConfig.abstractTypeResolvers()) { + JavaType concrete = r.resolveAbstractType(ctxt.getConfig(), beanDesc); + if (concrete != null) { + return concrete; + } + } + return null; + } + + /* + /********************************************************** + /* Public construction method beyond DeserializerFactory API: + /* can be called from outside as well as overridden by + /* sub-classes + /********************************************************** + */ + + /** + * Method that is to actually build a bean deserializer instance. + * All basic sanity checks have been done to know that what we have + * may be a valid bean type, and that there are no default simple + * deserializers. + */ + @SuppressWarnings("unchecked") + public JsonDeserializer buildBeanDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + // First: check what creators we can use, if any + ValueInstantiator valueInstantiator; + /* 04-Jun-2015, tatu: To work around [databind#636], need to catch the + * issue, defer; this seems like a reasonable good place for now. + * Note, however, that for non-Bean types (Collections, Maps) this + * probably won't work and needs to be added elsewhere. + */ + try { + valueInstantiator = findValueInstantiator(ctxt, beanDesc); + } catch (NoClassDefFoundError error) { + return new NoClassDefFoundDeserializer(error); + } + BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc); + builder.setValueInstantiator(valueInstantiator); + // And then setters for deserializing from JSON Object + addBeanProps(ctxt, beanDesc, builder); + addObjectIdReader(ctxt, beanDesc, builder); + + // managed/back reference fields/setters need special handling... first part + addReferenceProperties(ctxt, beanDesc, builder); + addInjectables(ctxt, beanDesc, builder); + + final DeserializationConfig config = ctxt.getConfig(); + // [JACKSON-440]: update builder now that all information is in? + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + builder = mod.updateBuilder(config, beanDesc, builder); + } + } + JsonDeserializer deserializer; + + /* 19-Mar-2012, tatu: This check used to be done earlier; but we have to defer + * it a bit to collect information on ObjectIdReader, for example. + */ + if (type.isAbstract() && !valueInstantiator.canInstantiate()) { + deserializer = builder.buildAbstract(); + } else { + deserializer = builder.build(); + } + + // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built: + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deserializer = mod.modifyDeserializer(config, beanDesc, deserializer); + } + } + return (JsonDeserializer) deserializer; + } + + /** + * Method for constructing a bean deserializer that uses specified + * intermediate Builder for binding data, and construction of the + * value instance. + * Note that implementation is mostly copied from the regular + * BeanDeserializer build method. + */ + @SuppressWarnings("unchecked") + protected JsonDeserializer buildBuilderBasedDeserializer( + DeserializationContext ctxt, JavaType valueType, BeanDescription builderDesc) + throws JsonMappingException + { + // Creators, anyone? (to create builder itself) + ValueInstantiator valueInstantiator = findValueInstantiator(ctxt, builderDesc); + final DeserializationConfig config = ctxt.getConfig(); + BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, builderDesc); + builder.setValueInstantiator(valueInstantiator); + // And then "with methods" for deserializing from JSON Object + addBeanProps(ctxt, builderDesc, builder); + addObjectIdReader(ctxt, builderDesc, builder); + + // managed/back reference fields/setters need special handling... first part + addReferenceProperties(ctxt, builderDesc, builder); + addInjectables(ctxt, builderDesc, builder); + + JsonPOJOBuilder.Value builderConfig = builderDesc.findPOJOBuilderConfig(); + final String buildMethodName = (builderConfig == null) ? + "build" : builderConfig.buildMethodName; + + // and lastly, find build method to use: + AnnotatedMethod buildMethod = builderDesc.findMethod(buildMethodName, null); + if (buildMethod != null) { // note: can't yet throw error; may be given build method + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(buildMethod.getMember(), config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + } + builder.setPOJOBuilder(buildMethod, builderConfig); + // this may give us more information... + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + builder = mod.updateBuilder(config, builderDesc, builder); + } + } + JsonDeserializer deserializer = builder.buildBuilderBased( + valueType, buildMethodName); + + // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built: + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deserializer = mod.modifyDeserializer(config, builderDesc, deserializer); + } + } + return (JsonDeserializer) deserializer; + } + + protected void addObjectIdReader(DeserializationContext ctxt, + BeanDescription beanDesc, BeanDeserializerBuilder builder) + throws JsonMappingException + { + ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo(); + if (objectIdInfo == null) { + return; + } + Class implClass = objectIdInfo.getGeneratorType(); + JavaType idType; + SettableBeanProperty idProp; + ObjectIdGenerator gen; + + ObjectIdResolver resolver = ctxt.objectIdResolverInstance(beanDesc.getClassInfo(), objectIdInfo); + + // Just one special case: Property-based generator is trickier + if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work + PropertyName propName = objectIdInfo.getPropertyName(); + idProp = builder.findProperty(propName); + if (idProp == null) { + throw new IllegalArgumentException("Invalid Object Id definition for " + +beanDesc.getBeanClass().getName()+": can not find property with name '"+propName+"'"); + } + idType = idProp.getType(); + gen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); + } else { + JavaType type = ctxt.constructType(implClass); + idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; + idProp = null; + gen = ctxt.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo); + } + // also: unlike with value deserializers, let's just resolve one we need here + JsonDeserializer deser = ctxt.findRootValueDeserializer(idType); + builder.setObjectIdReader(ObjectIdReader.construct(idType, + objectIdInfo.getPropertyName(), gen, deser, idProp, resolver)); + } + + @SuppressWarnings("unchecked") + public JsonDeserializer buildThrowableDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + // first: construct like a regular bean deserializer... + BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc); + builder.setValueInstantiator(findValueInstantiator(ctxt, beanDesc)); + + addBeanProps(ctxt, beanDesc, builder); + // (and assume there won't be any back references) + + // But then let's decorate things a bit + /* To resolve [JACKSON-95], need to add "initCause" as setter + * for exceptions (sub-classes of Throwable). + */ + AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS); + if (am != null) { // should never be null + SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am, + new PropertyName("cause")); + SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef, + am.getParameterType(0)); + if (prop != null) { + /* 21-Aug-2011, tatus: We may actually have found 'cause' property + * to set... but let's replace it just in case, + * otherwise can end up with odd errors. + */ + builder.addOrReplaceProperty(prop, true); + } + } + + // And also need to ignore "localizedMessage" + builder.addIgnorable("localizedMessage"); + // Java 7 also added "getSuppressed", skip if we have such data: + builder.addIgnorable("suppressed"); + /* As well as "message": it will be passed via constructor, + * as there's no 'setMessage()' method + */ + builder.addIgnorable("message"); + + // update builder now that all information is in? + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + builder = mod.updateBuilder(config, beanDesc, builder); + } + } + JsonDeserializer deserializer = builder.build(); + + /* At this point it ought to be a BeanDeserializer; if not, must assume + * it's some other thing that can handle deserialization ok... + */ + if (deserializer instanceof BeanDeserializer) { + deserializer = new ThrowableDeserializer((BeanDeserializer) deserializer); + } + + // may have modifier(s) that wants to modify or replace serializer we just built: + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + deserializer = mod.modifyDeserializer(config, beanDesc, deserializer); + } + } + return (JsonDeserializer) deserializer; + } + + /* + /********************************************************** + /* Helper methods for Bean deserializer construction, + /* overridable by sub-classes + /********************************************************** + */ + + /** + * Overridable method that constructs a {@link BeanDeserializerBuilder} + * which is used to accumulate information needed to create deserializer + * instance. + */ + protected BeanDeserializerBuilder constructBeanDeserializerBuilder(DeserializationContext ctxt, + BeanDescription beanDesc) { + return new BeanDeserializerBuilder(beanDesc, ctxt.getConfig()); + } + + /** + * Method called to figure out settable properties for the + * bean deserializer to use. + *

+ * Note: designed to be overridable, and effort is made to keep interface + * similar between versions. + */ + protected void addBeanProps(DeserializationContext ctxt, + BeanDescription beanDesc, BeanDeserializerBuilder builder) + throws JsonMappingException + { + final SettableBeanProperty[] creatorProps = + builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig()); + final boolean isConcrete = !beanDesc.getType().isAbstract(); + + // Things specified as "ok to ignore"? [JACKSON-77] + AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + boolean ignoreAny = false; + { + Boolean B = intr.findIgnoreUnknownProperties(beanDesc.getClassInfo()); + if (B != null) { + ignoreAny = B.booleanValue(); + builder.setIgnoreUnknownProperties(ignoreAny); + } + } + // Or explicit/implicit definitions? + Set ignored = ArrayBuilders.arrayToSet(intr.findPropertiesToIgnore(beanDesc.getClassInfo(), false)); + for (String propName : ignored) { + builder.addIgnorable(propName); + } + // Also, do we have a fallback "any" setter? + AnnotatedMethod anySetter = beanDesc.findAnySetter(); + if (anySetter != null) { + builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter)); + } + // NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any-setter + // Implicit ones via @JsonIgnore and equivalent? + if (anySetter == null) { + Collection ignored2 = beanDesc.getIgnoredPropertyNames(); + if (ignored2 != null) { + for (String propName : ignored2) { + // allow ignoral of similarly named JSON property, but do not force; + // latter means NOT adding this to 'ignored': + builder.addIgnorable(propName); + } + } + } + final boolean useGettersAsSetters = (ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS) + && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS)); + + // Ok: let's then filter out property definitions + List propDefs = filterBeanProps(ctxt, + beanDesc, builder, beanDesc.findProperties(), ignored); + + // After which we can let custom code change the set + if (_factoryConfig.hasDeserializerModifiers()) { + for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) { + propDefs = mod.updateProperties(ctxt.getConfig(), beanDesc, propDefs); + } + } + + // At which point we still have all kinds of properties; not all with mutators: + for (BeanPropertyDefinition propDef : propDefs) { + SettableBeanProperty prop = null; + /* 18-Oct-2013, tatu: Although constructor parameters have highest precedence, + * we need to do linkage (as per [databind#318]), and so need to start with + * other types, and only then create constructor parameter, if any. + */ + if (propDef.hasSetter()) { + JavaType propertyType = propDef.getSetter().getParameterType(0); + prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType); + } else if (propDef.hasField()) { + JavaType propertyType = propDef.getField().getType(); + prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType); + } else if (useGettersAsSetters && propDef.hasGetter()) { + /* May also need to consider getters + * for Map/Collection properties; but with lowest precedence + */ + AnnotatedMethod getter = propDef.getGetter(); + // should only consider Collections and Maps, for now? + Class rawPropertyType = getter.getRawType(); + if (Collection.class.isAssignableFrom(rawPropertyType) + || Map.class.isAssignableFrom(rawPropertyType)) { + prop = constructSetterlessProperty(ctxt, beanDesc, propDef); + } + } + // 25-Sep-2014, tatu: No point in finding constructor parameters for abstract types + // (since they are never used anyway) + if (isConcrete && propDef.hasConstructorParameter()) { + /* [JACKSON-700] If property is passed via constructor parameter, we must + * handle things in special way. Not sure what is the most optimal way... + * for now, let's just call a (new) method in builder, which does nothing. + */ + // but let's call a method just to allow custom builders to be aware... + final String name = propDef.getName(); + CreatorProperty cprop = null; + if (creatorProps != null) { + for (SettableBeanProperty cp : creatorProps) { + if (name.equals(cp.getName()) && (cp instanceof CreatorProperty)) { + cprop = (CreatorProperty) cp; + break; + } + } + } + if (cprop == null) { + throw ctxt.mappingException("Could not find creator property with name '%s' (in class %s)", + name, beanDesc.getBeanClass().getName()); + } + if (prop != null) { + cprop.setFallbackSetter(prop); + } + prop = cprop; + builder.addCreatorProperty(cprop); + continue; + } + + if (prop != null) { + Class[] views = propDef.findViews(); + if (views == null) { + // one more twist: if default inclusion disabled, need to force empty set of views + if (!ctxt.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) { + views = NO_VIEWS; + } + } + // one more thing before adding to builder: copy any metadata + prop.setViews(views); + builder.addProperty(prop); + } + } + } + + /** + * Helper method called to filter out explicit ignored properties, + * as well as properties that have "ignorable types". + * Note that this will not remove properties that have no + * setters. + */ + protected List filterBeanProps(DeserializationContext ctxt, + BeanDescription beanDesc, BeanDeserializerBuilder builder, + List propDefsIn, + Set ignored) + throws JsonMappingException + { + ArrayList result = new ArrayList( + Math.max(4, propDefsIn.size())); + HashMap,Boolean> ignoredTypes = new HashMap,Boolean>(); + // These are all valid setters, but we do need to introspect bit more + for (BeanPropertyDefinition property : propDefsIn) { + String name = property.getName(); + if (ignored.contains(name)) { // explicit ignoral using @JsonIgnoreProperties needs to block entries + continue; + } + if (!property.hasConstructorParameter()) { // never skip constructor params + Class rawPropertyType = null; + if (property.hasSetter()) { + rawPropertyType = property.getSetter().getRawParameterType(0); + } else if (property.hasField()) { + rawPropertyType = property.getField().getRawType(); + } + + // [JACKSON-429] Some types are declared as ignorable as well + if ((rawPropertyType != null) + && (isIgnorableType(ctxt.getConfig(), beanDesc, rawPropertyType, ignoredTypes))) { + // important: make ignorable, to avoid errors if value is actually seen + builder.addIgnorable(name); + continue; + } + } + result.add(property); + } + return result; + } + + /** + * Method that will find if bean has any managed- or back-reference properties, + * and if so add them to bean, to be linked during resolution phase. + */ + protected void addReferenceProperties(DeserializationContext ctxt, + BeanDescription beanDesc, BeanDeserializerBuilder builder) + throws JsonMappingException + { + // and then back references, not necessarily found as regular properties + Map refs = beanDesc.findBackReferenceProperties(); + if (refs != null) { + for (Map.Entry en : refs.entrySet()) { + String name = en.getKey(); + AnnotatedMember m = en.getValue(); + JavaType type; + if (m instanceof AnnotatedMethod) { + type = ((AnnotatedMethod) m).getParameterType(0); + } else { + type = m.getType(); + } + SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct( + ctxt.getConfig(), m); + builder.addBackReferenceProperty(name, constructSettableProperty( + ctxt, beanDesc, propDef, type)); + } + } + } + + /** + * Method called locate all members used for value injection (if any), + * constructor {@link com.fasterxml.jackson.databind.deser.impl.ValueInjector} instances, and add them to builder. + */ + protected void addInjectables(DeserializationContext ctxt, + BeanDescription beanDesc, BeanDeserializerBuilder builder) + throws JsonMappingException + { + Map raw = beanDesc.findInjectables(); + if (raw != null) { + boolean fixAccess = ctxt.canOverrideAccessModifiers(); + boolean forceAccess = fixAccess && ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS); + for (Map.Entry entry : raw.entrySet()) { + AnnotatedMember m = entry.getValue(); + if (fixAccess) { + m.fixAccess(forceAccess); // to ensure we can call it + } + builder.addInjectable(PropertyName.construct(m.getName()), + m.getType(), + beanDesc.getClassAnnotations(), m, entry.getKey()); + } + } + } + + /** + * Method called to construct fallback {@link SettableAnyProperty} + * for handling unknown bean properties, given a method that + * has been designated as such setter. + */ + protected SettableAnyProperty constructAnySetter(DeserializationContext ctxt, + BeanDescription beanDesc, AnnotatedMethod setter) + throws JsonMappingException + { + if (ctxt.canOverrideAccessModifiers()) { + setter.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); // to ensure we can call it + } + // we know it's a 2-arg method, second arg is the value + JavaType type = setter.getParameterType(1); + BeanProperty.Std property = new BeanProperty.Std(PropertyName.construct(setter.getName()), + type, null, beanDesc.getClassAnnotations(), setter, + PropertyMetadata.STD_OPTIONAL); + type = resolveType(ctxt, beanDesc, type, setter); + + /* AnySetter can be annotated with @JsonDeserialize (etc) just like a + * regular setter... so let's see if those are used. + * Returns null if no annotations, in which case binding will + * be done at a later point. + */ + JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, setter); + /* Otherwise, method may specify more specific (sub-)class for + * value (no need to check if explicit deser was specified): + */ + type = modifyTypeByAnnotation(ctxt, setter, type); + if (deser == null) { + deser = type.getValueHandler(); + } + TypeDeserializer typeDeser = type.getTypeHandler(); + return new SettableAnyProperty(property, setter, type, + deser, typeDeser); + } + + /** + * Method that will construct a regular bean property setter using + * the given setter method. + * + * @return Property constructed, if any; or null to indicate that + * there should be no property based on given definitions. + */ + protected SettableBeanProperty constructSettableProperty(DeserializationContext ctxt, + BeanDescription beanDesc, BeanPropertyDefinition propDef, + JavaType propType0) + throws JsonMappingException + { + // need to ensure method is callable (for non-public) + AnnotatedMember mutator = propDef.getNonConstructorMutator(); + if (ctxt.canOverrideAccessModifiers()) { + mutator.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + // note: this works since we know there's exactly one argument for methods + BeanProperty.Std property = new BeanProperty.Std(propDef.getFullName(), + propType0, propDef.getWrapperName(), + beanDesc.getClassAnnotations(), mutator, propDef.getMetadata()); + JavaType type = resolveType(ctxt, beanDesc, propType0, mutator); + // did type change? + if (type != propType0) { + property = property.withType(type); + } + + /* First: does the Method specify the deserializer to use? + * If so, let's use it. + */ + JsonDeserializer propDeser = findDeserializerFromAnnotation(ctxt, mutator); + type = modifyTypeByAnnotation(ctxt, mutator, type); + TypeDeserializer typeDeser = type.getTypeHandler(); + SettableBeanProperty prop; + if (mutator instanceof AnnotatedMethod) { + prop = new MethodProperty(propDef, type, typeDeser, + beanDesc.getClassAnnotations(), (AnnotatedMethod) mutator); + } else { + prop = new FieldProperty(propDef, type, typeDeser, + beanDesc.getClassAnnotations(), (AnnotatedField) mutator); + } + if (propDeser != null) { + prop = prop.withValueDeserializer(propDeser); + } + // [JACKSON-235]: need to retain name of managed forward references: + AnnotationIntrospector.ReferenceProperty ref = propDef.findReferenceType(); + if (ref != null && ref.isManagedReference()) { + prop.setManagedReferenceName(ref.getName()); + } + ObjectIdInfo objectIdInfo = propDef.findObjectIdInfo(); + if(objectIdInfo != null){ + prop.setObjectIdInfo(objectIdInfo); + } + return prop; + } + + /** + * Method that will construct a regular bean property setter using + * the given setter method. + */ + protected SettableBeanProperty constructSetterlessProperty(DeserializationContext ctxt, + BeanDescription beanDesc, BeanPropertyDefinition propDef) + throws JsonMappingException + { + final AnnotatedMethod getter = propDef.getGetter(); + // need to ensure it is callable now: + if (ctxt.canOverrideAccessModifiers()) { + getter.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + JavaType type = getter.getType(); + // First: does the Method specify the deserializer to use? If so, let's use it. + JsonDeserializer propDeser = findDeserializerFromAnnotation(ctxt, getter); + type = modifyTypeByAnnotation(ctxt, getter, type); + // As per [Issue#501], need full resolution: + type = resolveType(ctxt, beanDesc, type, getter); + TypeDeserializer typeDeser = type.getTypeHandler(); + SettableBeanProperty prop = new SetterlessProperty(propDef, type, typeDeser, + beanDesc.getClassAnnotations(), getter); + if (propDeser != null) { + prop = prop.withValueDeserializer(propDeser); + } + return prop; + } + + /* + /********************************************************** + /* Helper methods for Bean deserializer, other + /********************************************************** + */ + + /** + * Helper method used to skip processing for types that we know + * can not be (i.e. are never consider to be) beans: + * things like primitives, Arrays, Enums, and proxy types. + *

+ * Note that usually we shouldn't really be getting these sort of + * types anyway; but better safe than sorry. + */ + protected boolean isPotentialBeanType(Class type) + { + String typeStr = ClassUtil.canBeABeanType(type); + if (typeStr != null) { + throw new IllegalArgumentException("Can not deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean"); + } + if (ClassUtil.isProxyType(type)) { + throw new IllegalArgumentException("Can not deserialize Proxy class "+type.getName()+" as a Bean"); + } + /* also: can't deserialize some local classes: static are ok; in-method not; + * and with [JACKSON-594], other non-static inner classes are ok + */ + typeStr = ClassUtil.isLocalType(type, true); + if (typeStr != null) { + throw new IllegalArgumentException("Can not deserialize Class "+type.getName()+" (of type "+typeStr+") as a Bean"); + } + return true; + } + + /** + * Helper method that will check whether given raw type is marked as always ignorable + * (for purpose of ignoring properties with type) + */ + protected boolean isIgnorableType(DeserializationConfig config, BeanDescription beanDesc, + Class type, Map,Boolean> ignoredTypes) + { + Boolean status = ignoredTypes.get(type); + if (status != null) { + return status.booleanValue(); + } + BeanDescription desc = config.introspectClassAnnotations(type); + status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo()); + // We default to 'false', i.e. not ignorable + return (status == null) ? false : status.booleanValue(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,182 @@ +package com.fasterxml.jackson.databind.deser; + +import java.util.List; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.type.ArrayType; +import com.fasterxml.jackson.databind.type.CollectionLikeType; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.databind.type.MapLikeType; +import com.fasterxml.jackson.databind.type.MapType; +import com.fasterxml.jackson.databind.type.ReferenceType; + +/** + * Abstract class that defines API for objects that can be registered + * to participate in constructing {@link JsonDeserializer} instances + * (via {@link DeserializerFactory}). + * This is typically done by modules that want alter some aspects of deserialization + * process; and is preferable to sub-classing of {@link BeanDeserializerFactory}. + *

+ * Note that Jackson 2.2 adds more methods for customization; with earlier versions + * only {@link BeanDeserializer} instances could be modified, but with 2.2 all types + * of deserializers can be changed. + *

+ * Sequence in which callback methods are called for {@link BeanDeserializer} is: + *

    + *
  1. {@link #updateProperties} is called once all property definitions are + * collected, and initial filtering (by ignorable type and explicit ignoral-by-bean) + * has been performed. + *
  2. + *
  3. {@link #updateBuilder} is called once all initial pieces for building deserializer + * have been collected + *
  4. + *
  5. {@link #modifyDeserializer} is called after deserializer has been built + * by {@link BeanDeserializerBuilder} + * but before it is returned to be used + *
  6. + *
+ *

+ * For other types of deserializers, methods called depend on type of values for + * which deserializer is being constructed; and only a single method is called + * since the process does not involve builders (unlike that of {@link BeanDeserializer}. + *

+ * Default method implementations are "no-op"s, meaning that methods are implemented + * but have no effect; this is mostly so that new methods can be added in later + * versions. + */ +public abstract class BeanDeserializerModifier +{ + /** + * Method called by {@link BeanDeserializerFactory} when it has collected + * initial list of {@link BeanPropertyDefinition}s, and done basic by-name + * and by-type filtering, but before constructing builder or actual + * property handlers; or arranging order. + * + * The most common changes to make at this point are to completely remove + * specified properties, or rename then: other modifications are easier + * to make at later points. + */ + public List updateProperties(DeserializationConfig config, + BeanDescription beanDesc, List propDefs) { + return propDefs; + } + + /** + * Method called by {@link BeanDeserializerFactory} when it has collected + * basic information such as tentative list of properties to deserialize. + * + * Implementations may choose to modify state of builder (to affect deserializer being + * built), or even completely replace it (if they want to build different kind of + * deserializer). Typically changes mostly concern set of properties to deserialize. + */ + public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, + BeanDescription beanDesc, BeanDeserializerBuilder builder) { + return builder; + } + + /** + * Method called by {@link BeanDeserializerFactory} after constructing default + * bean deserializer instance with properties collected and ordered earlier. + * Implementations can modify or replace given deserializer and return deserializer + * to use. Note that although initial deserializer being passed is of type + * {@link BeanDeserializer}, modifiers may return deserializers of other types; + * and this is why implementations must check for type before casting. + */ + public JsonDeserializer modifyDeserializer(DeserializationConfig config, + BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /* + /********************************************************** + /* Callback methods for other types (since 2.2) + /********************************************************** + */ + + /** + * @since 2.2 + */ + public JsonDeserializer modifyEnumDeserializer(DeserializationConfig config, + JavaType type, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * @since 2.7 + */ + public JsonDeserializer modifyReferenceDeserializer(DeserializationConfig config, + ReferenceType type, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * Method called by {@link DeserializerFactory} after it has constructed the + * standard deserializer for given + * {@link ArrayType} + * to make it possible to either replace or augment this deserializer with + * additional functionality. + * + * @param config Configuration in use + * @param valueType Type of the value deserializer is used for. + * @param beanDesc Description f + * @param deserializer Default deserializer that would be used. + * + * @return Deserializer to use; either deserializer that was passed + * in, or an instance method constructed. + * + * @since 2.2 + */ + public JsonDeserializer modifyArrayDeserializer(DeserializationConfig config, + ArrayType valueType, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * @since 2.2 + */ + public JsonDeserializer modifyCollectionDeserializer(DeserializationConfig config, + CollectionType type, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * @since 2.2 + */ + public JsonDeserializer modifyCollectionLikeDeserializer(DeserializationConfig config, + CollectionLikeType type, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * @since 2.2 + */ + public JsonDeserializer modifyMapDeserializer(DeserializationConfig config, + MapType type, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * @since 2.2 + */ + public JsonDeserializer modifyMapLikeDeserializer(DeserializationConfig config, + MapLikeType type, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer; + } + + /** + * Method called by {@link DeserializerFactory} after it has constructed the + * standard key deserializer for given key type. + * This make it possible to replace the default key deserializer, or augment + * it somehow (including optional use of default deserializer with occasional + * override). + * + * @since 2.2 + */ + public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config, + JavaType type, KeyDeserializer deserializer) { + return deserializer; + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,704 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.impl.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.util.NameTransformer; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Class that handles deserialization using a separate + * Builder class, which is used for data binding and + * produces actual deserialized value at the end + * of data binding. + *

+ * Note on implementation: much of code has been copied from + * {@link BeanDeserializer}; there may be opportunities to + * refactor this in future. + */ +public class BuilderBasedDeserializer + extends BeanDeserializerBase +{ + private static final long serialVersionUID = 1L; + + protected final AnnotatedMethod _buildMethod; + + /* + /********************************************************** + /* Life-cycle, construction, initialization + /********************************************************** + */ + + /** + * Constructor used by {@link BeanDeserializerBuilder}. + */ + public BuilderBasedDeserializer(BeanDeserializerBuilder builder, + BeanDescription beanDesc, + BeanPropertyMap properties, Map backRefs, + HashSet ignorableProps, boolean ignoreAllUnknown, + boolean hasViews) + { + super(builder, beanDesc, properties, backRefs, + ignorableProps, ignoreAllUnknown, hasViews); + _buildMethod = builder.getBuildMethod(); + // 05-Mar-2012, tatu: Can not really make Object Ids work with builders, not yet anyway + if (_objectIdReader != null) { + throw new IllegalArgumentException("Can not use Object Id with Builder-based deserialization (type " + +beanDesc.getType()+")"); + } + } + + /** + * Copy-constructor that can be used by sub-classes to allow + * copy-on-write styling copying of settings of an existing instance. + */ + protected BuilderBasedDeserializer(BuilderBasedDeserializer src) + { + this(src, src._ignoreAllUnknown); + } + + protected BuilderBasedDeserializer(BuilderBasedDeserializer src, boolean ignoreAllUnknown) + { + super(src, ignoreAllUnknown); + _buildMethod = src._buildMethod; + } + + protected BuilderBasedDeserializer(BuilderBasedDeserializer src, NameTransformer unwrapper) { + super(src, unwrapper); + _buildMethod = src._buildMethod; + } + + public BuilderBasedDeserializer(BuilderBasedDeserializer src, ObjectIdReader oir) { + super(src, oir); + _buildMethod = src._buildMethod; + } + + public BuilderBasedDeserializer(BuilderBasedDeserializer src, HashSet ignorableProps) { + super(src, ignorableProps); + _buildMethod = src._buildMethod; + } + + @Override + public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + { + /* main thing really is to just enforce ignoring of unknown + * properties; since there may be multiple unwrapped values + * and properties for all may be interleaved... + */ + return new BuilderBasedDeserializer(this, unwrapper); + } + + @Override + public BuilderBasedDeserializer withObjectIdReader(ObjectIdReader oir) { + return new BuilderBasedDeserializer(this, oir); + } + + @Override + public BuilderBasedDeserializer withIgnorableProperties(HashSet ignorableProps) { + return new BuilderBasedDeserializer(this, ignorableProps); + } + + @Override + protected BeanAsArrayBuilderDeserializer asArrayDeserializer() { + SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder(); + return new BeanAsArrayBuilderDeserializer(this, props, _buildMethod); + } + + /* + /********************************************************** + /* JsonDeserializer implementation + /********************************************************** + */ + + protected final Object finishBuild(DeserializationContext ctxt, Object builder) + throws IOException + { + // As per [databind#777], allow returning builder itself + if (null == _buildMethod) { + return builder; + } + try { + return _buildMethod.getMember().invoke(builder); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; + } + } + + /** + * Main deserialization method for bean-based objects (POJOs). + */ + @Override + public final Object deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + JsonToken t = jp.getCurrentToken(); + + // common case first: + if (t == JsonToken.START_OBJECT) { + t = jp.nextToken(); + if (_vanillaProcessing) { + return finishBuild(ctxt, vanillaDeserialize(jp, ctxt, t)); + } + Object builder = deserializeFromObject(jp, ctxt); + return finishBuild(ctxt, builder); + } + // and then others, generally requiring use of @JsonCreator + switch (t) { + case VALUE_STRING: + return finishBuild(ctxt, deserializeFromString(jp, ctxt)); + case VALUE_NUMBER_INT: + return finishBuild(ctxt, deserializeFromNumber(jp, ctxt)); + case VALUE_NUMBER_FLOAT: + return finishBuild(ctxt, deserializeFromDouble(jp, ctxt)); + case VALUE_EMBEDDED_OBJECT: + return jp.getEmbeddedObject(); + case VALUE_TRUE: + case VALUE_FALSE: + return finishBuild(ctxt, deserializeFromBoolean(jp, ctxt)); + case START_ARRAY: + // these only work if there's a (delegating) creator... + return finishBuild(ctxt, deserializeFromArray(jp, ctxt)); + case FIELD_NAME: + case END_OBJECT: // added to resolve [JACKSON-319], possible related issues + return finishBuild(ctxt, deserializeFromObject(jp, ctxt)); + default: + throw ctxt.mappingException(handledType()); + } + } + + /** + * Secondary deserialization method, called in cases where POJO + * instance is created as part of deserialization, potentially + * after collecting some or all of the properties to set. + */ + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt, + Object builder) + throws IOException, JsonProcessingException + { + /* Important: we call separate method which does NOT call + * 'finishBuild()', to avoid problems with recursion + */ + return finishBuild(ctxt, _deserialize(jp, ctxt, builder)); + } + + /* + /********************************************************** + /* Concrete deserialization methods + /********************************************************** + */ + + protected final Object _deserialize(JsonParser jp, + DeserializationContext ctxt, Object builder) + throws IOException, JsonProcessingException + { + if (_injectables != null) { + injectValues(ctxt, builder); + } + if (_unwrappedPropertyHandler != null) { + return deserializeWithUnwrapped(jp, ctxt, builder); + } + if (_externalTypeIdHandler != null) { + return deserializeWithExternalTypeId(jp, ctxt, builder); + } + if (_needViewProcesing) { + Class view = ctxt.getActiveView(); + if (view != null) { + return deserializeWithView(jp, ctxt, builder, view); + } + } + JsonToken t = jp.getCurrentToken(); + // 23-Mar-2010, tatu: In some cases, we start with full JSON object too... + if (t == JsonToken.START_OBJECT) { + t = jp.nextToken(); + } + for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { + String propName = jp.getCurrentName(); + // Skip field name: + jp.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + + if (prop != null) { // normal case + try { + builder = prop.deserializeSetAndReturn(jp, ctxt, builder); + } catch (Exception e) { + wrapAndThrow(e, builder, propName, ctxt); + } + continue; + } + handleUnknownVanilla(jp, ctxt, handledType(), propName); + } + return builder; + } + + /** + * Streamlined version that is only used when no "special" + * features are enabled. + */ + private final Object vanillaDeserialize(JsonParser jp, + DeserializationContext ctxt, JsonToken t) + throws IOException, JsonProcessingException + { + Object bean = _valueInstantiator.createUsingDefault(ctxt); + for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { + String propName = jp.getCurrentName(); + // Skip field name: + jp.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + try { + bean = prop.deserializeSetAndReturn(jp, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + } else { + handleUnknownVanilla(jp, ctxt, bean, propName); + } + } + return bean; + } + + /** + * General version used when handling needs more advanced + * features. + */ + @Override + public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (_nonStandardCreation) { + if (_unwrappedPropertyHandler != null) { + return deserializeWithUnwrapped(jp, ctxt); + } + if (_externalTypeIdHandler != null) { + return deserializeWithExternalTypeId(jp, ctxt); + } + return deserializeFromObjectUsingNonDefault(jp, ctxt); + } + Object bean = _valueInstantiator.createUsingDefault(ctxt); + if (_injectables != null) { + injectValues(ctxt, bean); + } + if (_needViewProcesing) { + Class view = ctxt.getActiveView(); + if (view != null) { + return deserializeWithView(jp, ctxt, bean, view); + } + } + for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { + String propName = jp.getCurrentName(); + // Skip field name: + jp.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + try { + bean = prop.deserializeSetAndReturn(jp, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + handleUnknownVanilla(jp, ctxt, bean, propName); + } + return bean; + } + + /** + * Method called to deserialize bean using "property-based creator": + * this means that a non-default constructor or factory method is + * called, and then possibly other setters. The trick is that + * values for creator method need to be buffered, first; and + * due to non-guaranteed ordering possibly some other properties + * as well. + */ + @Override + @SuppressWarnings("resource") + protected final Object _deserializeUsingPropertyBased(final JsonParser jp, + final DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader); + + // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases + TokenBuffer unknown = null; + + JsonToken t = jp.getCurrentToken(); + for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { + String propName = jp.getCurrentName(); + jp.nextToken(); // to point to value + // creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, creatorProp.deserialize(jp, ctxt))) { + jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + continue; // never gets here + } + // polymorphic? + if (bean.getClass() != _beanType.getRawClass()) { + return handlePolymorphic(jp, ctxt, bean, unknown); + } + if (unknown != null) { // nope, just extra unknown stuff... + bean = handleUnknownProperties(ctxt, bean, unknown); + } + // or just clean? + return _deserialize(jp, ctxt, bean); + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + buffer.bufferProperty(prop, prop.deserialize(jp, ctxt)); + continue; + } + // As per [JACKSON-313], things marked as ignorable should not be + // passed to any setter + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(jp, ctxt, handledType(), propName); + continue; + } + // "any property"? + if (_anySetter != null) { + buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt)); + continue; + } + // Ok then, let's collect the whole field; name and value + if (unknown == null) { + unknown = new TokenBuffer(jp, ctxt); + } + unknown.writeFieldName(propName); + unknown.copyCurrentStructure(jp); + } + + // We hit END_OBJECT, so: + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; // never gets here + } + if (unknown != null) { + // polymorphic? + if (bean.getClass() != _beanType.getRawClass()) { + return handlePolymorphic(null, ctxt, bean, unknown); + } + // no, just some extra unknown properties + return handleUnknownProperties(ctxt, bean, unknown); + } + return bean; + } + + /* + /********************************************************** + /* Deserializing when we have to consider an active View + /********************************************************** + */ + + protected final Object deserializeWithView(JsonParser jp, DeserializationContext ctxt, + Object bean, Class activeView) + throws IOException, JsonProcessingException + { + JsonToken t = jp.getCurrentToken(); + for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { + String propName = jp.getCurrentName(); + // Skip field name: + jp.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + if (!prop.visibleInView(activeView)) { + jp.skipChildren(); + continue; + } + try { + bean = prop.deserializeSetAndReturn(jp, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + handleUnknownVanilla(jp, ctxt, bean, propName); + } + return bean; + } + + /* + /********************************************************** + /* Handling for cases where we have "unwrapped" values + /********************************************************** + */ + + /** + * Method called when there are declared "unwrapped" properties + * which need special handling + */ + @SuppressWarnings("resource") + protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt)); + } + if (_propertyBasedCreator != null) { + return deserializeUsingPropertyBasedWithUnwrapped(jp, ctxt); + } + TokenBuffer tokens = new TokenBuffer(jp, ctxt); + tokens.writeStartObject(); + Object bean = _valueInstantiator.createUsingDefault(ctxt); + + if (_injectables != null) { + injectValues(ctxt, bean); + } + + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + + for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { + String propName = jp.getCurrentName(); + jp.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + if (activeView != null && !prop.visibleInView(activeView)) { + jp.skipChildren(); + continue; + } + try { + bean = prop.deserializeSetAndReturn(jp, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + // ignorable things should be ignored + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(jp, ctxt, bean, propName); + continue; + } + // but... others should be passed to unwrapped property deserializers + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(jp); + // how about any setter? We'll get copies but... + if (_anySetter != null) { + try { + _anySetter.deserializeAndSet(jp, ctxt, bean, propName); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + } + tokens.writeEndObject(); + _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens); + return bean; + } + + @SuppressWarnings("resource") + protected Object deserializeWithUnwrapped(JsonParser jp, + DeserializationContext ctxt, Object bean) + throws IOException, JsonProcessingException + { + JsonToken t = jp.getCurrentToken(); + if (t == JsonToken.START_OBJECT) { + t = jp.nextToken(); + } + TokenBuffer tokens = new TokenBuffer(jp, ctxt); + tokens.writeStartObject(); + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { + String propName = jp.getCurrentName(); + SettableBeanProperty prop = _beanProperties.find(propName); + jp.nextToken(); + if (prop != null) { // normal case + if (activeView != null && !prop.visibleInView(activeView)) { + jp.skipChildren(); + continue; + } + try { + bean = prop.deserializeSetAndReturn(jp, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(jp, ctxt, bean, propName); + continue; + } + // but... others should be passed to unwrapped property deserializers + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(jp); + // how about any setter? We'll get copies but... + if (_anySetter != null) { + _anySetter.deserializeAndSet(jp, ctxt, bean, propName); + } + } + tokens.writeEndObject(); + _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens); + return bean; + } + + @SuppressWarnings("resource") + protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, + DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); + + TokenBuffer tokens = new TokenBuffer(p, ctxt); + tokens.writeStartObject(); + + JsonToken t = p.getCurrentToken(); + for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String propName = p.getCurrentName(); + p.nextToken(); // to point to value + // creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) { + t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT + Object bean; + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + continue; // never gets here + } + // if so, need to copy all remaining tokens into buffer + while (t == JsonToken.FIELD_NAME) { + p.nextToken(); // to skip name + tokens.copyCurrentStructure(p); + t = p.nextToken(); + } + tokens.writeEndObject(); + if (bean.getClass() != _beanType.getRawClass()) { + // !!! 08-Jul-2011, tatu: Could probably support; but for now + // it's too complicated, so bail out + throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values"); + } + return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { + buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); + continue; + } + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(p, ctxt, handledType(), propName); + continue; + } + tokens.writeFieldName(propName); + tokens.copyCurrentStructure(p); + // "any property"? + if (_anySetter != null) { + buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); + } + } + + // We hit END_OBJECT, so: + Object bean; + // !!! 15-Feb-2012, tatu: Need to modify creator to use Builder! + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; // never gets here + } + return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); + } + + /* + /********************************************************** + /* Handling for cases where we have property/-ies with + /* external type id + /********************************************************** + */ + + protected Object deserializeWithExternalTypeId(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (_propertyBasedCreator != null) { + return deserializeUsingPropertyBasedWithExternalTypeId(jp, ctxt); + } + return deserializeWithExternalTypeId(jp, ctxt, _valueInstantiator.createUsingDefault(ctxt)); + } + + protected Object deserializeWithExternalTypeId(JsonParser jp, + DeserializationContext ctxt, Object bean) + throws IOException, JsonProcessingException + { + final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + final ExternalTypeHandler ext = _externalTypeIdHandler.start(); + for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) { + String propName = jp.getCurrentName(); + jp.nextToken(); + SettableBeanProperty prop = _beanProperties.find(propName); + if (prop != null) { // normal case + if (activeView != null && !prop.visibleInView(activeView)) { + jp.skipChildren(); + continue; + } + try { + bean = prop.deserializeSetAndReturn(jp, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } + // ignorable things should be ignored + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + handleIgnoredProperty(jp, ctxt, bean, propName); + continue; + } + // but others are likely to be part of external type id thingy... + if (ext.handlePropertyValue(jp, ctxt, propName, bean)) { + continue; + } + // if not, the usual fallback handling: + if (_anySetter != null) { + try { + _anySetter.deserializeAndSet(jp, ctxt, bean, propName); + } catch (Exception e) { + wrapAndThrow(e, bean, propName, ctxt); + } + continue; + } else { + // Unknown: let's call handler method + handleUnknownProperty(jp, ctxt, bean, propName); + } + } + // and when we get this far, let's try finalizing the deal: + return ext.complete(jp, ctxt, bean); + } + + protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser jp, + DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + // !!! 04-Mar-2012, TODO: Need to fix -- will not work as is... + throw new IllegalStateException("Deserialization with Builder, External type id, @JsonCreator not yet implemented"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,43 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.*; + +/** + * Add-on interface that {@link JsonDeserializer}s can implement to get a callback + * that can be used to create contextual (context-dependent) instances of + * deserializer to use for handling properties of supported type. + * This can be useful + * for deserializers that can be configured by annotations, or should otherwise + * have differing behavior depending on what kind of property is being deserialized. + *

+ * Note that in cases where deserializer needs both contextualization and + * resolution -- that is, implements both this interface and {@link ResolvableDeserializer} + * -- resolution via {@link ResolvableDeserializer} occurs first, and contextual + * resolution (via this interface) later on. + */ +public interface ContextualDeserializer +{ + /** + * Method called to see if a different (or differently configured) deserializer + * is needed to deserialize values of specified property. + * Note that instance that this method is called on is typically shared one and + * as a result method should NOT modify this instance but rather construct + * and return a new instance. This instance should only be returned as-is, in case + * it is already suitable for use. + * + * @param ctxt Deserialization context to access configuration, additional + * deserializers that may be needed by this deserializer + * @param property Method, field or constructor parameter that represents the property + * (and is used to assign deserialized value). + * Should be available; but there may be cases where caller can not provide it and + * null is passed instead (in which case impls usually pass 'this' deserializer as is) + * + * @return Deserializer to use for deserializing values of specified property; + * may be this instance or a new instance. + * + * @throws JsonMappingException + */ + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ContextualKeyDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ContextualKeyDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ContextualKeyDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,34 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.*; + +/** + * Add-on interface that {@link KeyDeserializer}s can implement to get a callback + * that can be used to create contextual instances of key deserializer to use for + * handling Map keys of supported type. This can be useful + * for key deserializers that can be configured by annotations, or should otherwise + * have differing behavior depending on what kind of Map property keys are being deserialized. + */ +public interface ContextualKeyDeserializer +{ + /** + * Method called to see if a different (or differently configured) key deserializer + * is needed to deserialize keys of specified Map property. + * Note that instance that this method is called on is typically shared one and + * as a result method should NOT modify this instance but rather construct + * and return a new instance. This instance should only be returned as-is, in case + * it is already suitable for use. + * + * @param ctxt Deserialization context to access configuration, additional + * deserializers that may be needed by this deserializer + * @param property Method, field or constructor parameter that declared Map for which + * contextual instance will be used. Will not be available when deserializing root-level + * Map value; otherwise should not be null. + * + * @return Key deserializer to use for deserializing keys specified Map property, + * may be this instance or a new instance. + */ + public KeyDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/CreatorProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/CreatorProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/CreatorProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,221 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * This concrete sub-class implements property that is passed + * via Creator (constructor or static factory method). + * It is not a full-featured implementation in that its set method + * should never be called -- instead, value must separately passed. + *

+ * Note on injectable values: unlike with other mutators, where + * deserializer and injecting are separate, here we treat the two as related + * things. This is necessary to add proper priority, as well as to simplify + * coordination. + */ +public class CreatorProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + /** + * Placeholder that represents constructor parameter, when it is created + * from actual constructor. + * May be null when a synthetic instance is created. + */ + protected final AnnotatedParameter _annotated; + + /** + * Id of value to inject, if value injection should be used for this parameter + * (in addition to, or instead of, regular deserialization). + */ + protected final Object _injectableValueId; + + /** + * @since 2.1 + */ + protected final int _creatorIndex; + + /** + * In special cases, when implementing "updateValue", we can not use + * constructors or factory methods, but have to fall back on using a + * setter (or mutable field property). If so, this refers to that fallback + * accessor. + *

+ * Mutable only to allow setting after construction, but must be strictly + * set before any use. + * + * @since 2.3 + */ + protected SettableBeanProperty _fallbackSetter; + + /** + * @param name Name of the logical property + * @param type Type of the property, used to find deserializer + * @param typeDeser Type deserializer to use for handling polymorphic type + * information, if one is needed + * @param contextAnnotations Contextual annotations (usually by class that + * declares creator [constructor, factory method] that includes + * this property) + * @param param Representation of property, constructor or factory + * method parameter; used for accessing annotations of the property + * @param index Index of this property within creator invocation + * + * @since 2.3 + */ + public CreatorProperty(PropertyName name, JavaType type, PropertyName wrapperName, + TypeDeserializer typeDeser, + Annotations contextAnnotations, AnnotatedParameter param, + int index, Object injectableValueId, + PropertyMetadata metadata) + { + super(name, type, wrapperName, typeDeser, contextAnnotations, metadata); + _annotated = param; + _creatorIndex = index; + _injectableValueId = injectableValueId; + _fallbackSetter = null; + } + + /** + * @since 2.3 + */ + protected CreatorProperty(CreatorProperty src, PropertyName newName) { + super(src, newName); + _annotated = src._annotated; + _creatorIndex = src._creatorIndex; + _injectableValueId = src._injectableValueId; + _fallbackSetter = src._fallbackSetter; + } + + protected CreatorProperty(CreatorProperty src, JsonDeserializer deser) { + super(src, deser); + _annotated = src._annotated; + _creatorIndex = src._creatorIndex; + _injectableValueId = src._injectableValueId; + _fallbackSetter = src._fallbackSetter; + } + + @Override + public CreatorProperty withName(PropertyName newName) { + return new CreatorProperty(this, newName); + } + + @Override + public CreatorProperty withValueDeserializer(JsonDeserializer deser) { + return new CreatorProperty(this, deser); + } + + /** + * NOTE: one exception to immutability, due to problems with CreatorProperty instances + * being shared between Bean, separate PropertyBasedCreator + * + * @since 2.6.0 + */ + public void setFallbackSetter(SettableBeanProperty fallbackSetter) { + _fallbackSetter = fallbackSetter; + } + + /** + * Method that can be called to locate value to be injected for this + * property, if it is configured for this. + */ + public Object findInjectableValue(DeserializationContext context, Object beanInstance) + { + if (_injectableValueId == null) { + throw new IllegalStateException("Property '"+getName() + +"' (type "+getClass().getName()+") has no injectable value id configured"); + } + return context.findInjectableValue(_injectableValueId, this, beanInstance); + } + + /** + * Method to find value to inject, and inject it to this property. + */ + public void inject(DeserializationContext context, Object beanInstance) + throws IOException + { + set(beanInstance, findInjectableValue(context, beanInstance)); + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + @Override + public A getAnnotation(Class acls) { + if (_annotated == null) { + return null; + } + return _annotated.getAnnotation(acls); + } + + @Override public AnnotatedMember getMember() { return _annotated; } + + @Override public int getCreatorIndex() { + return _creatorIndex; + } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, + Object instance) + throws IOException, JsonProcessingException + { + set(instance, deserialize(jp, ctxt)); + } + + @Override + public Object deserializeSetAndReturn(JsonParser jp, + DeserializationContext ctxt, Object instance) + throws IOException, JsonProcessingException + { + return setAndReturn(instance, deserialize(jp, ctxt)); + } + + @Override + public void set(Object instance, Object value) throws IOException + { + /* Hmmmh. Should we return quietly (NOP), or error? + * Perhaps better to throw an exception, since it's generally an error. + */ + if (_fallbackSetter == null) { + throw new IllegalStateException("No fallback setter/field defined: can not use creator property for " + +getClass().getName()); + } + _fallbackSetter.set(instance, value); + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException + { + if (_fallbackSetter == null) { + throw new IllegalStateException("No fallback setter/field defined: can not use creator property for " + +getClass().getName()); + } + return _fallbackSetter.setAndReturn(instance, value); + } + + @Override + public Object getInjectableValueId() { + return _injectableValueId; + } + + @Override + public String toString() { return "[creator property, name '"+getName()+"'; inject id '"+_injectableValueId+"']"; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DataFormatReaders.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DataFormatReaders.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DataFormatReaders.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,388 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.format.*; +import com.fasterxml.jackson.core.io.MergedStream; + +import com.fasterxml.jackson.databind.*; + +/** + * Alternative to {@link DataFormatDetector} that needs to be used when + * using data-binding. + * + * @since 2.1 + */ +public class DataFormatReaders +{ + /** + * 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 readers which both represent data formats to + * detect (in precedence order, starting with highest) and contain + * factories used for actual detection. + */ + protected final ObjectReader[] _readers; + + /** + * 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 DataFormatReaders(ObjectReader... detectors) { + this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH, + DEFAULT_MAX_INPUT_LOOKAHEAD); + } + + public DataFormatReaders(Collection detectors) { + this(detectors.toArray(new ObjectReader[detectors.size()])); + } + + private DataFormatReaders(ObjectReader[] detectors, + MatchStrength optMatch, MatchStrength minMatch, + int maxInputLookahead) + { + _readers = detectors; + _optimalMatch = optMatch; + _minimalMatch = minMatch; + _maxInputLookahead = maxInputLookahead; + } + + /* + /********************************************************** + /* Fluent factories for changing match settings + /********************************************************** + */ + + public DataFormatReaders withOptimalMatch(MatchStrength optMatch) { + if (optMatch == _optimalMatch) { + return this; + } + return new DataFormatReaders(_readers, optMatch, _minimalMatch, _maxInputLookahead); + } + + public DataFormatReaders withMinimalMatch(MatchStrength minMatch) { + if (minMatch == _minimalMatch) { + return this; + } + return new DataFormatReaders(_readers, _optimalMatch, minMatch, _maxInputLookahead); + } + + public DataFormatReaders with(ObjectReader[] readers) { + return new DataFormatReaders(readers, _optimalMatch, _minimalMatch, _maxInputLookahead); + } + + public DataFormatReaders withMaxInputLookahead(int lookaheadBytes) + { + if (lookaheadBytes == _maxInputLookahead) { + return this; + } + return new DataFormatReaders(_readers, _optimalMatch, _minimalMatch, lookaheadBytes); + } + + /* + /********************************************************** + /* Fluent factories for changing underlying readers + /********************************************************** + */ + + public DataFormatReaders with(DeserializationConfig config) + { + final int len = _readers.length; + ObjectReader[] r = new ObjectReader[len]; + for (int i = 0; i < len; ++i) { + r[i] = _readers[i].with(config); + } + return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead); + } + + public DataFormatReaders withType(JavaType type) + { + final int len = _readers.length; + ObjectReader[] r = new ObjectReader[len]; + for (int i = 0; i < len; ++i) { + r[i] = _readers[i].forType(type); + } + return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead); + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + /** + * Method to call to find format that content (accessible via given + * {@link InputStream}) given has, as per configuration of this detector + * instance. + * + * @return Matcher object which contains result; never null, even in cases + * where no match (with specified minimal match strength) is found. + */ + public Match findFormat(InputStream in) throws IOException + { + return _findFormat(new AccessorForReader(in, new byte[_maxInputLookahead])); + } + + /** + * Method to call to find format that given content (full document) + * has, as per configuration of this detector instance. + * + * @return Matcher object which contains result; never null, even in cases + * where no match (with specified minimal match strength) is found. + */ + public Match findFormat(byte[] fullInputData) throws IOException + { + return _findFormat(new AccessorForReader(fullInputData)); + } + + /** + * Method to call to find format that given content (full document) + * has, as per configuration of this detector instance. + * + * @return Matcher object which contains result; never null, even in cases + * where no match (with specified minimal match strength) is found. + * + * @since 2.1 + */ + public Match findFormat(byte[] fullInputData, int offset, int len) throws IOException + { + return _findFormat(new AccessorForReader(fullInputData, offset, len)); + } + + /* + /********************************************************** + /* Overrides + /********************************************************** + */ + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append('['); + final int len = _readers.length; + if (len > 0) { + sb.append(_readers[0].getFactory().getFormatName()); + for (int i = 1; i < len; ++i) { + sb.append(", "); + sb.append(_readers[i].getFactory().getFormatName()); + } + } + sb.append(']'); + return sb.toString(); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + private Match _findFormat(AccessorForReader acc) throws IOException + { + ObjectReader bestMatch = null; + MatchStrength bestMatchStrength = null; + for (ObjectReader f : _readers) { + acc.reset(); + MatchStrength strength = f.getFactory().hasFormat(acc); + // if not better than what we have so far (including minimal level limit), skip + if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) { + continue; + } + // also, needs to better match than before + if (bestMatch != null) { + if (bestMatchStrength.ordinal() >= strength.ordinal()) { + continue; + } + } + // finally: if it's good enough match, we are done + bestMatch = f; + bestMatchStrength = strength; + if (strength.ordinal() >= _optimalMatch.ordinal()) { + break; + } + } + return acc.createMatcher(bestMatch, bestMatchStrength); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * We need sub-class here as well, to be able to access efficiently. + */ + protected class AccessorForReader extends InputAccessor.Std + { + public AccessorForReader(InputStream in, byte[] buffer) { + super(in, buffer); + } + public AccessorForReader(byte[] inputDocument) { + super(inputDocument); + } + public AccessorForReader(byte[] inputDocument, int start, int len) { + super(inputDocument, start, len); + } + + public Match createMatcher(ObjectReader match, MatchStrength matchStrength) + { + return new Match(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart), + match, matchStrength); + } + } + + /** + * Result class, similar to {@link DataFormatMatcher} + */ + public static class Match + { + protected final InputStream _originalStream; + + /** + * Content read during format matching process + */ + protected final byte[] _bufferedData; + + /** + * Pointer to the first byte in buffer available for reading + */ + protected final int _bufferedStart; + + /** + * Number of bytes available in buffer. + */ + protected final int _bufferedLength; + + /** + * Factory that produced sufficient match (if any) + */ + protected final ObjectReader _match; + + /** + * Strength of match with {@link #_match} + */ + protected final MatchStrength _matchStrength; + + protected Match(InputStream in, byte[] buffered, + int bufferedStart, int bufferedLength, + ObjectReader match, MatchStrength strength) + { + _originalStream = in; + _bufferedData = buffered; + _bufferedStart = bufferedStart; + _bufferedLength = bufferedLength; + _match = match; + _matchStrength = strength; + } + + /* + /********************************************************** + /* Public API, simple accessors + /********************************************************** + */ + + /** + * Accessor to use to see if any formats matched well enough with + * the input data. + */ + public boolean hasMatch() { return _match != null; } + + /** + * Method for accessing strength of the match, if any; if no match, + * will return {@link MatchStrength#INCONCLUSIVE}. + */ + public MatchStrength getMatchStrength() { + return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength; + } + + /** + * Accessor for {@link JsonFactory} that represents format that data matched. + */ + public ObjectReader getReader() { return _match; } + + /** + * Accessor for getting brief textual name of matched format if any (null + * if none). Equivalent to: + *

+         *   return hasMatch() ? getMatch().getFormatName() : null;
+         *
+ */ + public String getMatchedFormatName() { + return _match.getFactory().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; + } + JsonFactory jf = _match.getFactory(); + if (_originalStream == null) { + return jf.createParser(_bufferedData, _bufferedStart, _bufferedLength); + } + return jf.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); + } + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,356 @@ +package com.fasterxml.jackson.databind.deser; + +import java.util.*; +import java.util.Map.Entry; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey; +import com.fasterxml.jackson.annotation.SimpleObjectIdResolver; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Complete {@link DeserializationContext} implementation that adds + * extended API for {@link ObjectMapper} (and {@link ObjectReader}) + * to call, as well as implements certain parts that base class + * has left abstract. + * The remaining abstract methods ({@link #createInstance}, {@link #with}) + * are left so that custom implementations will properly implement them + * to return intended subtype. + */ +public abstract class DefaultDeserializationContext + extends DeserializationContext + implements java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1L; + + protected transient LinkedHashMap _objectIds; + + private List _objectIdResolvers; + + /** + * Constructor that will pass specified deserializer factory and + * cache: cache may be null (in which case default implementation + * will be used), factory can not be null + */ + protected DefaultDeserializationContext(DeserializerFactory df, DeserializerCache cache) { + super(df, cache); + } + + protected DefaultDeserializationContext(DefaultDeserializationContext src, + DeserializationConfig config, JsonParser jp, InjectableValues values) { + super(src, config, jp, values); + } + + protected DefaultDeserializationContext(DefaultDeserializationContext src, + DeserializerFactory factory) { + super(src, factory); + } + + /** + * @since 2.4.4 + */ + protected DefaultDeserializationContext(DefaultDeserializationContext src) { + super(src); + } + + /** + * Method needed to ensure that {@link ObjectMapper#copy} will work + * properly; specifically, that caches are cleared, but settings + * will otherwise remain identical; and that no sharing of state + * occurs. + * + * @since 2.4.4 + */ + public DefaultDeserializationContext copy() { + throw new IllegalStateException("DefaultDeserializationContext sub-class not overriding copy()"); + } + + /* + /********************************************************** + /* Abstract methods impls, Object Id + /********************************************************** + */ + + @Override + public ReadableObjectId findObjectId(Object id, ObjectIdGenerator gen, ObjectIdResolver resolverType) + { + /* 02-Apr-2015, tatu: As per [databind#742] should allow 'null', similar to how + * missing id already works. + */ + if (id == null) { + return null; + } + + final ObjectIdGenerator.IdKey key = gen.key(id); + + if (_objectIds == null) { + _objectIds = new LinkedHashMap(); + } else { + ReadableObjectId entry = _objectIds.get(key); + if (entry != null) { + return entry; + } + } + + // Not seen yet, must create entry and configure resolver. + ObjectIdResolver resolver = null; + + if (_objectIdResolvers == null) { + _objectIdResolvers = new ArrayList(8); + } else { + for (ObjectIdResolver res : _objectIdResolvers) { + if (res.canUseFor(resolverType)) { + resolver = res; + break; + } + } + } + + if (resolver == null) { + resolver = resolverType.newForDeserialization(this); + _objectIdResolvers.add(resolver); + } + + ReadableObjectId entry = createReadableObjectId(key); + entry.setResolver(resolver); + _objectIds.put(key, entry); + return entry; + } + + /** + * Overridable factory method to create a new instance of ReadableObjectId or its + * subclass. It is meant to be overridden when custom ReadableObjectId is + * needed for {@link #tryToResolveUnresolvedObjectId}. + * Default implementation simply constructs default {@link ReadableObjectId} with + * given key. + * + * @param key The key to associate with the new ReadableObjectId + * @return New ReadableObjectId instance + * + * @since 2.7 + */ + protected ReadableObjectId createReadableObjectId(IdKey key) { + return new ReadableObjectId(key); + } + + @Deprecated // since 2.4 + @Override + public ReadableObjectId findObjectId(Object id, ObjectIdGenerator gen) { + return findObjectId(id, gen, new SimpleObjectIdResolver()); + } + + @Override + public void checkUnresolvedObjectId() throws UnresolvedForwardReference + { + if (_objectIds == null) { + return; + } + // 29-Dec-2014, tatu: As per [databind#299], may also just let unresolved refs be... + if (!isEnabled(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS)) { + return; + } + UnresolvedForwardReference exception = null; + for (Entry entry : _objectIds.entrySet()) { + ReadableObjectId roid = entry.getValue(); + if (!roid.hasReferringProperties()) { + continue; + } + // as per [databind#675], allow resolution at this point + if (tryToResolveUnresolvedObjectId(roid)) { + continue; + } + if (exception == null) { + exception = new UnresolvedForwardReference(getParser(), "Unresolved forward references for: "); + } + Object key = roid.getKey().key; + for (Iterator iterator = roid.referringProperties(); iterator.hasNext(); ) { + Referring referring = iterator.next(); + exception.addUnresolvedId(key, referring.getBeanType(), referring.getLocation()); + } + } + if (exception != null) { + throw exception; + } + } + + /** + * Overridable helper method called to try to resolve otherwise unresolvable {@link ReadableObjectId}; + * and if this succeeds, return true to indicate problem has been resolved in + * some way, so that caller can avoid reporting it as an error. + *

+ * Default implementation simply calls {@link ReadableObjectId#tryToResolveUnresolved} and + * returns whatever it returns. + * + * @since 2.6 + */ + protected boolean tryToResolveUnresolvedObjectId(ReadableObjectId roid) + { + return roid.tryToResolveUnresolved(this); + } + + /* + /********************************************************** + /* Abstract methods impls, other factory methods + /********************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public JsonDeserializer deserializerInstance(Annotated ann, Object deserDef) + throws JsonMappingException + { + if (deserDef == null) { + return null; + } + JsonDeserializer deser; + + if (deserDef instanceof JsonDeserializer) { + deser = (JsonDeserializer) deserDef; + } else { + /* Alas, there's no way to force return type of "either class + * X or Y" -- need to throw an exception after the fact + */ + if (!(deserDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned deserializer definition of type "+deserDef.getClass().getName()+"; expected type JsonDeserializer or Class instead"); + } + Class deserClass = (Class)deserDef; + // there are some known "no class" markers to consider too: + if (deserClass == JsonDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) { + return null; + } + if (!JsonDeserializer.class.isAssignableFrom(deserClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName()+"; expected Class"); + } + HandlerInstantiator hi = _config.getHandlerInstantiator(); + deser = (hi == null) ? null : hi.deserializerInstance(_config, ann, deserClass); + if (deser == null) { + deser = (JsonDeserializer) ClassUtil.createInstance(deserClass, + _config.canOverrideAccessModifiers()); + } + } + // First: need to resolve + if (deser instanceof ResolvableDeserializer) { + ((ResolvableDeserializer) deser).resolve(this); + } + return (JsonDeserializer) deser; + } + + @Override + public final KeyDeserializer keyDeserializerInstance(Annotated ann, Object deserDef) + throws JsonMappingException + { + if (deserDef == null) { + return null; + } + + KeyDeserializer deser; + + if (deserDef instanceof KeyDeserializer) { + deser = (KeyDeserializer) deserDef; + } else { + if (!(deserDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type " + +deserDef.getClass().getName() + +"; expected type KeyDeserializer or Class instead"); + } + Class deserClass = (Class)deserDef; + // there are some known "no class" markers to consider too: + if (deserClass == KeyDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) { + return null; + } + if (!KeyDeserializer.class.isAssignableFrom(deserClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName() + +"; expected Class"); + } + HandlerInstantiator hi = _config.getHandlerInstantiator(); + deser = (hi == null) ? null : hi.keyDeserializerInstance(_config, ann, deserClass); + if (deser == null) { + deser = (KeyDeserializer) ClassUtil.createInstance(deserClass, + _config.canOverrideAccessModifiers()); + } + } + // First: need to resolve + if (deser instanceof ResolvableDeserializer) { + ((ResolvableDeserializer) deser).resolve(this); + } + return deser; + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Fluent factory method used for constructing a blueprint instance + * with different factory + */ + public abstract DefaultDeserializationContext with(DeserializerFactory factory); + + /** + * Method called to create actual usable per-deserialization + * context instance. + */ + public abstract DefaultDeserializationContext createInstance( + DeserializationConfig config, JsonParser jp, InjectableValues values); + + /* + /********************************************************** + /* And then the concrete implementation class + /********************************************************** + */ + + /** + * Actual full concrete implementation + */ + public final static class Impl extends DefaultDeserializationContext + { + private static final long serialVersionUID = 1L; + + /** + * Default constructor for a blueprint object, which will use the standard + * {@link DeserializerCache}, given factory. + */ + public Impl(DeserializerFactory df) { + super(df, null); + } + + protected Impl(Impl src, + DeserializationConfig config, JsonParser jp, InjectableValues values) { + super(src, config, jp, values); + } + + protected Impl(Impl src) { super(src); } + + protected Impl(Impl src, DeserializerFactory factory) { + super(src, factory); + } + + @Override + public DefaultDeserializationContext copy() { + if (getClass() != Impl.class) { + return super.copy(); + } + return new Impl(this); + } + + @Override + public DefaultDeserializationContext createInstance(DeserializationConfig config, + JsonParser jp, InjectableValues values) { + return new Impl(this, config, jp, values); + } + + @Override + public DefaultDeserializationContext with(DeserializerFactory factory) { + return new Impl(this, factory); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * This is the class that can be registered (via + * {@link DeserializationConfig} object owner by + * {@link ObjectMapper}) to get called when a potentially + * recoverable problem is encountered during deserialization + * process. Handlers can try to resolve the problem, throw + * an exception or just skip the content. + *

+ * Default implementations for all methods implemented minimal + * "do nothing" functionality, which is roughly equivalent to + * not having a registered listener at all. This allows for + * only implemented handler methods one is interested in, without + * handling other cases. + *

+ * NOTE: it is typically NOT acceptable to simply do nothing, + * because this will result in unprocessed tokens being left in + * token stream (read via {@link JsonParser}, in case a structured + * (JSON Object or JSON Array) value is being pointed to by parser. + */ +public abstract class DeserializationProblemHandler +{ + /** + * Method called when a JSON Map ("Object") entry with an unrecognized + * name is encountered. + * Content (supposedly) matching the property are accessible via + * parser that can be obtained from passed deserialization context. + * Handler can also choose to skip the content; if so, it MUST return + * true to indicate it did handle property successfully. + * Skipping is usually done like so: + *

+     *  jp.skipChildren();
+     *
+ *

+ * Note: version 1.2 added new deserialization feature + * (DeserializationConfig.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES). + * It will only have effect after handler is called, and only + * if handler did not handle the problem. + * + * @param beanOrClass Either bean instance being deserialized (if one + * has been instantiated so far); or Class that indicates type that + * will be instantiated (if no instantiation done yet: for example + * when bean uses non-default constructors) + * @param jp Parser to use for handling problematic content + * + * @return True if the problem is resolved (and content available used or skipped); + * false if the handler did not anything and the problem is unresolved. Note that in + * latter case caller will either throw an exception or explicitly skip the content, + * depending on configuration. + */ + public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser jp, + JsonDeserializer deserializer, Object beanOrClass, String propertyName) + throws IOException, JsonProcessingException + { + return false; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializerCache.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializerCache.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializerCache.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,589 @@ +package com.fasterxml.jackson.databind.deser; + +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.type.*; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Class that defines caching layer between callers (like + * {@link ObjectMapper}, + * {@link com.fasterxml.jackson.databind.DeserializationContext}) + * and classes that construct deserializers + * ({@link com.fasterxml.jackson.databind.deser.DeserializerFactory}). + */ +public final class DeserializerCache + implements java.io.Serializable // since 2.1 -- needs to be careful tho +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Caching + /********************************************************** + */ + + /** + * We will also cache some dynamically constructed deserializers; + * specifically, ones that are expensive to construct. + * This currently means bean and Enum deserializers; starting with + * 2.5, container deserializers will also be cached. + *

+ * Given that we don't expect much concurrency for additions + * (should very quickly converge to zero after startup), let's + * define a relatively low concurrency setting. + */ + final protected ConcurrentHashMap> _cachedDeserializers + = new ConcurrentHashMap>(64, 0.75f, 4); + + /** + * During deserializer construction process we may need to keep track of partially + * completed deserializers, to resolve cyclic dependencies. This is the + * map used for storing deserializers before they are fully complete. + */ + final protected HashMap> _incompleteDeserializers + = new HashMap>(8); + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public DeserializerCache() { } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + Object writeReplace() { + // instead of making this transient, just clear it: + _incompleteDeserializers.clear(); + // TODO: clear out "cheap" cached deserializers? + return this; + } + + /* + /********************************************************** + /* Access to caching aspects + /********************************************************** + */ + + /** + * Method that can be used to determine how many deserializers this + * provider is caching currently + * (if it does caching: default implementation does) + * Exact count depends on what kind of deserializers get cached; + * default implementation caches only dynamically constructed deserializers, + * but not eagerly constructed standard deserializers (which is different + * from how serializer provider works). + *

+ * The main use case for this method is to allow conditional flushing of + * deserializer cache, if certain number of entries is reached. + */ + public int cachedDeserializersCount() { + return _cachedDeserializers.size(); + } + + /** + * Method that will drop all dynamically constructed deserializers (ones that + * are counted as result value for {@link #cachedDeserializersCount}). + * This can be used to remove memory usage (in case some deserializers are + * only used once or so), or to force re-construction of deserializers after + * configuration changes for mapper than owns the provider. + */ + public void flushCachedDeserializers() { + _cachedDeserializers.clear(); + } + + /* + /********************************************************** + /* General deserializer locating method + /********************************************************** + */ + + /** + * Method called to get hold of a deserializer for a value of given type; + * or if no such deserializer can be found, a default handler (which + * may do a best-effort generic serialization or just simply + * throw an exception when invoked). + *

+ * Note: this method is only called for value types; not for keys. + * Key deserializers can be accessed using {@link #findKeyDeserializer}. + *

+ * Note also that deserializer returned is guaranteed to be resolved + * (if it is of type {@link ResolvableDeserializer}), but + * not contextualized (wrt {@link ContextualDeserializer}): caller + * has to handle latter if necessary. + * + * @param ctxt Deserialization context + * @param propertyType Declared type of the value to deserializer (obtained using + * 'setter' method signature and/or type annotations + * + * @throws JsonMappingException if there are fatal problems with + * accessing suitable deserializer; including that of not + * finding any serializer + */ + public JsonDeserializer findValueDeserializer(DeserializationContext ctxt, + DeserializerFactory factory, JavaType propertyType) + throws JsonMappingException + { + JsonDeserializer deser = _findCachedDeserializer(propertyType); + if (deser == null) { + // If not, need to request factory to construct (or recycle) + deser = _createAndCacheValueDeserializer(ctxt, factory, propertyType); + if (deser == null) { + /* Should we let caller handle it? Let's have a helper method + * decide it; can throw an exception, or return a valid + * deserializer + */ + deser = _handleUnknownValueDeserializer(ctxt, propertyType); + } + } + return deser; + } + + /** + * Method called to get hold of a deserializer to use for deserializing + * keys for {@link java.util.Map}. + * + * @throws JsonMappingException if there are fatal problems with + * accessing suitable key deserializer; including that of not + * finding any serializer + */ + public KeyDeserializer findKeyDeserializer(DeserializationContext ctxt, + DeserializerFactory factory, JavaType type) + throws JsonMappingException + { + KeyDeserializer kd = factory.createKeyDeserializer(ctxt, type); + if (kd == null) { // if none found, need to use a placeholder that'll fail + return _handleUnknownKeyDeserializer(ctxt, type); + } + // First: need to resolve? + if (kd instanceof ResolvableDeserializer) { + ((ResolvableDeserializer) kd).resolve(ctxt); + } + return kd; + } + + /** + * Method called to find out whether provider would be able to find + * a deserializer for given type, using a root reference (i.e. not + * through fields or membership in an array or collection) + */ + public boolean hasValueDeserializerFor(DeserializationContext ctxt, + DeserializerFactory factory, JavaType type) + throws JsonMappingException + { + /* Note: mostly copied from findValueDeserializer, except for + * handling of unknown types + */ + JsonDeserializer deser = _findCachedDeserializer(type); + if (deser == null) { + deser = _createAndCacheValueDeserializer(ctxt, factory, type); + } + return (deser != null); + } + + /* + /********************************************************** + /* Helper methods that handle cache lookups + /********************************************************** + */ + + protected JsonDeserializer _findCachedDeserializer(JavaType type) + { + if (type == null) { + throw new IllegalArgumentException("Null JavaType passed"); + } + if (_hasCustomValueHandler(type)) { + return null; + } + return _cachedDeserializers.get(type); + } + + /** + * Method that will try to create a deserializer for given type, + * and resolve and cache it if necessary + * + * @param ctxt Currently active deserialization context + * @param type Type of property to deserialize + */ + protected JsonDeserializer _createAndCacheValueDeserializer(DeserializationContext ctxt, + DeserializerFactory factory, JavaType type) + throws JsonMappingException + { + /* Only one thread to construct deserializers at any given point in time; + * limitations necessary to ensure that only completely initialized ones + * are visible and used. + */ + synchronized (_incompleteDeserializers) { + // Ok, then: could it be that due to a race condition, deserializer can now be found? + JsonDeserializer deser = _findCachedDeserializer(type); + if (deser != null) { + return deser; + } + int count = _incompleteDeserializers.size(); + // Or perhaps being resolved right now? + if (count > 0) { + deser = _incompleteDeserializers.get(type); + if (deser != null) { + return deser; + } + } + // Nope: need to create and possibly cache + try { + return _createAndCache2(ctxt, factory, type); + } finally { + // also: any deserializers that have been created are complete by now + if (count == 0 && _incompleteDeserializers.size() > 0) { + _incompleteDeserializers.clear(); + } + } + } + } + + /** + * Method that handles actual construction (via factory) and caching (both + * intermediate and eventual) + */ + protected JsonDeserializer _createAndCache2(DeserializationContext ctxt, + DeserializerFactory factory, JavaType type) + throws JsonMappingException + { + JsonDeserializer deser; + try { + deser = _createDeserializer(ctxt, factory, type); + } catch (IllegalArgumentException iae) { + /* We better only expose checked exceptions, since those + * are what caller is expected to handle + */ + throw JsonMappingException.from(ctxt, iae.getMessage(), iae); + } + if (deser == null) { + return null; + } + /* cache resulting deserializer? always true for "plain" BeanDeserializer + * (but can be re-defined for sub-classes by using @JsonCachable!) + */ + // 08-Jun-2010, tatu: Related to [JACKSON-296], need to avoid caching MapSerializers... so: + boolean isResolvable = (deser instanceof ResolvableDeserializer); + // 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desers + boolean addToCache = !_hasCustomValueHandler(type) && deser.isCachable(); + + /* we will temporarily hold on to all created deserializers (to + * handle cyclic references, and possibly reuse non-cached + * deserializers (list, map)) + */ + /* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental + * resolution of a reference -- couple of ways to prevent this; + * either not add Lists or Maps, or clear references eagerly. + * Let's actually do both; since both seem reasonable. + */ + /* Need to resolve? Mostly done for bean deserializers; required for + * resolving cyclic references. + */ + if (isResolvable) { + _incompleteDeserializers.put(type, deser); + ((ResolvableDeserializer)deser).resolve(ctxt); + _incompleteDeserializers.remove(type); + } + if (addToCache) { + _cachedDeserializers.put(type, deser); + } + return deser; + } + + /* + /********************************************************** + /* Helper methods for actual construction of deserializers + /********************************************************** + */ + + /** + * Method that does the heavy lifting of checking for per-type annotations, + * find out full type, and figure out which actual factory method + * to call. + */ + @SuppressWarnings("unchecked") + protected JsonDeserializer _createDeserializer(DeserializationContext ctxt, + DeserializerFactory factory, JavaType type) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + + // First things first: do we need to use abstract type mapping? + if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) { + type = factory.mapAbstractType(config, type); + } + BeanDescription beanDesc = config.introspect(type); + // Then: does type define explicit deserializer to use, with annotation(s)? + JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, + beanDesc.getClassInfo()); + if (deser != null) { + return deser; + } + + // If not, may have further type-modification annotations to check: + JavaType newType = modifyTypeByAnnotation(ctxt, beanDesc.getClassInfo(), type); + if (newType != type) { + type = newType; + beanDesc = config.introspect(newType); + } + + // We may also have a Builder type to consider... + Class builder = beanDesc.findPOJOBuilder(); + if (builder != null) { + return (JsonDeserializer) factory.createBuilderBasedDeserializer( + ctxt, type, beanDesc, builder); + } + + // Or perhaps a Converter? + Converter conv = beanDesc.findDeserializationConverter(); + if (conv == null) { // nope, just construct in normal way + return (JsonDeserializer) _createDeserializer2(ctxt, factory, type, beanDesc); + } + // otherwise need to do bit of introspection + JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); + // One more twist, as per [Issue#288]; probably need to get new BeanDesc + if (!delegateType.hasRawClass(type.getRawClass())) { + beanDesc = config.introspect(delegateType); + } + return new StdDelegatingDeserializer(conv, delegateType, + _createDeserializer2(ctxt, factory, delegateType, beanDesc)); + } + + protected JsonDeserializer _createDeserializer2(DeserializationContext ctxt, + DeserializerFactory factory, JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + final DeserializationConfig config = ctxt.getConfig(); + // If not, let's see which factory method to use: + if (type.isEnumType()) { + return factory.createEnumDeserializer(ctxt, type, beanDesc); + } + if (type.isContainerType()) { + if (type.isArrayType()) { + return factory.createArrayDeserializer(ctxt, (ArrayType) type, beanDesc); + } + if (type.isMapLikeType()) { + MapLikeType mlt = (MapLikeType) type; + if (mlt.isTrueMapType()) { + return factory.createMapDeserializer(ctxt,(MapType) mlt, beanDesc); + } + return factory.createMapLikeDeserializer(ctxt, mlt, beanDesc); + } + if (type.isCollectionLikeType()) { + /* 03-Aug-2012, tatu: As per [Issue#40], one exception is if shape + * is to be Shape.OBJECT. Ideally we'd determine it bit later on + * (to allow custom handler checks), but that won't work for other + * reasons. So do it here. + */ + JsonFormat.Value format = beanDesc.findExpectedFormat(null); + if (format == null || format.getShape() != JsonFormat.Shape.OBJECT) { + CollectionLikeType clt = (CollectionLikeType) type; + if (clt.isTrueCollectionType()) { + return factory.createCollectionDeserializer(ctxt, (CollectionType) clt, beanDesc); + } + return factory.createCollectionLikeDeserializer(ctxt, clt, beanDesc); + } + } + } + if (type.isReferenceType()) { + return factory.createReferenceDeserializer(ctxt, (ReferenceType) type, beanDesc); + } + if (JsonNode.class.isAssignableFrom(type.getRawClass())) { + return factory.createTreeDeserializer(config, type, beanDesc); + } + return factory.createBeanDeserializer(ctxt, type, beanDesc); + } + + /** + * Helper method called to check if a class or method + * has annotation that tells which class to use for deserialization. + * Returns null if no such annotation found. + */ + protected JsonDeserializer findDeserializerFromAnnotation(DeserializationContext ctxt, + Annotated ann) + throws JsonMappingException + { + Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann); + if (deserDef == null) { + return null; + } + JsonDeserializer deser = ctxt.deserializerInstance(ann, deserDef); + // One more thing however: may need to also apply a converter: + return findConvertingDeserializer(ctxt, ann, deser); + } + + /** + * Helper method that will check whether given annotated entity (usually class, + * but may also be a property accessor) indicates that a {@link Converter} is to + * be used; and if so, to construct and return suitable serializer for it. + * If not, will simply return given serializer as is. + */ + protected JsonDeserializer findConvertingDeserializer(DeserializationContext ctxt, + Annotated a, JsonDeserializer deser) + throws JsonMappingException + { + Converter conv = findConverter(ctxt, a); + if (conv == null) { + return deser; + } + JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); + return (JsonDeserializer) new StdDelegatingDeserializer(conv, delegateType, deser); + } + + protected Converter findConverter(DeserializationContext ctxt, + Annotated a) + throws JsonMappingException + { + Object convDef = ctxt.getAnnotationIntrospector().findDeserializationConverter(a); + if (convDef == null) { + return null; + } + return ctxt.converterInstance(a, convDef); + } + /** + * Method called to see if given method has annotations that indicate + * a more specific type than what the argument specifies. + * If annotations are present, they must specify compatible Class; + * instance of which can be assigned using the method. This means + * that the Class has to be raw class of type, or its sub-class + * (or, implementing class if original Class instance is an interface). + * + * @param a Method or field that the type is associated with + * @param type Type derived from the setter argument + * + * @return Original type if no annotations are present; or a more + * specific type derived from it if type annotation(s) was found + * + * @throws JsonMappingException if invalid annotation is found + */ + private JavaType modifyTypeByAnnotation(DeserializationContext ctxt, + Annotated a, JavaType type) + throws JsonMappingException + { + AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + if (intr == null) { + return type; + } + + // First things first: find explicitly annotated deserializer(s) + + // then key/value handlers (annotated deserializers)? + if (type.isMapLikeType()) { + JavaType keyType = type.getKeyType(); + // 21-Mar-2011, tatu: ... and associated deserializer too (unless already assigned) + // (not 100% why or how, but this does seem to get called more than once, which + // is not good: for now, let's just avoid errors) + if (keyType != null && keyType.getValueHandler() == null) { + Object kdDef = intr.findKeyDeserializer(a); + if (kdDef != null) { + KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef); + if (kd != null) { + type = ((MapLikeType) type).withKeyValueHandler(kd); + keyType = type.getKeyType(); // just in case it's used below + } + } + } + } + JavaType contentType = type.getContentType(); + if (contentType != null) { + if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception) + Object cdDef = intr.findContentDeserializer(a); + if (cdDef != null) { + JsonDeserializer cd = null; + if (cdDef instanceof JsonDeserializer) { + cdDef = (JsonDeserializer) cdDef; + } else { + Class cdClass = _verifyAsClass(cdDef, "findContentDeserializer", JsonDeserializer.None.class); + if (cdClass != null) { + cd = ctxt.deserializerInstance(a, cdClass); + } + } + if (cd != null) { + type = type.withContentValueHandler(cd); + } + } + } + } + + // And after handlers, possible type refinements + // (note: could possibly avoid this if explicit deserializer was invoked?) + type = intr.refineDeserializationType(ctxt.getConfig(), a, type); + + return type; + } + + /* + /********************************************************** + /* Helper methods, other + /********************************************************** + */ + + /** + * Helper method used to prevent both caching and cache lookups for structured + * types that have custom value handlers + * + * @since 2.4.6 + */ + private boolean _hasCustomValueHandler(JavaType t) { + if (t.isContainerType()) { + JavaType ct = t.getContentType(); + if (ct != null) { + return (ct.getValueHandler() != null) || (ct.getTypeHandler() != null); + } + } + return false; + } + + private Class _verifyAsClass(Object src, String methodName, Class noneClass) + { + if (src == null) { + return null; + } + if (!(src instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class instead"); + } + Class cls = (Class) src; + if (cls == noneClass || ClassUtil.isBogusClass(cls)) { + return null; + } + return cls; + } + + /* + /********************************************************** + /* Overridable error reporting methods + /********************************************************** + */ + + // NOTE: changed 2.6 -> 2.7 to pass context; no way to make backwards compatible + protected JsonDeserializer _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type) + throws JsonMappingException + { + /* Let's try to figure out the reason, to give better error + * messages + */ + Class rawClass = type.getRawClass(); + if (!ClassUtil.isConcrete(rawClass)) { + throw JsonMappingException.from(ctxt, "Can not find a Value deserializer for abstract type "+type); + } + throw JsonMappingException.from(ctxt, "Can not find a Value deserializer for type "+type); + } + + protected KeyDeserializer _handleUnknownKeyDeserializer(DeserializationContext ctxt, JavaType type) + throws JsonMappingException + { + throw JsonMappingException.from(ctxt, "Can not find a (Map) Key deserializer for type "+type); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/DeserializerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,204 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.type.*; + +/** + * Abstract class that defines API used by {@link DeserializationContext} + * to construct actual + * {@link JsonDeserializer} instances (which are then cached by + * context and/or dedicated cache). + *

+ * Since there are multiple broad categories of deserializers, there are + * multiple factory methods: + *

    + *
  • For JSON "Array" type, we need 2 methods: one to deal with expected + * Java arrays ({@link #createArrayDeserializer}) + * and the other for other Java containers like {@link java.util.List}s + * and {@link java.util.Set}s ({@link #createCollectionDeserializer}). + * Actually there is also a third method for "Collection-like" types; + * things like Scala collections that act like JDK collections but do not + * implement same interfaces. + *
  • + *
  • For JSON "Object" type, we need 2 methods: one to deal with + * expected Java {@link java.util.Map}s + * ({@link #createMapDeserializer}), and another for POJOs + * ({@link #createBeanDeserializer}. + * As an additional twist there is also a callback for "Map-like" types, + * mostly to make it possible to support Scala Maps (which are NOT JDK + * Map compatible). + *
  • + *
  • For Tree Model ({@link com.fasterxml.jackson.databind.JsonNode}) properties there is + * {@link #createTreeDeserializer} + *
  • For enumerated types ({@link java.lang.Enum}) there is + * {@link #createEnumDeserializer} + *
  • + *
  • For all other types, {@link #createBeanDeserializer} is used. + *
+ *

+ */ +public abstract class DeserializerFactory +{ + protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0]; + + /* + /******************************************************** + /* Configuration handling + /******************************************************** + */ + + /** + * Convenience method for creating a new factory instance with additional deserializer + * provider. + */ + public abstract DeserializerFactory withAdditionalDeserializers(Deserializers additional); + + /** + * Convenience method for creating a new factory instance with additional + * {@link KeyDeserializers}. + */ + public abstract DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional); + + /** + * Convenience method for creating a new factory instance with additional + * {@link BeanDeserializerModifier}. + */ + public abstract DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier); + + /** + * Convenience method for creating a new factory instance with additional + * {@link AbstractTypeResolver}. + */ + public abstract DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver); + + /** + * Convenience method for creating a new factory instance with additional + * {@link ValueInstantiators}. + */ + public abstract DeserializerFactory withValueInstantiators(ValueInstantiators instantiators); + + /* + /********************************************************** + /* Basic DeserializerFactory API: + /********************************************************** + */ + + /** + * Method that can be called to try to resolve an abstract type + * (interface, abstract class) into a concrete type, or at least + * something "more concrete" (abstract class instead of interface). + * Will either return passed type, or a more specific type. + */ + public abstract JavaType mapAbstractType(DeserializationConfig config, JavaType type) + throws JsonMappingException; + + /** + * Method that is to find all creators (constructors, factory methods) + * for the bean type to deserialize. + */ + public abstract ValueInstantiator findValueInstantiator(DeserializationContext ctxt, + BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to create (or, for completely immutable deserializers, + * reuse) a deserializer that can convert JSON content into values of + * specified Java "bean" (POJO) type. + * At this point it is known that the type is not otherwise recognized + * as one of structured types (array, Collection, Map) or a well-known + * JDK type (enum, primitives/wrappers, String); this method only + * gets called if other options are exhausted. This also means that + * this method can be overridden to add support for custom types. + * + * @param type Type to be deserialized + */ + public abstract JsonDeserializer createBeanDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to create a deserializer that will use specified Builder + * class for building value instances. + */ + public abstract JsonDeserializer createBuilderBasedDeserializer( + DeserializationContext ctxt, JavaType type, BeanDescription beanDesc, + Class builderClass) + throws JsonMappingException; + + + public abstract JsonDeserializer createEnumDeserializer(DeserializationContext ctxt, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * @since 2.7 + */ + public abstract JsonDeserializer createReferenceDeserializer(DeserializationContext ctxt, + ReferenceType type, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to create and return a deserializer that can construct + * JsonNode(s) from JSON content. + */ + public abstract JsonDeserializer createTreeDeserializer(DeserializationConfig config, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to create (or, for completely immutable deserializers, + * reuse) a deserializer that can convert JSON content into values of + * specified Java type. + * + * @param type Type to be deserialized + */ + public abstract JsonDeserializer createArrayDeserializer(DeserializationContext ctxt, + ArrayType type, BeanDescription beanDesc) + throws JsonMappingException; + + public abstract JsonDeserializer createCollectionDeserializer(DeserializationContext ctxt, + CollectionType type, BeanDescription beanDesc) + throws JsonMappingException; + + public abstract JsonDeserializer createCollectionLikeDeserializer(DeserializationContext ctxt, + CollectionLikeType type, BeanDescription beanDesc) + throws JsonMappingException; + + public abstract JsonDeserializer createMapDeserializer(DeserializationContext ctxt, + MapType type, BeanDescription beanDesc) + throws JsonMappingException; + + public abstract JsonDeserializer createMapLikeDeserializer(DeserializationContext ctxt, + MapLikeType type, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to find if factory knows how to create a key deserializer + * for specified type; currently this means checking if a module has registered + * possible deserializers. + * + * @return Key deserializer to use for specified type, if one found; null if not + * (and default key deserializer should be used) + */ + public abstract KeyDeserializer createKeyDeserializer(DeserializationContext ctxt, + JavaType type) + throws JsonMappingException; + + /** + * Method called to find and create a type information deserializer for given base type, + * if one is needed. If not needed (no polymorphic handling configured for type), + * should return null. + *

+ * Note that this method is usually only directly called for values of container (Collection, + * array, Map) types and root values, but not for bean property values. + * + * @param baseType Declared base type of the value to deserializer (actual + * deserializer type will be this type or its subtype) + * + * @return Type deserializer to use for given base type, if one is needed; null if not. + */ + public abstract TypeDeserializer findTypeDeserializer(DeserializationConfig config, + JavaType baseType) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/Deserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/Deserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/Deserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,332 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.type.*; + +/** + * Interface that defines API for simple extensions that can provide additional deserializers + * for various types. Access is by a single callback method; instance is to either return + * a configured {@link JsonDeserializer} for specified type, or null to indicate that it + * does not support handling of the type. In latter case, further calls can be made + * for other providers; in former case returned deserializer is used for handling of + * instances of specified type. + *

+ * It is strongly recommended that implementations always extend {@link Deserializers.Base} + * and NOT just implement {@link Deserializers}. + */ +public interface Deserializers +{ + // // // Scalar types first: + + /** + * Method called to locate deserializer for specified {@link java.lang.Enum} type. + * + * @param type Type of {@link java.lang.Enum} instances to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findEnumDeserializer(Class type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to locate deserializer for specified JSON tree node type. + * + * @param nodeType Specific type of JSON tree nodes to deserialize + * (subtype of {@link com.fasterxml.jackson.databind.JsonNode}) + * @param config Configuration in effect + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findTreeNodeDeserializer(Class nodeType, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException; + + /** + * Method called to locate deserializer for specified value type which does not belong to any other + * category (not an Enum, Collection, Map, Array, reference value or tree node) + * + * @param type Bean type to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findBeanDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException; + + // // // Then container types + + /** + * Method called to locate deserializer for value that is of referential + * type, + * + * @param refType Specific referential type to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the reference type that contains class annotations and + * other information typically needed for building deserializers + * @param contentTypeDeserializer Possible type deserializer for referenced value + * @param contentDeserializer Value deserializer to use for referenced value, if indicated + * by property annotation + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findReferenceDeserializer(ReferenceType refType, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer contentTypeDeserializer, JsonDeserializer contentDeserializer) + throws JsonMappingException; + + /** + * Method called to locate serializer for specified array type. + *

+ * Deserializer for element type may be passed, if configured explicitly at higher level (by + * annotations, typically), but usually are not. + * Type deserializer for element is passed if one is needed based on contextual information + * (annotations on declared element class; or on field or method type is associated with). + * + * @param type Type of array instances to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * @param elementTypeDeserializer If element type needs polymorphic type handling, this is + * the type information deserializer to use; should usually be used as is when constructing + * array deserializer. + * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using + * annotations, for exmple). May be null, in which case it should be resolved here (or using + * {@link ResolvableDeserializer} callback) + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findArrayDeserializer(ArrayType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException; + + + /** + * Method called to locate serializer for specified {@link java.util.Collection} (List, Set etc) type. + *

+ * Deserializer for element type may be passed, if configured explicitly at higher level (by + * annotations, typically), but usually are not. + * Type deserializer for element is passed if one is needed based on contextual information + * (annotations on declared element class; or on field or method type is associated with). + * + * @param type Type of collection instances to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * @param elementTypeDeserializer If element type needs polymorphic type handling, this is + * the type information deserializer to use; should usually be used as is when constructing + * array deserializer. + * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using + * annotations, for exmple). May be null, in which case it should be resolved here (or using + * {@link ResolvableDeserializer} callback) + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findCollectionDeserializer(CollectionType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException; + + /** + * Method called to locate serializer for specified + * "Collection-like" type (one that acts + * like {@link java.util.Collection} but does not implement it). + *

+ * Deserializer for element type may be passed, if configured explicitly at higher level (by + * annotations, typically), but usually are not. + * Type deserializer for element is passed if one is needed based on contextual information + * (annotations on declared element class; or on field or method type is associated with). + * + * @param type Type of instances to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * @param elementTypeDeserializer If element type needs polymorphic type handling, this is + * the type information deserializer to use; should usually be used as is when constructing + * array deserializer. + * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using + * annotations, for exmple). May be null, in which case it should be resolved here (or using + * {@link ResolvableDeserializer} callback) + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException; + + /** + * Method called to locate deserializer for specified {@link java.util.Map} type. + *

+ * Deserializer for element type may be passed, if configured explicitly at higher level (by + * annotations, typically), but usually are not. + * Type deserializer for element is passed if one is needed based on contextual information + * (annotations on declared element class; or on field or method type is associated with). + *

+ * Similarly, a {@link KeyDeserializer} may be passed, but this is only done if there is + * a specific configuration override (annotations) to indicate instance to use. + * Otherwise null is passed, and key deserializer needs to be obtained later during + * resolution (using {@link ResolvableDeserializer#resolve}). + * + * @param type Type of {@link java.util.Map} instances to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * @param keyDeserializer Key deserializer use, if it is defined via annotations or other configuration; + * null if default key deserializer for key type can be used. + * @param elementTypeDeserializer If element type needs polymorphic type handling, this is + * the type information deserializer to use; should usually be used as is when constructing + * array deserializer. + * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using + * annotations, for exmple). May be null, in which case it should be resolved here (or using + * {@link ResolvableDeserializer} callback) + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findMapDeserializer(MapType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException; + + /** + * Method called to locate serializer for specified + * "Map-like" type (one that acts + * like {@link java.util.Map} but does not implement it). + *

+ * Deserializer for element type may be passed, if configured explicitly at higher level (by + * annotations, typically), but usually are not. + * Type deserializer for element is passed if one is needed based on contextual information + * (annotations on declared element class; or on field or method type is associated with). + *

+ * Similarly, a {@link KeyDeserializer} may be passed, but this is only done if there is + * a specific configuration override (annotations) to indicate instance to use. + * Otherwise null is passed, and key deserializer needs to be obtained later during + * resolution (using {@link ResolvableDeserializer#resolve}). + * + * @param type Type of {@link java.util.Map} instances to deserialize + * @param config Configuration in effect + * @param beanDesc Definition of the enumeration type that contains class annotations and + * other information typically needed for building deserializers + * @param keyDeserializer Key deserializer use, if it is defined via annotations or other configuration; + * null if default key deserializer for key type can be used. + * @param elementTypeDeserializer If element type needs polymorphic type handling, this is + * the type information deserializer to use; should usually be used as is when constructing + * array deserializer. + * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using + * annotations, for exmple). May be null, in which case it should be resolved here (or using + * {@link ResolvableDeserializer} callback) + * + * @return Deserializer to use for the type; or null if this provider does not know how to construct it + */ + public JsonDeserializer findMapLikeDeserializer(MapLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException; + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Basic {@link Deserializers} implementation that implements all methods but provides + * no deserializers. Its main purpose is to serve as a base class so that + * sub-classes only need to override methods they need, as most of the time some + * of methods are not needed (especially enumeration and array deserializers are + * very rarely overridden). + */ + public static class Base implements Deserializers + { + @Override + public JsonDeserializer findEnumDeserializer(Class type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + return null; + } + + @Override + public JsonDeserializer findTreeNodeDeserializer(Class nodeType, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + return null; + } + + @Override // since 2.7 + public JsonDeserializer findReferenceDeserializer(ReferenceType refType, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer contentTypeDeserializer, JsonDeserializer contentDeserializer) + throws JsonMappingException { + // 21-Oct-2015, tatu: For backwards compatibility, let's delegate to "bean" variant, + // for 2.7 -- remove work-around from 2.8 or later + return findBeanDeserializer(refType, config, beanDesc); + } + + @Override + public JsonDeserializer findBeanDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + return null; + } + + @Override + public JsonDeserializer findArrayDeserializer(ArrayType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return null; + } + + @Override + public JsonDeserializer findCollectionDeserializer(CollectionType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return null; + } + + @Override + public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return null; + } + + @Override + public JsonDeserializer findMapDeserializer(MapType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return null; + } + + @Override + public JsonDeserializer findMapLikeDeserializer(MapLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return null; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/KeyDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/KeyDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/KeyDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,17 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.*; + +/** + * Interface that defines API for simple extensions that can provide additional deserializers + * for deserializer Map keys of various types, from JSON property names. + * Access is by a single callback method; instance is to either return + * a configured {@link KeyDeserializer} for specified type, or null to indicate that it + * does not support handling of the type. In latter case, further calls can be made + * for other providers; in former case returned key deserializer is used for handling of + * key instances of specified type. + */ +public interface KeyDeserializers +{ + public KeyDeserializer findKeyDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * Interface used to indicate deserializers that want to do post-processing + * after construction but before being returned to caller (and possibly cached) + * and used. + * This is typically used to resolve references + * to other contained types; for example, bean deserializers use this callback + * to locate deserializers for contained field types. + * Main reason for using a callback (instead of trying to resolve dependencies + * immediately) is to make it possible to cleanly handle self-references; + * otherwise it would be easy to get into infinite recursion. + *

+ * Note that {@link #resolve} method does NOT allow returning anything + * (specifically, a new deserializer instance): reason for this is that + * allowing this would not work with proper handling of cyclic dependencies, + * which are resolved by two-phase processing, where initially constructed + * deserializer is added as known deserializer, and only after this + * resolution is done. Resolution is the part that results in lookups for + * dependant deserializers, which may include handling references to + * deserializer itself. + *

+ * Note that in cases where deserializer needs both contextualization and + * resolution -- that is, implements both this interface and {@link ContextualDeserializer} + * -- resolution via this interface occurs first, and contextual + * resolution (using {@link ContextualDeserializer}) later on. + */ +public interface ResolvableDeserializer +{ + /** + * Method called after deserializer instance has been constructed + * (and registered as necessary by provider objects), + * but before it has returned it to the caller. + * Called object can then resolve its dependencies to other types, + * including self-references (direct or indirect). + * + * @param ctxt Context to use for accessing configuration, resolving + * secondary deserializers + */ + public abstract void resolve(DeserializationContext ctxt) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,221 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Class that represents a "wildcard" set method which can be used + * to generically set values of otherwise unmapped (aka "unknown") + * properties read from Json content. + *

+ * !!! Note: might make sense to refactor to share some code + * with {@link SettableBeanProperty}? + */ +public class SettableAnyProperty + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Method used for setting "any" properties, along with annotation + * information. Retained to allow contextualization of any properties. + */ + protected final BeanProperty _property; + + /** + * Annotated variant is needed for JDK serialization only + */ + final protected AnnotatedMethod _setter; + + protected final JavaType _type; + + protected JsonDeserializer _valueDeserializer; + + protected final TypeDeserializer _valueTypeDeserializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public SettableAnyProperty(BeanProperty property, AnnotatedMethod setter, JavaType type, + JsonDeserializer valueDeser, TypeDeserializer typeDeser) + { + _property = property; + _setter = setter; + _type = type; + _valueDeserializer = valueDeser; + _valueTypeDeserializer = typeDeser; + } + + public SettableAnyProperty withValueDeserializer(JsonDeserializer deser) { + return new SettableAnyProperty(_property, _setter, _type, + deser, _valueTypeDeserializer); + } + + /** + * Constructor used for JDK Serialization when reading persisted object + */ + protected SettableAnyProperty(SettableAnyProperty src) + { + _property = src._property; + _setter = src._setter; + _type = src._type; + _valueDeserializer = src._valueDeserializer; + _valueTypeDeserializer = src._valueTypeDeserializer; + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + /** + * Need to define this to verify that we retain actual Method reference + */ + Object readResolve() { + // sanity check... + if (_setter == null || _setter.getAnnotated() == null) { + throw new IllegalArgumentException("Missing method (broken JDK (de)serialization?)"); + } + return this; + } + + /* + /********************************************************** + /* Public API, accessors + /********************************************************** + */ + + public BeanProperty getProperty() { return _property; } + + public boolean hasValueDeserializer() { return (_valueDeserializer != null); } + + public JavaType getType() { return _type; } + + /* + /********************************************************** + /* Public API, deserialization + /********************************************************** + */ + + /** + * Method called to deserialize appropriate value, given parser (and + * context), and set it using appropriate method (a setter method). + */ + public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object instance, String propName) + throws IOException + { + try { + set(instance, propName, deserialize(p, ctxt)); + } catch (UnresolvedForwardReference reference) { + if (!(_valueDeserializer.getObjectIdReader() != null)) { + throw JsonMappingException.from(p, "Unresolved forward reference but no identity info.", reference); + } + AnySetterReferring referring = new AnySetterReferring(this, reference, + _type.getRawClass(), instance, propName); + reference.getRoid().appendReferring(referring); + } + } + + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NULL) { + return null; + } + if (_valueTypeDeserializer != null) { + return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + } + return _valueDeserializer.deserialize(p, ctxt); + } + + public void set(Object instance, String propName, Object value) throws IOException + { + try { + // note: can not use 'setValue()' due to taking 2 args + _setter.getAnnotated().invoke(instance, propName, value); + } catch (Exception e) { + _throwAsIOE(e, propName, value); + } + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + /** + * @param e Exception to re-throw or wrap + * @param propName Name of property (from Json input) to set + * @param value Value of the property + */ + protected void _throwAsIOE(Exception e, String propName, Object value) + throws IOException + { + if (e instanceof IllegalArgumentException) { + String actType = (value == null) ? "[NULL]" : value.getClass().getName(); + StringBuilder msg = new StringBuilder("Problem deserializing \"any\" property '").append(propName); + msg.append("' of class "+getClassName()+" (expected type: ").append(_type); + msg.append("; actual type: ").append(actType).append(")"); + String origMsg = e.getMessage(); + if (origMsg != null) { + msg.append(", problem: ").append(origMsg); + } else { + msg.append(" (no error message provided)"); + } + throw new JsonMappingException(null, msg.toString(), e); + } + if (e instanceof IOException) { + throw (IOException) e; + } + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + // let's wrap the innermost problem + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + throw new JsonMappingException(null, t.getMessage(), t); + } + + private String getClassName() { return _setter.getDeclaringClass().getName(); } + + @Override public String toString() { return "[any property on class "+getClassName()+"]"; } + + private static class AnySetterReferring extends Referring { + private final SettableAnyProperty _parent; + private final Object _pojo; + private final String _propName; + + public AnySetterReferring(SettableAnyProperty parent, + UnresolvedForwardReference reference, Class type, Object instance, String propName) + { + super(reference, type); + _parent = parent; + _pojo = instance; + _propName = propName; + } + + @Override + public void handleResolvedForwardReference(Object id, Object value) + throws IOException + { + if (!hasId(id)) { + throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id.toString() + + "] that wasn't previously registered."); + } + _parent.set(_pojo, _propName, value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,552 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.impl.FailingDeserializer; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase; +import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.Annotations; +import com.fasterxml.jackson.databind.util.ViewMatcher; + +/** + * Base class for deserializable properties of a bean: contains + * both type and name definitions, and reflection-based set functionality. + * Concrete sub-classes implement details, so that field- and + * setter-backed properties, as well as a few more esoteric variations, + * can be handled. + */ +@SuppressWarnings("serial") +public abstract class SettableBeanProperty + extends ConcreteBeanPropertyBase + implements java.io.Serializable +{ + /** + * To avoid nasty NPEs, let's use a placeholder for _valueDeserializer, + * if real deserializer is not (yet) available. + * + * @since 2.2 + */ + protected static final JsonDeserializer MISSING_VALUE_DESERIALIZER = new FailingDeserializer( + "No _valueDeserializer assigned"); + + /** + * Logical name of the property (often but not always derived + * from the setter method name) + */ + protected final PropertyName _propName; + + /** + * Base type for property; may be a supertype of actual value. + */ + protected final JavaType _type; + + /** + * @since 2.2 + */ + protected final PropertyName _wrapperName; + + /** + * Class that contains this property (either class that declares + * the property or one of its subclasses), class that is + * deserialized using deserializer that contains this property. + */ + protected final transient Annotations _contextAnnotations; + + /** + * Deserializer used for handling property value. + *

+ * NOTE: has been immutable since 2.3 + */ + protected final JsonDeserializer _valueDeserializer; + + /** + * If value will contain type information (to support + * polymorphic handling), this is the type deserializer + * used to handle type resolution. + */ + protected final TypeDeserializer _valueTypeDeserializer; + + /* + /********************************************************** + /* Configuration that is not yet immutable; generally assigned + /* during initialization process but can not be passed to + /* constructor. + /********************************************************** + */ + + /** + * If property represents a managed (forward) reference + * (see [JACKSON-235]), we will need name of reference for + * later linking. + *

+ * TODO: should try to make immutable. + */ + protected String _managedReferenceName; + + /** + * This is the information for object identity associated with the property. + *

+ * TODO: should try to make immutable. + */ + protected ObjectIdInfo _objectIdInfo; + + /** + * Helper object used for checking whether this property is to + * be included in the active view, if property is view-specific; + * null otherwise. + *

+ * TODO: should try to make immutable. + */ + protected ViewMatcher _viewMatcher; + + /** + * Index of property (within all property of a bean); assigned + * when all properties have been collected. Order of entries + * is arbitrary, but once indexes are assigned they are not + * changed. + *

+ * TODO: should try to make immutable if at all possible + */ + protected int _propertyIndex = -1; + + /* + /********************************************************** + /* Life-cycle (construct & configure) + /********************************************************** + */ + + protected SettableBeanProperty(BeanPropertyDefinition propDef, + JavaType type, TypeDeserializer typeDeser, Annotations contextAnnotations) + { + this(propDef.getFullName(), type, propDef.getWrapperName(), typeDeser, + contextAnnotations, propDef.getMetadata()); + } + + @Deprecated // since 2.3 + protected SettableBeanProperty(String propName, JavaType type, PropertyName wrapper, + TypeDeserializer typeDeser, Annotations contextAnnotations, + boolean isRequired) + { + this(new PropertyName(propName), type, wrapper, typeDeser, contextAnnotations, + PropertyMetadata.construct(isRequired, null, null, null)); + } + + protected SettableBeanProperty(PropertyName propName, JavaType type, PropertyName wrapper, + TypeDeserializer typeDeser, Annotations contextAnnotations, + PropertyMetadata metadata) + { + super(metadata); + // 09-Jan-2009, tatu: Intern()ing makes sense since Jackson parsed + // field names are (usually) interned too, hence lookups will be faster. + // 23-Oct-2009, tatu: should this be disabled wrt [JACKSON-180]? + // Probably need not, given that namespace of field/method names + // is not unbounded, unlike potential JSON names. + if (propName == null) { + _propName = PropertyName.NO_NAME; + } else { + _propName = propName.internSimpleName(); + } + _type = type; + _wrapperName = wrapper; + _contextAnnotations = contextAnnotations; + _viewMatcher = null; + + // 30-Jan-2012, tatu: Important: contextualize TypeDeserializer now... + if (typeDeser != null) { + typeDeser = typeDeser.forProperty(this); + } + _valueTypeDeserializer = typeDeser; + _valueDeserializer = MISSING_VALUE_DESERIALIZER; + } + + /** + * Constructor only used by {@link com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty}. + * + * @since 2.3 + */ + protected SettableBeanProperty(PropertyName propName, JavaType type, + PropertyMetadata metadata, JsonDeserializer valueDeser) + { + super(metadata); + // as with above ctor, intern()ing probably fine + if (propName == null) { + _propName = PropertyName.NO_NAME; + } else { + _propName = propName.internSimpleName(); + } + _type = type; + _wrapperName = null; + _contextAnnotations = null; + _viewMatcher = null; + _valueTypeDeserializer = null; + _valueDeserializer = valueDeser; + } + + /** + * Basic copy-constructor for sub-classes to use. + */ + protected SettableBeanProperty(SettableBeanProperty src) + { + super(src); + _propName = src._propName; + _type = src._type; + _wrapperName = src._wrapperName; + _contextAnnotations = src._contextAnnotations; + _valueDeserializer = src._valueDeserializer; + _valueTypeDeserializer = src._valueTypeDeserializer; + _managedReferenceName = src._managedReferenceName; + _propertyIndex = src._propertyIndex; + _viewMatcher = src._viewMatcher; + } + + /** + * Copy-with-deserializer-change constructor for sub-classes to use. + */ + @SuppressWarnings("unchecked") + protected SettableBeanProperty(SettableBeanProperty src, JsonDeserializer deser) + { + super(src); + _propName = src._propName; + _type = src._type; + _wrapperName = src._wrapperName; + _contextAnnotations = src._contextAnnotations; + _valueTypeDeserializer = src._valueTypeDeserializer; + _managedReferenceName = src._managedReferenceName; + _propertyIndex = src._propertyIndex; + + if (deser == null) { + _valueDeserializer = MISSING_VALUE_DESERIALIZER; + } else { + _valueDeserializer = (JsonDeserializer) deser; + } + _viewMatcher = src._viewMatcher; + } + + /** + * Copy-with-deserializer-change constructor for sub-classes to use. + */ + protected SettableBeanProperty(SettableBeanProperty src, PropertyName newName) + { + super(src); + _propName = newName; + _type = src._type; + _wrapperName = src._wrapperName; + _contextAnnotations = src._contextAnnotations; + _valueDeserializer = src._valueDeserializer; + _valueTypeDeserializer = src._valueTypeDeserializer; + _managedReferenceName = src._managedReferenceName; + _propertyIndex = src._propertyIndex; + _viewMatcher = src._viewMatcher; + } + + /** + * Fluent factory method for constructing and returning a new instance + * with specified value deserializer. + * Note that this method should NOT change configuration of this instance. + * + * @param deser Deserializer to assign to the new property instance + * + * @return Newly constructed instance, if value deserializer differs from the + * one used for this instance; or 'this' if not. + */ + public abstract SettableBeanProperty withValueDeserializer(JsonDeserializer deser); + + /** + * Fluent factory method for constructing and returning a new instance + * with specified property name. + * Note that this method should NOT change configuration of this instance. + * + * @param newName Name to use for the new instance. + * + * @return Newly constructed instance, if property name differs from the + * one used for this instance; or 'this' if not. + */ + public abstract SettableBeanProperty withName(PropertyName newName); + + /** + * @since 2.3 + */ + public SettableBeanProperty withSimpleName(String simpleName) { + PropertyName n = (_propName == null) + ? new PropertyName(simpleName) : _propName.withSimpleName(simpleName); + return (n == _propName) ? this : withName(n); + } + + @Deprecated // since 2.3 -- use 'withSimpleName' instead if need be + public SettableBeanProperty withName(String simpleName) { + return withName(new PropertyName(simpleName)); + } + + public void setManagedReferenceName(String n) { + _managedReferenceName = n; + } + + public void setObjectIdInfo(ObjectIdInfo objectIdInfo) { + _objectIdInfo = objectIdInfo; + } + + public void setViews(Class[] views) { + if (views == null) { + _viewMatcher = null; + } else { + _viewMatcher = ViewMatcher.construct(views); + } + } + + /** + * Method used to assign index for property. + */ + public void assignIndex(int index) { + if (_propertyIndex != -1) { + throw new IllegalStateException("Property '"+getName()+"' already had index ("+_propertyIndex+"), trying to assign "+index); + } + _propertyIndex = index; + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + @Override + public final String getName() { + return _propName.getSimpleName(); + } + + @Override + public PropertyName getFullName() { + return _propName; + } + + @Override + public JavaType getType() { return _type; } + + @Override + public PropertyName getWrapperName() { + return _wrapperName; + } + + @Override + public abstract AnnotatedMember getMember(); + + @Override + public abstract A getAnnotation(Class acls); + + @Override + public A getContextAnnotation(Class acls) { + return _contextAnnotations.get(acls); + } + + @Override + public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) + throws JsonMappingException + { + if (isRequired()) { + objectVisitor.property(this); + } else { + objectVisitor.optionalProperty(this); + } + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + protected final Class getDeclaringClass() { + return getMember().getDeclaringClass(); + } + + public String getManagedReferenceName() { return _managedReferenceName; } + + public ObjectIdInfo getObjectIdInfo() { return _objectIdInfo; } + + public boolean hasValueDeserializer() { + return (_valueDeserializer != null) && (_valueDeserializer != MISSING_VALUE_DESERIALIZER); + } + + public boolean hasValueTypeDeserializer() { return (_valueTypeDeserializer != null); } + + public JsonDeserializer getValueDeserializer() { + JsonDeserializer deser = _valueDeserializer; + if (deser == MISSING_VALUE_DESERIALIZER) { + return null; + } + return deser; + } + + public TypeDeserializer getValueTypeDeserializer() { return _valueTypeDeserializer; } + + public boolean visibleInView(Class activeView) { + return (_viewMatcher == null) || _viewMatcher.isVisibleForView(activeView); + } + + public boolean hasViews() { return _viewMatcher != null; } + + /** + * Method for accessing unique index of this property; indexes are + * assigned once all properties of a {@link BeanDeserializer} have + * been collected. + * + * @return Index of this property + */ + public int getPropertyIndex() { return _propertyIndex; } + + /** + * Method for accessing index of the creator property: for other + * types of properties will simply return -1. + * + * @since 2.1 + */ + public int getCreatorIndex() { return -1; } + + /** + * Accessor for id of injectable value, if this bean property supports + * value injection. + */ + public Object getInjectableValueId() { return null; } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + /** + * Method called to deserialize appropriate value, given parser (and + * context), and set it using appropriate mechanism. + * Pre-condition is that passed parser must point to the first token + * that should be consumed to produce the value (the only value for + * scalars, multiple for Objects and Arrays). + */ + public abstract void deserializeAndSet(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException; + + /** + * Alternative to {@link #deserializeAndSet} that returns + * either return value of setter method called (if one is), + * or null to indicate that no return value is available. + * Mostly used to support Builder style deserialization. + * + * @since 2.0 + */ + public abstract Object deserializeSetAndReturn(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException; + + /** + * Method called to assign given value to this property, on + * specified Object. + *

+ * Note: this is an optional operation, not supported by all + * implementations, creator-backed properties for example do not + * support this method. + */ + public abstract void set(Object instance, Object value) throws IOException; + + /** + * Method called to assign given value to this property, on + * specified Object, and return whatever delegating accessor + * returned (if anything) + *

+ * Note: this is an optional operation, not supported by all + * implementations, creator-backed properties for example do not + * support this method. + */ + public abstract Object setAndReturn(Object instance, Object value) throws IOException; + + /** + * This method is needed by some specialized bean deserializers, + * and also called by some {@link #deserializeAndSet} implementations. + *

+ * Pre-condition is that passed parser must point to the first token + * that should be consumed to produce the value (the only value for + * scalars, multiple for Objects and Arrays). + *

+ * Note that this method is final for performance reasons: to override + * functionality you must override other methods that call this method; + * this method should also not be called directly unless you really know + * what you are doing (and probably not even then). + */ + public final Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + JsonToken t = p.getCurrentToken(); + + if (t == JsonToken.VALUE_NULL) { + return _valueDeserializer.getNullValue(ctxt); + } + if (_valueTypeDeserializer != null) { + return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + } + return _valueDeserializer.deserialize(p, ctxt); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + /** + * Method that takes in exception of any type, and casts or wraps it + * to an IOException or its subclass. + */ + protected void _throwAsIOE(JsonParser p, Exception e, Object value) throws IOException + { + if (e instanceof IllegalArgumentException) { + String actType = (value == null) ? "[NULL]" : value.getClass().getName(); + StringBuilder msg = new StringBuilder("Problem deserializing property '").append(getName()); + msg.append("' (expected type: ").append(getType()); + msg.append("; actual type: ").append(actType).append(")"); + String origMsg = e.getMessage(); + if (origMsg != null) { + msg.append(", problem: ").append(origMsg); + } else { + msg.append(" (no error message provided)"); + } + throw JsonMappingException.from(p, msg.toString(), e); + } + _throwAsIOE(p, e); + } + + /** + * @since 2.7 + */ + protected IOException _throwAsIOE(JsonParser p, Exception e) throws IOException + { + if (e instanceof IOException) { + throw (IOException) e; + } + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + // let's wrap the innermost problem + Throwable th = e; + while (th.getCause() != null) { + th = th.getCause(); + } + throw JsonMappingException.from(p, th.getMessage(), th); + } + + @Deprecated // since 2.7 + protected IOException _throwAsIOE(Exception e) throws IOException { + return _throwAsIOE((JsonParser) null, e); + } + + // 10-Oct-2015, tatu: _Should_ be deprecated, too, but its remaining + // callers can not actually provide a JsonParser + protected void _throwAsIOE(Exception e, Object value) throws IOException { + _throwAsIOE((JsonParser) null, e, value); + } + + @Override public String toString() { return "[property '"+getName()+"']"; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,100 @@ +package com.fasterxml.jackson.databind.deser; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; + +/** + * Exception thrown during deserialization when there are object id that can't + * be resolved. + * + * @author pgelinas + */ +public class UnresolvedForwardReference extends JsonMappingException { + private static final long serialVersionUID = 1L; + private ReadableObjectId _roid; + private List _unresolvedIds; + + /** + * @since 2.7 + */ + public UnresolvedForwardReference(JsonParser p, String msg, JsonLocation loc, ReadableObjectId roid) { + super(p, msg, loc); + _roid = roid; + } + + /** + * @since 2.7 + */ + public UnresolvedForwardReference(JsonParser p, String msg) { + super(p, msg); + _unresolvedIds = new ArrayList(); + } + + /** + * @deprecated Since 2.7 + */ + @Deprecated // since 2.7 + public UnresolvedForwardReference(String msg, JsonLocation loc, ReadableObjectId roid) { + super(msg, loc); + _roid = roid; + } + + /** + * @deprecated Since 2.7 + */ + @Deprecated // since 2.7 + public UnresolvedForwardReference(String msg) { + super(msg); + _unresolvedIds = new ArrayList(); + } + + /* + /********************************************************** + /* Accessor methods + /********************************************************** + */ + + public ReadableObjectId getRoid() { + return _roid; + } + + public Object getUnresolvedId() { + return _roid.getKey().key; + } + + public void addUnresolvedId(Object id, Class type, JsonLocation where) { + _unresolvedIds.add(new UnresolvedId(id, type, where)); + } + + public List getUnresolvedIds(){ + return _unresolvedIds; + } + + @Override + public String getMessage() + { + String msg = super.getMessage(); + if (_unresolvedIds == null) { + return msg; + } + + StringBuilder sb = new StringBuilder(msg); + Iterator iterator = _unresolvedIds.iterator(); + while (iterator.hasNext()) { + UnresolvedId unresolvedId = iterator.next(); + sb.append(unresolvedId.toString()); + if (iterator.hasNext()) { + sb.append(", "); + } + } + sb.append('.'); + return sb.toString(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/UnresolvedId.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/UnresolvedId.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/UnresolvedId.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,37 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.core.JsonLocation; + +/** + * Helper class for {@link UnresolvedForwardReference}, to contain information about unresolved ids. + * + * @author pgelinas + */ +public class UnresolvedId { + private final Object _id; + private final JsonLocation _location; + private final Class _type; + + public UnresolvedId(Object id, Class type, JsonLocation where) { + _id = id; + _type = type; + _location = where; + } + + /** + * The id which is unresolved. + */ + public Object getId() { return _id; } + + /** + * The type of object which was expected. + */ + public Class getType() { return _type; } + public JsonLocation getLocation() { return _location; } + + @Override + public String toString() { + return String.format("Object id [%s] (for %s) at %s", _id, + (_type == null) ? "NULL" : _type.getName(), _location); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ValueInstantiator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ValueInstantiator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ValueInstantiator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,319 @@ +package com.fasterxml.jackson.databind.deser; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; +import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; + +/** + * Class that defines simple API implemented by objects that create value + * instances. Some or all of properties of value instances may + * be initialized by instantiator, rest being populated by deserializer, + * to which value instance is passed. + * Since different kinds of JSON values (structured and scalar) + * may be bound to Java values, in some cases instantiator + * fully defines resulting value; this is the case when JSON value + * is a scalar value (String, number, boolean). + *

+ * Note that this type is not parameterized (even though it would seemingly + * make sense), because such type information can not be use effectively + * during runtime: access is always using either wildcard type, or just + * basic {@link java.lang.Object}; and so adding type parameter seems + * like unnecessary extra work. + *

+ * Actual implementations are strongly recommended to be based on + * {@link com.fasterxml.jackson.databind.deser.std.StdValueInstantiator} + * which implements all methods, and as such will be compatible + * across versions even if new methods were added to this interface. + */ +public abstract class ValueInstantiator +{ + /* + /********************************************************** + /* Metadata accessors + /********************************************************** + */ + + /** + * Method that returns description of the value type this instantiator + * handles. Used for error messages, diagnostics. + */ + public abstract String getValueTypeDesc(); + + /** + * Method that will return true if any of canCreateXxx method + * returns true: that is, if there is any way that an instance could + * be created. + */ + public boolean canInstantiate() { + return canCreateUsingDefault() || canCreateUsingDelegate() + || canCreateFromObjectWith() || canCreateFromString() + || canCreateFromInt() || canCreateFromLong() + || canCreateFromDouble() || canCreateFromBoolean(); + } + + /** + * Method that can be called to check whether a String-based creator + * is available for this instantiator + */ + public boolean canCreateFromString() { return false; } + + /** + * Method that can be called to check whether an integer (int, Integer) based + * creator is available to use (to call {@link #createFromInt}). + */ + public boolean canCreateFromInt() { return false; } + + /** + * Method that can be called to check whether a long (long, Long) based + * creator is available to use (to call {@link #createFromLong}). + */ + public boolean canCreateFromLong() { return false; } + + /** + * Method that can be called to check whether a double (double / Double) based + * creator is available to use (to call {@link #createFromDouble}). + */ + public boolean canCreateFromDouble() { return false; } + + /** + * Method that can be called to check whether a double (boolean / Boolean) based + * creator is available to use (to call {@link #createFromDouble}). + */ + public boolean canCreateFromBoolean() { return false; } + + /** + * Method that can be called to check whether a default creator (constructor, + * or no-arg static factory method) + * is available for this instantiator + */ + public boolean canCreateUsingDefault() { return getDefaultCreator() != null; } + + /** + * Method that can be called to check whether a delegate-based creator (single-arg + * constructor or factory method) + * is available for this instantiator + */ + public boolean canCreateUsingDelegate() { return false; } + + /** + * Method that can be called to check whether a array-delegate-based creator + * (single-arg constructor or factory method) + * is available for this instantiator + */ + public boolean canCreateUsingArrayDelegate() { return false; } + + /** + * Method that can be called to check whether a property-based creator + * (argument-taking constructor or factory method) + * is available to instantiate values from JSON Object + */ + public boolean canCreateFromObjectWith() { return false; } + + /** + * Method called to determine types of instantiation arguments + * to use when creating instances with creator arguments + * (when {@link #canCreateFromObjectWith()} returns true). + * These arguments are bound from JSON, using specified + * property types to locate deserializers. + *

+ * NOTE: all properties will be of type + * {@link com.fasterxml.jackson.databind.deser.CreatorProperty}. + */ + public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { + return null; + } + + /** + * Method that can be used to determine what is the type of delegate + * type to use, if any; if no delegates are used, will return null. + * If non-null type is returned, deserializer will bind JSON into + * specified type (using standard deserializer for that type), and + * pass that to instantiator. + */ + public JavaType getDelegateType(DeserializationConfig config) { return null; } + + /** + * Method that can be used to determine what is the type of array delegate + * type to use, if any; if no delegates are used, will return null. If + * non-null type is returned, deserializer will bind JSON into specified + * type (using standard deserializer for that type), and pass that to + * instantiator. + */ + public JavaType getArrayDelegateType(DeserializationConfig config) { return null; } + + /* + /********************************************************** + /* Instantiation methods for JSON Object + /********************************************************** + */ + + /** + * Method called to create value instance from a JSON value when + * no data needs to passed to creator (constructor, factory method); + * typically this will call the default constructor of the value object. + * It will only be used if more specific creator methods are not + * applicable; hence "default". + *

+ * This method is called if {@link #getFromObjectArguments} returns + * null or empty List. + */ + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s; no default creator found", + getValueTypeDesc()); + } + + /** + * Method called to create value instance from JSON Object when + * instantiation arguments are passed; this is done, for example when passing information + * specified with "Creator" annotations. + *

+ * This method is called if {@link #getFromObjectArguments} returns + * a non-empty List of arguments. + */ + public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s with arguments", + getValueTypeDesc()); + } + + /** + * Method to called to create value instance from JSON Object using + * an intermediate "delegate" value to pass to createor method + */ + public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s using delegate", + getValueTypeDesc()); + } + + /** + * Method to called to create value instance from JSON Array using + * an intermediate "delegate" value to pass to createor method + */ + public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s using delegate", + getValueTypeDesc()); + } + + /* + /********************************************************** + /* Instantiation methods for JSON scalar types + /* (String, Number, Boolean) + /********************************************************** + */ + + public Object createFromString(DeserializationContext ctxt, String value) throws IOException { + return _createFromStringFallbacks(ctxt, value); + } + + public Object createFromInt(DeserializationContext ctxt, int value) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s from Integer number (%s, int)", + getValueTypeDesc(), value); + } + + public Object createFromLong(DeserializationContext ctxt, long value) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s from Integer number (%s, long)", + getValueTypeDesc(), value); + } + + public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s from Floating-point number (%s, double)", + getValueTypeDesc(), value); + } + + public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException { + throw ctxt.mappingException("Can not instantiate value of type %s from Boolean value (%s)", + getValueTypeDesc(), value); + } + + /* + /********************************************************** + /* Accessors for underlying creator objects (optional) + /********************************************************** + */ + + /** + * Method that can be called to try to access member (constructor, + * static factory method) that is used as the "default creator" + * (creator that is called without arguments; typically default + * [zero-argument] constructor of the type). + * Note that implementations not required to return actual object + * they use (or, they may use some other instantiation) method. + * That is, even if {@link #canCreateUsingDefault()} returns true, + * this method may return null . + */ + public AnnotatedWithParams getDefaultCreator() { return null; } + + /** + * Method that can be called to try to access member (constructor, + * static factory method) that is used as the "delegate creator". + * Note that implementations not required to return actual object + * they use (or, they may use some other instantiation) method. + * That is, even if {@link #canCreateUsingDelegate()} returns true, + * this method may return null . + */ + public AnnotatedWithParams getDelegateCreator() { return null; } + + /** + * Method that can be called to try to access member (constructor, + * static factory method) that is used as the "array delegate creator". + * Note that implementations not required to return actual object + * they use (or, they may use some other instantiation) method. + * That is, even if {@link #canCreateUsingArrayDelegate()} returns true, + * this method may return null . + */ + public AnnotatedWithParams getArrayDelegateCreator() { return null; } + + /** + * Method that can be called to try to access member (constructor, + * static factory method) that is used as the "non-default creator" + * (constructor or factory method that takes one or more arguments). + * Note that implementations not required to return actual object + * they use (or, they may use some other instantiation) method. + * That is, even if {@link #canCreateFromObjectWith()} returns true, + * this method may return null . + */ + public AnnotatedWithParams getWithArgsCreator() { return null; } + + /** + * If an incomplete creator was found, this is the first parameter that + * needs further annotation to help make the creator complete. + */ + public AnnotatedParameter getIncompleteParameter() { return null; } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + /** + * @since 2.4 (demoted from StdValueInstantiator) + */ + protected Object _createFromStringFallbacks(DeserializationContext ctxt, String value) + throws IOException, JsonProcessingException + { + /* 28-Sep-2011, tatu: Ok this is not clean at all; but since there are legacy + * systems that expect conversions in some cases, let's just add a minimal + * patch (note: same could conceivably be used for numbers too). + */ + if (canCreateFromBoolean()) { + String str = value.trim(); + if ("true".equals(str)) { + return createFromBoolean(ctxt, true); + } + if ("false".equals(str)) { + return createFromBoolean(ctxt, false); + } + } + // also, empty Strings might be accepted as null Object... + if (value.length() == 0) { + if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { + return null; + } + } + throw ctxt.mappingException("Can not instantiate value of type %s from String value ('%s'); no single-String constructor/factory method", + getValueTypeDesc(), value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ValueInstantiators.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ValueInstantiators.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/ValueInstantiators.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,47 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.databind.*; + +/** + * Interface for providers of {@link ValueInstantiator} instances. + * Implemented when an object wants to provide custom value instantiators, + * usually to support custom value types with alternate constructors, or + * which need specified post-processing after construction but before + * binding data. + */ +public interface ValueInstantiators +{ + /** + * Method called to find the {@link ValueInstantiator} to use for creating + * instances of specified type during deserialization. + * Note that a default value instantiator is always created first and passed; + * if an implementation does not want to modify or replace it, it has to return + * passed instance as is (returning null is an error) + * + * @param config Deserialization configuration in use + * @param beanDesc Additional information about POJO type to be instantiated + * @param defaultInstantiator Instantiator that will be used if no changes are made; + * passed to allow custom instances to use annotation-provided information + * (note, however, that earlier {@link ValueInstantiators} may have changed it to + * a custom instantiator already) + * + * @return Instantiator to use; either defaultInstantiator that was passed, + * or a custom variant; can not be null. + */ + public ValueInstantiator findValueInstantiator(DeserializationConfig config, + BeanDescription beanDesc, ValueInstantiator defaultInstantiator); + + /** + * Basic "NOP" implementation that can be used as the base class for custom implementations. + * Safer to extend (instead of implementing {@link ValueInstantiators}) in case later + * Jackson versions add new methods in base interface. + */ + public static class Base implements ValueInstantiators + { + @Override + public ValueInstantiator findValueInstantiator(DeserializationConfig config, + BeanDescription beanDesc, ValueInstantiator defaultInstantiator) { + return defaultInstantiator; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,371 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.HashSet; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.util.NameTransformer; + +public class BeanAsArrayBuilderDeserializer + extends BeanDeserializerBase +{ + private static final long serialVersionUID = 1L; + + /** + * Deserializer we delegate operations that we can not handle. + */ + final protected BeanDeserializerBase _delegate; + + /** + * Properties in order expected to be found in JSON array. + */ + final protected SettableBeanProperty[] _orderedProperties; + + final protected AnnotatedMethod _buildMethod; + + /* + /********************************************************** + /* Life-cycle, construction, initialization + /********************************************************** + */ + + /** + * Main constructor used both for creating new instances (by + * {@link BeanDeserializer#asArrayDeserializer}) and for + * creating copies with different delegate. + */ + public BeanAsArrayBuilderDeserializer(BeanDeserializerBase delegate, + SettableBeanProperty[] ordered, + AnnotatedMethod buildMethod) + { + super(delegate); + _delegate = delegate; + _orderedProperties = ordered; + _buildMethod = buildMethod; + } + + @Override + public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + { + /* We can't do much about this; could either replace _delegate + * with unwrapping instance, or just replace this one. Latter seems + * more sensible. + */ + return _delegate.unwrappingDeserializer(unwrapper); + } + + @Override + public BeanAsArrayBuilderDeserializer withObjectIdReader(ObjectIdReader oir) { + return new BeanAsArrayBuilderDeserializer(_delegate.withObjectIdReader(oir), + _orderedProperties, _buildMethod); + } + + @Override + public BeanAsArrayBuilderDeserializer withIgnorableProperties(HashSet ignorableProps) { + return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps), + _orderedProperties, _buildMethod); + } + + @Override + protected BeanAsArrayBuilderDeserializer asArrayDeserializer() { + return this; + } + + /* + /********************************************************** + /* JsonDeserializer implementation + /********************************************************** + */ + + protected final Object finishBuild(DeserializationContext ctxt, Object builder) + throws IOException + { + try { + return _buildMethod.getMember().invoke(builder); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; + } + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // Let's delegate just in case we got a JSON Object (could error out, alternatively?) + if (!p.isExpectedStartArrayToken()) { + return finishBuild(ctxt, _deserializeFromNonArray(p, ctxt)); + } + if (!_vanillaProcessing) { + return finishBuild(ctxt, _deserializeNonVanilla(p, ctxt)); + } + Object builder = _valueInstantiator.createUsingDefault(ctxt); + final SettableBeanProperty[] props = _orderedProperties; + int i = 0; + final int propCount = props.length; + while (true) { + if (p.nextToken() == JsonToken.END_ARRAY) { + return finishBuild(ctxt, builder); + } + if (i == propCount) { + break; + } + SettableBeanProperty prop = props[i]; + if (prop != null) { // normal case + try { + builder = prop.deserializeSetAndReturn(p, ctxt, builder); + } catch (Exception e) { + wrapAndThrow(e, builder, prop.getName(), ctxt); + } + } else { // just skip? + p.skipChildren(); + } + ++i; + } + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown) { + throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + } + // otherwise, skip until end + while (p.nextToken() != JsonToken.END_ARRAY) { + p.skipChildren(); + } + return finishBuild(ctxt, builder); + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt, Object builder) + throws IOException + { + /* No good way to verify that we have an array... although could I guess + * check via JsonParser. So let's assume everything is working fine, for now. + */ + if (_injectables != null) { + injectValues(ctxt, builder); + } + final SettableBeanProperty[] props = _orderedProperties; + int i = 0; + final int propCount = props.length; + while (true) { + if (p.nextToken() == JsonToken.END_ARRAY) { + return finishBuild(ctxt, builder); + } + if (i == propCount) { + break; + } + SettableBeanProperty prop = props[i]; + if (prop != null) { // normal case + try { + builder = prop.deserializeSetAndReturn(p, ctxt, builder); + } catch (Exception e) { + wrapAndThrow(e, builder, prop.getName(), ctxt); + } + } else { // just skip? + p.skipChildren(); + } + ++i; + } + + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown) { + throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + } + // otherwise, skip until end + while (p.nextToken() != JsonToken.END_ARRAY) { + p.skipChildren(); + } + return finishBuild(ctxt, builder); + } + + // needed since 2.1 + @Override + public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException + { + return _deserializeFromNonArray(p, ctxt); + } + + /* + /********************************************************** + /* Helper methods, non-standard creation + /********************************************************** + */ + + /** + * Alternate deserialization method that has to check many more configuration + * aspects than the "vanilla" processing. + * Note: should NOT resolve builder; caller will do that + * + * @return Builder object in use. + */ + protected Object _deserializeNonVanilla(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_nonStandardCreation) { + return _deserializeWithCreator(p, ctxt); + } + Object builder = _valueInstantiator.createUsingDefault(ctxt); + if (_injectables != null) { + injectValues(ctxt, builder); + } + Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + final SettableBeanProperty[] props = _orderedProperties; + int i = 0; + final int propCount = props.length; + while (true) { + if (p.nextToken() == JsonToken.END_ARRAY) { + return builder; + } + if (i == propCount) { + break; + } + SettableBeanProperty prop = props[i]; + ++i; + if (prop != null) { // normal case + if (activeView == null || prop.visibleInView(activeView)) { + try { + prop.deserializeSetAndReturn(p, ctxt, builder); + } catch (Exception e) { + wrapAndThrow(e, builder, prop.getName(), ctxt); + } + continue; + } + } + // otherwise, skip it (view-filtered, no prop etc) + p.skipChildren(); + } + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown) { + throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + } + // otherwise, skip until end + while (p.nextToken() != JsonToken.END_ARRAY) { + p.skipChildren(); + } + return builder; + } + + protected Object _deserializeWithCreator(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + if (_propertyBasedCreator != null) { + return _deserializeUsingPropertyBased(p, ctxt); + } + // should only occur for abstract types... + if (_beanType.isAbstract()) { + throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType + +" (need to add/enable type information?)"); + } + throw JsonMappingException.from(p, "No suitable constructor found for type " + +_beanType+": can not instantiate from JSON object (need to add/enable type information?)"); + } + + /** + * Method called to deserialize bean using "property-based creator": + * this means that a non-default constructor or factory method is + * called, and then possibly other setters. The trick is that + * values for creator method need to be buffered, first; and + * due to non-guaranteed ordering possibly some other properties + * as well. + */ + @Override + protected final Object _deserializeUsingPropertyBased(final JsonParser p, + final DeserializationContext ctxt) + throws IOException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); + + final SettableBeanProperty[] props = _orderedProperties; + final int propCount = props.length; + int i = 0; + Object builder = null; + + for (; p.nextToken() != JsonToken.END_ARRAY; ++i) { + SettableBeanProperty prop = (i < propCount) ? props[i] : null; + if (prop == null) { // we get null if there are extra elements; maybe otherwise too? + p.skipChildren(); + continue; + } + // if we have already constructed POJO, things are simple: + if (builder != null) { + try { + builder = prop.deserializeSetAndReturn(p, ctxt, builder); + } catch (Exception e) { + wrapAndThrow(e, builder, prop.getName(), ctxt); + } + continue; + } + final String propName = prop.getName(); + // if not yet, maybe we got a creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) { + try { + builder = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + continue; // never gets here + } + // polymorphic? + if (builder.getClass() != _beanType.getRawClass()) { + /* 23-Jul-2012, tatu: Not sure if these could ever be properly + * supported (since ordering of elements may not be guaranteed); + * but make explicitly non-supported for now. + */ + throw ctxt.mappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: " + +"nominal type %s, actual type %s", + _beanType.getRawClass().getName(), builder.getClass().getName()); + } + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); + } + + // In case we didn't quite get all the creator properties, we may have to do this: + if (builder == null) { + try { + builder = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; // never gets here + } + } + return builder; + } + + /* + /********************************************************** + /* Helper methods, error reporting + /********************************************************** + */ + + protected Object _deserializeFromNonArray(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // Let's start with failure + throw ctxt.mappingException("Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): " + +"type/property designed to be serialized as JSON Array", + _beanType.getRawClass().getName(), + p.getCurrentToken()); + // in future, may allow use of "standard" POJO serialization as well; if so, do: + //return _delegate.deserialize(p, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,369 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.HashSet; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Variant of {@link BeanDeserializer} used for handling deserialization + * of POJOs when serialized as JSON Arrays, instead of JSON Objects. + * + * @since 2.1 + */ +public class BeanAsArrayDeserializer + extends BeanDeserializerBase +{ + private static final long serialVersionUID = 1L; + + /** + * Deserializer we delegate operations that we can not handle. + */ + protected final BeanDeserializerBase _delegate; + + /** + * Properties in order expected to be found in JSON array. + */ + protected final SettableBeanProperty[] _orderedProperties; + + /* + /********************************************************** + /* Life-cycle, construction, initialization + /********************************************************** + */ + + /** + * Main constructor used both for creating new instances (by + * {@link BeanDeserializer#asArrayDeserializer}) and for + * creating copies with different delegate. + */ + public BeanAsArrayDeserializer(BeanDeserializerBase delegate, + SettableBeanProperty[] ordered) + { + super(delegate); + _delegate = delegate; + _orderedProperties = ordered; + } + + @Override + public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) + { + /* We can't do much about this; could either replace _delegate + * with unwrapping instance, or just replace this one. Latter seems + * more sensible. + */ + return _delegate.unwrappingDeserializer(unwrapper); + } + + @Override + public BeanAsArrayDeserializer withObjectIdReader(ObjectIdReader oir) { + return new BeanAsArrayDeserializer(_delegate.withObjectIdReader(oir), + _orderedProperties); + } + + @Override + public BeanAsArrayDeserializer withIgnorableProperties(HashSet ignorableProps) { + return new BeanAsArrayDeserializer(_delegate.withIgnorableProperties(ignorableProps), + _orderedProperties); + } + + @Override + protected BeanDeserializerBase asArrayDeserializer() { + return this; + } + + /* + /********************************************************** + /* JsonDeserializer implementation + /********************************************************** + */ + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // Let's delegate just in case we got a JSON Object (could error out, alternatively?) + if (!p.isExpectedStartArrayToken()) { + return _deserializeFromNonArray(p, ctxt); + } + if (!_vanillaProcessing) { + return _deserializeNonVanilla(p, ctxt); + } + final Object bean = _valueInstantiator.createUsingDefault(ctxt); + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + + final SettableBeanProperty[] props = _orderedProperties; + int i = 0; + final int propCount = props.length; + while (true) { + if (p.nextToken() == JsonToken.END_ARRAY) { + return bean; + } + if (i == propCount) { + break; + } + SettableBeanProperty prop = props[i]; + if (prop != null) { // normal case + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { // just skip? + p.skipChildren(); + } + ++i; + } + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown) { + throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + } + // otherwise, skip until end + while (p.nextToken() != JsonToken.END_ARRAY) { + p.skipChildren(); + } + return bean; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean) + throws IOException + { + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + /* No good way to verify that we have an array... although could I guess + * check via JsonParser. So let's assume everything is working fine, for now. + */ + if (_injectables != null) { + injectValues(ctxt, bean); + } + final SettableBeanProperty[] props = _orderedProperties; + int i = 0; + final int propCount = props.length; + while (true) { + if (p.nextToken() == JsonToken.END_ARRAY) { + return bean; + } + if (i == propCount) { + break; + } + SettableBeanProperty prop = props[i]; + if (prop != null) { // normal case + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + } else { // just skip? + p.skipChildren(); + } + ++i; + } + + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown) { + throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + } + // otherwise, skip until end + while (p.nextToken() != JsonToken.END_ARRAY) { + p.skipChildren(); + } + return bean; + } + + + // needed since 2.1 + @Override + public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) + throws IOException + { + return _deserializeFromNonArray(p, ctxt); + } + + /* + /********************************************************** + /* Helper methods, non-standard creation + /********************************************************** + */ + + /** + * Alternate deserialization method that has to check many more configuration + * aspects than the "vanilla" processing. + */ + protected Object _deserializeNonVanilla(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_nonStandardCreation) { + return _deserializeWithCreator(p, ctxt); + } + final Object bean = _valueInstantiator.createUsingDefault(ctxt); + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + if (_injectables != null) { + injectValues(ctxt, bean); + } + Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; + final SettableBeanProperty[] props = _orderedProperties; + int i = 0; + final int propCount = props.length; + while (true) { + if (p.nextToken() == JsonToken.END_ARRAY) { + return bean; + } + if (i == propCount) { + break; + } + SettableBeanProperty prop = props[i]; + ++i; + if (prop != null) { // normal case + if (activeView == null || prop.visibleInView(activeView)) { + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + continue; + } + } + // otherwise, skip it (view-filtered, no prop etc) + p.skipChildren(); + } + // Ok; extra fields? Let's fail, unless ignoring extra props is fine + if (!_ignoreAllUnknown) { + throw ctxt.mappingException("Unexpected JSON values; expected at most %d properties (in JSON Array)", + propCount); + } + // otherwise, skip until end + while (p.nextToken() != JsonToken.END_ARRAY) { + p.skipChildren(); + } + return bean; + } + + protected Object _deserializeWithCreator(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt)); + } + if (_propertyBasedCreator != null) { + return _deserializeUsingPropertyBased(p, ctxt); + } + // should only occur for abstract types... + if (_beanType.isAbstract()) { + throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType + +" (need to add/enable type information?)"); + } + throw JsonMappingException.from(p, "No suitable constructor found for type " + +_beanType+": can not instantiate from JSON object (need to add/enable type information?)"); + } + + /** + * Method called to deserialize bean using "property-based creator": + * this means that a non-default constructor or factory method is + * called, and then possibly other setters. The trick is that + * values for creator method need to be buffered, first; and + * due to non-guaranteed ordering possibly some other properties + * as well. + */ + @Override + protected final Object _deserializeUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt) + throws IOException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); + + final SettableBeanProperty[] props = _orderedProperties; + final int propCount = props.length; + int i = 0; + Object bean = null; + + for (; p.nextToken() != JsonToken.END_ARRAY; ++i) { + SettableBeanProperty prop = (i < propCount) ? props[i] : null; + if (prop == null) { // we get null if there are extra elements; maybe otherwise too? + p.skipChildren(); + continue; + } + // if we have already constructed POJO, things are simple: + if (bean != null) { + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, prop.getName(), ctxt); + } + continue; + } + final String propName = prop.getName(); + // if not yet, maybe we got a creator property? + SettableBeanProperty creatorProp = creator.findCreatorProperty(propName); + if (creatorProp != null) { + // Last creator property to set? + if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) { + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); + continue; // never gets here + } + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(bean); + + // polymorphic? + if (bean.getClass() != _beanType.getRawClass()) { + /* 23-Jul-2012, tatu: Not sure if these could ever be properly + * supported (since ordering of elements may not be guaranteed); + * but make explicitly non-supported for now. + */ + throw ctxt.mappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: " + +"nominal type %s, actual type %s", + _beanType.getRawClass().getName(), bean.getClass().getName()); + } + } + continue; + } + // Object Id property? + if (buffer.readIdProperty(propName)) { + continue; + } + // regular property? needs buffering + buffer.bufferProperty(prop, prop.deserialize(p, ctxt)); + } + + // In case we didn't quite get all the creator properties, we may have to do this: + if (bean == null) { + try { + bean = creator.build(ctxt, buffer); + } catch (Exception e) { + wrapInstantiationProblem(e, ctxt); + return null; // never gets here + } + } + return bean; + } + + /* + /********************************************************** + /* Helper methods, error reporting + /********************************************************** + */ + + protected Object _deserializeFromNonArray(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // Let's start with failure + throw ctxt.mappingException("Can not deserialize a POJO (of type %s) from non-Array representation (token: %s): " + +"type/property designed to be serialized as JSON Array", + _beanType.getRawClass().getName(), + p.getCurrentToken()); + // in future, may allow use of "standard" POJO serialization as well; if so, do: + //return _delegate.deserialize(p, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,547 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Helper class used for storing mapping from property name to + * {@link SettableBeanProperty} instances. + *

+ * Note that this class is used instead of generic {@link java.util.HashMap} + * for bit of performance gain (and some memory savings): although default + * implementation is very good for generic use cases, it can be streamlined + * a bit for specific use case we have. Even relatively small improvements + * matter since this is directly on the critical path during deserialization, + * as it is done for each and every POJO property deserialized. + */ +public class BeanPropertyMap + implements Iterable, + java.io.Serializable +{ + private static final long serialVersionUID = 2L; + + /** + * @since 2.5 + */ + protected final boolean _caseInsensitive; + + private int _hashMask; + + /** + * Number of entries stored in the hash area. + */ + private int _size; + + private int _spillCount; + + /** + * Hash area that contains key/property pairs in adjacent elements. + */ + private Object[] _hashArea; + + /** + * Array of properties in the exact order they were handed in. This is + * used by as-array serialization, deserialization. + */ + private SettableBeanProperty[] _propsInOrder; + + public BeanPropertyMap(boolean caseInsensitive, Collection props) + { + _caseInsensitive = caseInsensitive; + _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]); + init(props); + } + + protected void init(Collection props) + { + _size = props.size(); + + // First: calculate size of primary hash area + final int hashSize = findSize(_size); + _hashMask = hashSize-1; + + // and allocate enough to contain primary/secondary, expand for spillovers as need be + int alloc = (hashSize + (hashSize>>1)) * 2; + Object[] hashed = new Object[alloc]; + int spillCount = 0; + + for (SettableBeanProperty prop : props) { + // Due to removal, renaming, theoretically possible we'll have "holes" so: + if (prop == null) { + continue; + } + + String key = getPropertyName(prop); + int slot = _hashCode(key); + int ix = (slot<<1); + + // primary slot not free? + if (hashed[ix] != null) { + // secondary? + ix = (hashSize + (slot >> 1)) << 1; + if (hashed[ix] != null) { + // ok, spill over. + ix = ((hashSize + (hashSize >> 1) ) << 1) + spillCount; + spillCount += 2; + if (ix >= hashed.length) { + hashed = Arrays.copyOf(hashed, hashed.length + 4); + } + } + } +//System.err.println(" add '"+key+" at #"+(ix>>1)+"/"+size+" (hashed at "+slot+")"); + hashed[ix] = key; + hashed[ix+1] = prop; + } +/* +for (int i = 0; i < hashed.length; i += 2) { +System.err.printf("#%02d: %s\n", i>>1, (hashed[i] == null) ? "-" : hashed[i]); +} +*/ + _hashArea = hashed; + _spillCount = spillCount; + } + + private final static int findSize(int size) + { + if (size <= 5) { + return 8; + } + if (size <= 12) { + return 16; + } + int needed = size + (size >> 2); // at most 80% full + int result = 32; + while (result < needed) { + result += result; + } + return result; + } + + /** + * @since 2.6 + */ + public static BeanPropertyMap construct(Collection props, boolean caseInsensitive) { + return new BeanPropertyMap(caseInsensitive, props); + } + + /** + * Fluent copy method that creates a new instance that is a copy + * of this instance except for one additional property that is + * passed as the argument. + * Note that method does not modify this instance but constructs + * and returns a new one. + */ + public BeanPropertyMap withProperty(SettableBeanProperty newProp) + { + // First: may be able to just replace? + String key = getPropertyName(newProp); + + for (int i = 1, end = _hashArea.length; i < end; i += 2) { + SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; + if ((prop != null) && prop.getName().equals(key)) { + _hashArea[i] = newProp; + _propsInOrder[_findFromOrdered(prop)] = newProp; + return this; + } + } + // If not, append + final int slot = _hashCode(key); + final int hashSize = _hashMask+1; + int ix = (slot<<1); + + // primary slot not free? + if (_hashArea[ix] != null) { + // secondary? + ix = (hashSize + (slot >> 1)) << 1; + if (_hashArea[ix] != null) { + // ok, spill over. + ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount; + _spillCount += 2; + if (ix >= _hashArea.length) { + _hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4); + // Uncomment for debugging only + /* +for (int i = 0; i < _hashArea.length; i += 2) { + if (_hashArea[i] != null) { + System.err.println("Property #"+(i/2)+" '"+_hashArea[i]+"'..."); + } +} +System.err.println("And new propr #"+slot+" '"+key+"'"); +*/ + + } + } + } + _hashArea[ix] = key; + _hashArea[ix+1] = newProp; + + int last = _propsInOrder.length; + _propsInOrder = Arrays.copyOf(_propsInOrder, last+1); + _propsInOrder[last] = newProp; + + // should we just create a new one? Or is resetting ok? + + return this; + } + + public BeanPropertyMap assignIndexes() + { + // order is arbitrary, but stable: + int index = 0; + for (int i = 1, end = _hashArea.length; i < end; i += 2) { + SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; + if (prop != null) { + prop.assignIndex(index++); + } + } + return this; + } + + /** + * Factory method for constructing a map where all entries use given + * prefix + */ + public BeanPropertyMap renameAll(NameTransformer transformer) + { + if (transformer == null || (transformer == NameTransformer.NOP)) { + return this; + } + // Try to retain insertion ordering as well + final int len = _propsInOrder.length; + ArrayList newProps = new ArrayList(len); + + for (int i = 0; i < len; ++i) { + SettableBeanProperty prop = _propsInOrder[i]; + + // What to do with holes? For now, retain + if (prop == null) { + newProps.add(prop); + continue; + } + newProps.add(_rename(prop, transformer)); + } + // should we try to re-index? Ordering probably changed but caller probably doesn't want changes... + return new BeanPropertyMap(_caseInsensitive, newProps); + } + + /** + * Specialized method that can be used to replace an existing entry + * (note: entry MUST exist; otherwise exception is thrown) with + * specified replacement. + */ + public void replace(SettableBeanProperty newProp) + { + String key = getPropertyName(newProp); + int ix = _findIndexInHash(key); + + if (ix >= 0) { + SettableBeanProperty prop = (SettableBeanProperty) _hashArea[ix]; + _hashArea[ix] = newProp; + // also, replace in in-order + _propsInOrder[_findFromOrdered(prop)] = newProp; + return; + } + + throw new NoSuchElementException("No entry '"+key+"' found, can't replace"); + } + + private List properties() { + ArrayList p = new ArrayList(_size); + for (int i = 1, end = _hashArea.length; i < end; i += 2) { + SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; + if (prop != null) { + p.add(prop); + } + } + return p; + } + + /** + * Accessor for traversing over all contained properties. + */ + @Override + public Iterator iterator() { + return properties().iterator(); + } + + /** + * Method that will re-create initial insertion-ordering of + * properties contained in this map. Note that if properties + * have been removed, array may contain nulls; otherwise + * it should be consecutive. + * + * @since 2.1 + */ + public SettableBeanProperty[] getPropertiesInInsertionOrder() { + return _propsInOrder; + } + + // Confining this case insensitivity to this function (and the find method) in case we want to + // apply a particular locale to the lower case function. For now, using the default. + protected final String getPropertyName(SettableBeanProperty prop) { + return _caseInsensitive ? prop.getName().toLowerCase() : prop.getName(); + } + + /** + * @since 2.3 + */ + public SettableBeanProperty find(int index) + { + // note: will scan the whole area, including primary, secondary and + // possible spill-area + for (int i = 1, end = _hashArea.length; i < end; i += 2) { + SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; + if ((prop != null) && (index == prop.getPropertyIndex())) { + return prop; + } + } + return null; + } + + public SettableBeanProperty find(String key) + { + if (key == null) { + throw new IllegalArgumentException("Can not pass null property name"); + } + if (_caseInsensitive) { + key = key.toLowerCase(); + } + + // inlined `_hashCode(key)` + int slot = key.hashCode() & _hashMask; +// int h = key.hashCode(); +// int slot = (h + (h >> 13)) & _hashMask; + + int ix = (slot<<1); + Object match = _hashArea[ix]; + if ((match == key) || key.equals(match)) { + return (SettableBeanProperty) _hashArea[ix+1]; + } + return _find2(key, slot, match); + } + + private final SettableBeanProperty _find2(String key, int slot, Object match) + { + if (match == null) { + return null; + } + // no? secondary? + int hashSize = _hashMask+1; + int ix = hashSize + (slot>>1) << 1; + match = _hashArea[ix]; + if (key.equals(match)) { + return (SettableBeanProperty) _hashArea[ix+1]; + } + if (match != null) { // _findFromSpill(...) + int i = (hashSize + (hashSize>>1)) << 1; + for (int end = i + _spillCount; i < end; i += 2) { + match = _hashArea[i]; + if ((match == key) || key.equals(match)) { + return (SettableBeanProperty) _hashArea[i+1]; + } + } + } + return null; + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + public int size() { return _size; } + + /** + * Specialized method for removing specified existing entry. + * NOTE: entry MUST exist, otherwise an exception is thrown. + */ + public void remove(SettableBeanProperty propToRm) { + ArrayList props = new ArrayList(_size); + String key = getPropertyName(propToRm); + boolean found = false; + + for (int i = 1, end = _hashArea.length; i < end; i += 2) { + SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i]; + if (prop == null) { + continue; + } + if (!found) { + found = key.equals(prop.getName()); + if (found) { + // need to leave a hole here + _propsInOrder[_findFromOrdered(prop)] = null; + continue; + } + } + props.add(prop); + } + if (!found) { + throw new NoSuchElementException("No entry '"+propToRm.getName()+"' found, can't remove"); + } + init(props); + } + + /** + * Convenience method that tries to find property with given name, and + * if it is found, call {@link SettableBeanProperty#deserializeAndSet} + * on it, and return true; or, if not found, return false. + * Note, too, that if deserialization is attempted, possible exceptions + * are wrapped if and as necessary, so caller need not handle those. + * + * @since 2.5 + */ + public boolean findDeserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object bean, String key) throws IOException + { + final SettableBeanProperty prop = find(key); + if (prop == null) { + return false; + } + try { + prop.deserializeAndSet(p, ctxt, bean); + } catch (Exception e) { + wrapAndThrow(e, bean, key, ctxt); + } + return true; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("Properties=["); + int count = 0; + + Iterator it = iterator(); + while (it.hasNext()) { + SettableBeanProperty prop = it.next(); + if (count++ > 0) { + sb.append(", "); + } + sb.append(prop.getName()); + sb.append('('); + sb.append(prop.getType()); + sb.append(')'); + } + sb.append(']'); + return sb.toString(); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected SettableBeanProperty _rename(SettableBeanProperty prop, NameTransformer xf) + { + if (prop == null) { + return prop; + } + String newName = xf.transform(prop.getName()); + prop = prop.withSimpleName(newName); + JsonDeserializer deser = prop.getValueDeserializer(); + if (deser != null) { + @SuppressWarnings("unchecked") + JsonDeserializer newDeser = (JsonDeserializer) + deser.unwrappingDeserializer(xf); + if (newDeser != deser) { + prop = prop.withValueDeserializer(newDeser); + } + } + return prop; + } + + protected void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt) + throws IOException + { + // inlined 'throwOrReturnThrowable' + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors to be passed as is + if (t instanceof Error) { + throw (Error) t; + } + // StackOverflowErrors are tricky ones; need to be careful... + boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); + // Ditto for IOExceptions; except we may want to wrap JSON exceptions + if (t instanceof IOException) { + if (!wrap || !(t instanceof JsonProcessingException)) { + throw (IOException) t; + } + } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + } + throw JsonMappingException.wrapWithPath(t, bean, fieldName); + } + + /** + * Helper method used to find exact location of a property with name + * given exactly, not subject to case changes, within hash area. + * Expectation is that such property SHOULD exist, although no + * exception is thrown. + * + * @since 2.7 + */ + private final int _findIndexInHash(String key) + { + final int slot = _hashCode(key); + int ix = (slot<<1); + + // primary match? + if (key.equals(_hashArea[ix])) { + return ix+1; + } + // no? secondary? + int hashSize = _hashMask+1; + ix = hashSize + (slot>>1) << 1; + if (key.equals(_hashArea[ix])) { + return ix+1; + } + // perhaps spill then + int i = (hashSize + (hashSize>>1)) << 1; + for (int end = i + _spillCount; i < end; i += 2) { + if (key.equals(_hashArea[i])) { + return i+1; + } + } + return -1; + } + + private final int _findFromOrdered(SettableBeanProperty prop) { + for (int i = 0, end = _propsInOrder.length; i < end; ++i) { + if (_propsInOrder[i] == prop) { + return i; + } + } + throw new IllegalStateException("Illegal state: property '"+prop.getName()+"' missing from _propsInOrder"); + } + + // Offlined version for convenience if we want to change hashing scheme + private final int _hashCode(String key) { + // This method produces better hash, fewer collisions... yet for some + // reason produces slightly worse performance. Very strange. + + // 05-Aug-2015, tatu: ... still true? + + /* + int h = key.hashCode(); + return (h + (h >> 13)) & _hashMask; + */ + return key.hashCode() & _hashMask; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,390 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.reflect.Member; +import java.util.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.deser.CreatorProperty; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Container class for storing information on creators (based on annotations, + * visibility), to be able to build actual instantiator later on. + */ +public class CreatorCollector +{ + // Since 2.5 + protected final static int C_DEFAULT = 0; + protected final static int C_STRING = 1; + protected final static int C_INT = 2; + protected final static int C_LONG = 3; + protected final static int C_DOUBLE = 4; + protected final static int C_BOOLEAN = 5; + protected final static int C_DELEGATE = 6; + protected final static int C_PROPS = 7; + protected final static int C_ARRAY_DELEGATE = 8; + + protected final static String[] TYPE_DESCS = new String[] { + "default", + "String", "int", "long", "double", "boolean", + "delegate", "property-based" + }; + + /// Type of bean being created + final protected BeanDescription _beanDesc; + + final protected boolean _canFixAccess; + + /** + * @since 2.7 + */ + final protected boolean _forceAccess; + + /** + * Set of creators we have collected so far + * + * @since 2.5 + */ + protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[9]; + + /** + * Bitmask of creators that were explicitly marked as creators; false for auto-detected + * (ones included base on naming and/or visibility, not annotation) + * + * @since 2.5 + */ + protected int _explicitCreators = 0; + + protected boolean _hasNonDefaultCreator = false; + + // when there are injectable values along with delegate: + protected SettableBeanProperty[] _delegateArgs; + + protected SettableBeanProperty[] _arrayDelegateArgs; + + protected SettableBeanProperty[] _propertyBasedArgs; + + protected AnnotatedParameter _incompleteParameter; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public CreatorCollector(BeanDescription beanDesc, MapperConfig config) + { + _beanDesc = beanDesc; + _canFixAccess = config.canOverrideAccessModifiers(); + _forceAccess = config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS); + } + + public ValueInstantiator constructValueInstantiator(DeserializationConfig config) + { + final JavaType delegateType = _computeDelegateType(_creators[C_DELEGATE], _delegateArgs); + final JavaType arrayDelegateType = _computeDelegateType(_creators[C_ARRAY_DELEGATE], _arrayDelegateArgs); + final JavaType type = _beanDesc.getType(); + + // Any non-standard creator will prevent; with one exception: int-valued constructor + // that standard containers have can be ignored + if (!_hasNonDefaultCreator) { + /* 10-May-2014, tatu: If we have nothing special, and we are dealing with one + * of "well-known" types, can create a non-reflection-based instantiator. + */ + final Class rawType = type.getRawClass(); + if (rawType == Collection.class || rawType == List.class || rawType == ArrayList.class) { + return new Vanilla(Vanilla.TYPE_COLLECTION); + } + if (rawType == Map.class || rawType == LinkedHashMap.class) { + return new Vanilla(Vanilla.TYPE_MAP); + } + if (rawType == HashMap.class) { + return new Vanilla(Vanilla.TYPE_HASH_MAP); + } + } + + StdValueInstantiator inst = new StdValueInstantiator(config, type); + inst.configureFromObjectSettings(_creators[C_DEFAULT], + _creators[C_DELEGATE], delegateType, _delegateArgs, + _creators[C_PROPS], _propertyBasedArgs); + inst.configureFromArraySettings(_creators[C_ARRAY_DELEGATE], arrayDelegateType, _arrayDelegateArgs); + inst.configureFromStringCreator(_creators[C_STRING]); + inst.configureFromIntCreator(_creators[C_INT]); + inst.configureFromLongCreator(_creators[C_LONG]); + inst.configureFromDoubleCreator(_creators[C_DOUBLE]); + inst.configureFromBooleanCreator(_creators[C_BOOLEAN]); + inst.configureIncompleteParameter(_incompleteParameter); + return inst; + } + + /* + /********************************************************** + /* Setters + /********************************************************** + */ + + /** + * Method called to indicate the default creator: no-arguments + * constructor or factory method that is called to instantiate + * a value before populating it with data. Default creator is + * only used if no other creators are indicated. + * + * @param creator Creator method; no-arguments constructor or static + * factory method. + */ + public void setDefaultCreator(AnnotatedWithParams creator) { + _creators[C_DEFAULT] = _fixAccess(creator); + } + + public void addStringCreator(AnnotatedWithParams creator, boolean explicit) { + verifyNonDup(creator, C_STRING, explicit); + } + public void addIntCreator(AnnotatedWithParams creator, boolean explicit) { + verifyNonDup(creator, C_INT, explicit); + } + public void addLongCreator(AnnotatedWithParams creator, boolean explicit) { + verifyNonDup(creator, C_LONG, explicit); + } + public void addDoubleCreator(AnnotatedWithParams creator, boolean explicit) { + verifyNonDup(creator, C_DOUBLE, explicit); + } + public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) { + verifyNonDup(creator, C_BOOLEAN, explicit); + } + + public void addDelegatingCreator(AnnotatedWithParams creator, boolean explicit, + SettableBeanProperty[] injectables) + { + if (creator.getParameterType(0).isCollectionLikeType()) { + verifyNonDup(creator, C_ARRAY_DELEGATE, explicit); + _arrayDelegateArgs = injectables; + } else { + verifyNonDup(creator, C_DELEGATE, explicit); + _delegateArgs = injectables; + } + } + + public void addPropertyCreator(AnnotatedWithParams creator, boolean explicit, + SettableBeanProperty[] properties) + { + verifyNonDup(creator, C_PROPS, explicit); + // Better ensure we have no duplicate names either... + if (properties.length > 1) { + HashMap names = new HashMap(); + for (int i = 0, len = properties.length; i < len; ++i) { + String name = properties[i].getName(); + /* [Issue-13]: Need to consider Injectables, which may not have + * a name at all, and need to be skipped + */ + if (name.length() == 0 && properties[i].getInjectableValueId() != null) { + continue; + } + Integer old = names.put(name, Integer.valueOf(i)); + if (old != null) { + throw new IllegalArgumentException("Duplicate creator property \""+name+"\" (index "+old+" vs "+i+")"); + } + } + } + _propertyBasedArgs = properties; + } + + public void addIncompeteParameter(AnnotatedParameter parameter) { + if (_incompleteParameter == null) { + _incompleteParameter = parameter; + } + } + + // Bunch of methods deprecated in 2.5, to be removed from 2.6 or later + + @Deprecated // since 2.5 + public void addStringCreator(AnnotatedWithParams creator) { + addStringCreator(creator, false); + } + @Deprecated // since 2.5 + public void addIntCreator(AnnotatedWithParams creator) { + addBooleanCreator(creator, false); + } + @Deprecated // since 2.5 + public void addLongCreator(AnnotatedWithParams creator) { + addBooleanCreator(creator, false); + } + @Deprecated // since 2.5 + public void addDoubleCreator(AnnotatedWithParams creator) { + addBooleanCreator(creator, false); + } + @Deprecated // since 2.5 + public void addBooleanCreator(AnnotatedWithParams creator) { + addBooleanCreator(creator, false); + } + + @Deprecated // since 2.5 + public void addDelegatingCreator(AnnotatedWithParams creator, CreatorProperty[] injectables) { + addDelegatingCreator(creator, false, injectables); + } + + @Deprecated // since 2.5 + public void addPropertyCreator(AnnotatedWithParams creator, CreatorProperty[] properties) { + addPropertyCreator(creator, false, properties); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + /** + * @since 2.1 + */ + public boolean hasDefaultCreator() { + return _creators[C_DEFAULT] != null; + } + + /** + * @since 2.6 + */ + public boolean hasDelegatingCreator() { + return _creators[C_DELEGATE] != null; + } + + /** + * @since 2.6 + */ + public boolean hasPropertyBasedCreator() { + return _creators[C_PROPS] != null; + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + private JavaType _computeDelegateType(AnnotatedWithParams creator, SettableBeanProperty[] delegateArgs) + { + if (!_hasNonDefaultCreator || (creator == null)) { + return null; + } else { + // need to find type... + int ix = 0; + if (delegateArgs != null) { + for (int i = 0, len = delegateArgs.length; i < len; ++i) { + if (delegateArgs[i] == null) { // marker for delegate itself + ix = i; + break; + } + } + } + return creator.getParameterType(ix); + } + } + + private T _fixAccess(T member) + { + if (member != null && _canFixAccess) { + ClassUtil.checkAndFixAccess((Member) member.getAnnotated(), _forceAccess); + } + return member; + } + + protected void verifyNonDup(AnnotatedWithParams newOne, int typeIndex, boolean explicit) + { + final int mask = (1 << typeIndex); + _hasNonDefaultCreator = true; + AnnotatedWithParams oldOne = _creators[typeIndex]; + // already had an explicitly marked one? + if (oldOne != null) { + boolean verify; + + if ((_explicitCreators & mask) != 0) { // already had explicitly annotated, leave as-is + // but skip, if new one not annotated + if (!explicit) { + return; + } + // both explicit: verify + verify = true; + } else { + // otherwise only verify if neither explicitly annotated. + verify = !explicit; + } + + // one more thing: ok to override in sub-class + if (verify && (oldOne.getClass() == newOne.getClass())) { + // [databind#667]: avoid one particular class of bogus problems + Class oldType = oldOne.getRawParameterType(0); + Class newType = newOne.getRawParameterType(0); + + if (oldType == newType) { + throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex] + +" creators: already had explicitly marked "+oldOne+", encountered "+newOne); + } + // otherwise, which one to choose? + if (newType.isAssignableFrom(oldType)) { + // new type more generic, use old + return; + } + // new type more specific, use it + } + } + if (explicit) { + _explicitCreators |= mask; + } + _creators[typeIndex] = _fixAccess(newOne); + } + + /* + /********************************************************** + /* Helper class(es) + /********************************************************** + */ + + protected final static class Vanilla + extends ValueInstantiator + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + public final static int TYPE_COLLECTION = 1; + public final static int TYPE_MAP = 2; + public final static int TYPE_HASH_MAP = 3; + + private final int _type; + + public Vanilla(int t) { + _type = t; + } + + + @Override + public String getValueTypeDesc() { + switch (_type) { + case TYPE_COLLECTION: return ArrayList.class.getName(); + case TYPE_MAP: return LinkedHashMap.class.getName(); + case TYPE_HASH_MAP: return HashMap.class.getName(); + } + return Object.class.getName(); + } + + @Override + public boolean canInstantiate() { return true; } + + @Override + public boolean canCreateUsingDefault() { return true; } + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + switch (_type) { + case TYPE_COLLECTION: return new ArrayList(); + case TYPE_MAP: return new LinkedHashMap(); + case TYPE_HASH_MAP: return new HashMap(); + } + throw new IllegalStateException("Unknown type "+_type); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,334 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Helper class that is used to flatten JSON structure when using + * "external type id" (see {@link com.fasterxml.jackson.annotation.JsonTypeInfo.As#EXTERNAL_PROPERTY}). + * This is needed to store temporary state and buffer tokens, as the structure is + * rearranged a bit so that actual type deserializer can resolve type and + * finalize deserialization. + */ +public class ExternalTypeHandler +{ + private final ExtTypedProperty[] _properties; + private final HashMap _nameToPropertyIndex; + + private final String[] _typeIds; + private final TokenBuffer[] _tokens; + + protected ExternalTypeHandler(ExtTypedProperty[] properties, + HashMap nameToPropertyIndex, + String[] typeIds, TokenBuffer[] tokens) + { + _properties = properties; + _nameToPropertyIndex = nameToPropertyIndex; + _typeIds = typeIds; + _tokens = tokens; + } + + protected ExternalTypeHandler(ExternalTypeHandler h) + { + _properties = h._properties; + _nameToPropertyIndex = h._nameToPropertyIndex; + int len = _properties.length; + _typeIds = new String[len]; + _tokens = new TokenBuffer[len]; + } + + public ExternalTypeHandler start() { + return new ExternalTypeHandler(this); + } + + /** + * Method called to see if given property/value pair is an external type + * id; and if so handle it. This is only to be called in case + * containing POJO has similarly named property as the external type id; + * otherwise {@link #handlePropertyValue} should be called instead. + */ + public boolean handleTypePropertyValue(JsonParser jp, DeserializationContext ctxt, + String propName, Object bean) + throws IOException + { + Integer I = _nameToPropertyIndex.get(propName); + if (I == null) { + return false; + } + int index = I.intValue(); + ExtTypedProperty prop = _properties[index]; + if (!prop.hasTypePropertyName(propName)) { + return false; + } + String typeId = jp.getText(); + // note: can NOT skip child values (should always be String anyway) + boolean canDeserialize = (bean != null) && (_tokens[index] != null); + // Minor optimization: deserialize properties as soon as we have all we need: + if (canDeserialize) { + _deserializeAndSet(jp, ctxt, bean, index, typeId); + // clear stored data, to avoid deserializing+setting twice: + _tokens[index] = null; + } else { + _typeIds[index] = typeId; + } + return true; + } + + /** + * Method called to ask handler to handle value of given property, + * at point where parser points to the first token of the value. + * Handling can mean either resolving type id it contains (if it matches type + * property name), or by buffering the value for further use. + * + * @return True, if the given property was properly handled + */ + public boolean handlePropertyValue(JsonParser p, DeserializationContext ctxt, + String propName, Object bean) throws IOException + { + Integer I = _nameToPropertyIndex.get(propName); + if (I == null) { + return false; + } + int index = I.intValue(); + ExtTypedProperty prop = _properties[index]; + boolean canDeserialize; + if (prop.hasTypePropertyName(propName)) { + _typeIds[index] = p.getText(); + p.skipChildren(); + canDeserialize = (bean != null) && (_tokens[index] != null); + } else { + @SuppressWarnings("resource") + TokenBuffer tokens = new TokenBuffer(p, ctxt); + tokens.copyCurrentStructure(p); + _tokens[index] = tokens; + canDeserialize = (bean != null) && (_typeIds[index] != null); + } + /* Minor optimization: let's deserialize properties as soon as + * we have all pertinent information: + */ + if (canDeserialize) { + String typeId = _typeIds[index]; + // clear stored data, to avoid deserializing+setting twice: + _typeIds[index] = null; + _deserializeAndSet(p, ctxt, bean, index, typeId); + _tokens[index] = null; + } + return true; + } + + /** + * Method called after JSON Object closes, and has to ensure that all external + * type ids have been handled. + */ + @SuppressWarnings("resource") + public Object complete(JsonParser p, DeserializationContext ctxt, Object bean) + throws IOException + { + for (int i = 0, len = _properties.length; i < len; ++i) { + String typeId = _typeIds[i]; + if (typeId == null) { + TokenBuffer tokens = _tokens[i]; + // let's allow missing both type and property (may already have been set, too) + // but not just one + if (tokens == null) { + continue; + } + // [databind#118]: Need to mind natural types, for which no type id + // will be included. + JsonToken t = tokens.firstToken(); + if (t != null && t.isScalarValue()) { + JsonParser buffered = tokens.asParser(p); + buffered.nextToken(); + SettableBeanProperty extProp = _properties[i].getProperty(); + Object result = TypeDeserializer.deserializeIfNatural(buffered, ctxt, extProp.getType()); + if (result != null) { + extProp.set(bean, result); + continue; + } + // 26-Oct-2012, tatu: As per [databind#94], must allow use of 'defaultImpl' + if (!_properties[i].hasDefaultType()) { + throw ctxt.mappingException("Missing external type id property '%s'", + _properties[i].getTypePropertyName()); + } + typeId = _properties[i].getDefaultTypeId(); + } + } else if (_tokens[i] == null) { + SettableBeanProperty prop = _properties[i].getProperty(); + throw ctxt.mappingException("Missing property '%s' for external type id '%s'", + prop.getName(), _properties[i].getTypePropertyName()); + } + _deserializeAndSet(p, ctxt, bean, i, typeId); + } + return bean; + } + + /** + * Variant called when creation of the POJO involves buffering of creator properties + * as well as property-based creator. + */ + public Object complete(JsonParser jp, DeserializationContext ctxt, + PropertyValueBuffer buffer, PropertyBasedCreator creator) + throws IOException + { + // first things first: deserialize all data buffered: + final int len = _properties.length; + Object[] values = new Object[len]; + for (int i = 0; i < len; ++i) { + String typeId = _typeIds[i]; + if (typeId == null) { + // let's allow missing both type and property (may already have been set, too) + if (_tokens[i] == null) { + continue; + } + // but not just one + // 26-Oct-2012, tatu: As per [Issue#94], must allow use of 'defaultImpl' + if (!_properties[i].hasDefaultType()) { + throw ctxt.mappingException("Missing external type id property '%s'", + _properties[i].getTypePropertyName()); + } + typeId = _properties[i].getDefaultTypeId(); + } else if (_tokens[i] == null) { + SettableBeanProperty prop = _properties[i].getProperty(); + throw ctxt.mappingException("Missing property '%s' for external type id '%s'", + prop.getName(), _properties[i].getTypePropertyName()); + } + values[i] = _deserialize(jp, ctxt, i, typeId); + } + // second: fill in creator properties: + for (int i = 0; i < len; ++i) { + SettableBeanProperty prop = _properties[i].getProperty(); + if (creator.findCreatorProperty(prop.getName()) != null) { + buffer.assignParameter(prop, values[i]); + } + } + Object bean = creator.build(ctxt, buffer); + // third: assign non-creator properties + for (int i = 0; i < len; ++i) { + SettableBeanProperty prop = _properties[i].getProperty(); + if (creator.findCreatorProperty(prop.getName()) == null) { + prop.set(bean, values[i]); + } + } + return bean; + } + + @SuppressWarnings("resource") + protected final Object _deserialize(JsonParser p, DeserializationContext ctxt, + int index, String typeId) throws IOException + { + JsonParser p2 = _tokens[index].asParser(p); + JsonToken t = p2.nextToken(); + // 29-Sep-2015, tatu: As per [databind#942], nulls need special support + if (t == JsonToken.VALUE_NULL) { + return null; + } + TokenBuffer merged = new TokenBuffer(p, ctxt); + merged.writeStartArray(); + merged.writeString(typeId); + merged.copyCurrentStructure(p2); + merged.writeEndArray(); + + // needs to point to START_OBJECT (or whatever first token is) + JsonParser mp = merged.asParser(p); + mp.nextToken(); + return _properties[index].getProperty().deserialize(mp, ctxt); + } + + @SuppressWarnings("resource") + protected final void _deserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object bean, int index, String typeId) throws IOException + { + /* Ok: time to mix type id, value; and we will actually use "wrapper-array" + * style to ensure we can handle all kinds of JSON constructs. + */ + JsonParser p2 = _tokens[index].asParser(p); + JsonToken t = p2.nextToken(); + // 29-Sep-2015, tatu: As per [databind#942], nulls need special support + if (t == JsonToken.VALUE_NULL) { + _properties[index].getProperty().set(bean, null); + return; + } + TokenBuffer merged = new TokenBuffer(p, ctxt); + merged.writeStartArray(); + merged.writeString(typeId); + + merged.copyCurrentStructure(p2); + merged.writeEndArray(); + // needs to point to START_OBJECT (or whatever first token is) + JsonParser mp = merged.asParser(p); + mp.nextToken(); + _properties[index].getProperty().deserializeAndSet(mp, ctxt, bean); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + public static class Builder + { + private final ArrayList _properties = new ArrayList(); + private final HashMap _nameToPropertyIndex = new HashMap(); + + public void addExternal(SettableBeanProperty property, TypeDeserializer typeDeser) + { + Integer index = _properties.size(); + _properties.add(new ExtTypedProperty(property, typeDeser)); + _nameToPropertyIndex.put(property.getName(), index); + _nameToPropertyIndex.put(typeDeser.getPropertyName(), index); + } + + public ExternalTypeHandler build() { + return new ExternalTypeHandler(_properties.toArray(new ExtTypedProperty[_properties.size()]), + _nameToPropertyIndex, null, null); + } + } + + private final static class ExtTypedProperty + { + private final SettableBeanProperty _property; + private final TypeDeserializer _typeDeserializer; + private final String _typePropertyName; + + public ExtTypedProperty(SettableBeanProperty property, TypeDeserializer typeDeser) + { + _property = property; + _typeDeserializer = typeDeser; + _typePropertyName = typeDeser.getPropertyName(); + } + + public boolean hasTypePropertyName(String n) { + return n.equals(_typePropertyName); + } + + public boolean hasDefaultType() { + return _typeDeserializer.getDefaultImpl() != null; + } + + /** + * Specialized called when we need to expose type id of `defaultImpl` when + * serializing: we may need to expose it for assignment to a property, or + * it may be requested as visible for some other reason. + */ + public String getDefaultTypeId() { + Class defaultType = _typeDeserializer.getDefaultImpl(); + if (defaultType == null) { + return null; + } + return _typeDeserializer.getTypeIdResolver().idFromValueAndType(null, defaultType); + } + + public String getTypePropertyName() { return _typePropertyName; } + + public SettableBeanProperty getProperty() { + return _property; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,29 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +/** + * Special bogus "serializer" that will throw + * {@link JsonMappingException} if an attempt is made to deserialize + * a value. This is used as placeholder to avoid NPEs for uninitialized + * structured serializers or handlers. + */ +public class FailingDeserializer extends StdDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final String _message; + + public FailingDeserializer(String m) { + super(Object.class); + _message = m; + } + + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws JsonMappingException{ + throw ctxt.mappingException(_message); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,154 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * This concrete sub-class implements property that is set + * directly assigning to a Field. + */ +public final class FieldProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + final protected AnnotatedField _annotated; + + /** + * Actual field to set when deserializing this property. + * Transient since there is no need to persist; only needed during + * construction of objects. + */ + final protected transient Field _field; + + public FieldProperty(BeanPropertyDefinition propDef, JavaType type, + TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedField field) + { + super(propDef, type, typeDeser, contextAnnotations); + _annotated = field; + _field = field.getAnnotated(); + } + + protected FieldProperty(FieldProperty src, JsonDeserializer deser) { + super(src, deser); + _annotated = src._annotated; + _field = src._field; + } + + protected FieldProperty(FieldProperty src, PropertyName newName) { + super(src, newName); + _annotated = src._annotated; + _field = src._field; + } + + /** + * Constructor used for JDK Serialization when reading persisted object + */ + protected FieldProperty(FieldProperty src) + { + super(src); + _annotated = src._annotated; + Field f = _annotated.getAnnotated(); + if (f == null) { + throw new IllegalArgumentException("Missing field (broken JDK (de)serialization?)"); + } + _field = f; + } + + @Override + public FieldProperty withName(PropertyName newName) { + return new FieldProperty(this, newName); + } + + @Override + public FieldProperty withValueDeserializer(JsonDeserializer deser) { + return new FieldProperty(this, deser); + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + @Override + public A getAnnotation(Class acls) { + return (_annotated == null) ? null : _annotated.getAnnotation(acls); + } + + @Override public AnnotatedMember getMember() { return _annotated; } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public void deserializeAndSet(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException + { + Object value = deserialize(p, ctxt); + try { + _field.set(instance, value); + } catch (Exception e) { + _throwAsIOE(p, e, value); + } + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException + { + Object value = deserialize(p, ctxt); + try { + _field.set(instance, value); + } catch (Exception e) { + _throwAsIOE(p, e, value); + } + return instance; + } + + @Override + public final void set(Object instance, Object value) throws IOException + { + try { + _field.set(instance, value); + } catch (Exception e) { + // 15-Sep-2015, tatu: How coud we get a ref to JsonParser? + _throwAsIOE(e, value); + } + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException + { + try { + _field.set(instance, value); + } catch (Exception e) { + // 15-Sep-2015, tatu: How coud we get a ref to JsonParser? + _throwAsIOE(e, value); + } + return instance; + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + Object readResolve() { + return new FieldProperty(this); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,160 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * This sub-class is used to handle special case of value being a + * non-static inner class. If so, we will have to use a special + * alternative for default constructor; but otherwise can delegate + * to regular implementation. + */ +public final class InnerClassProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + /** + * Actual property that we use after value construction. + */ + protected final SettableBeanProperty _delegate; + + /** + * Constructor used when deserializing this property. + * Transient since there is no need to persist; only needed during + * construction of objects. + */ + final protected transient Constructor _creator; + + /** + * Serializable version of single-arg constructor we use for value instantiation. + */ + protected AnnotatedConstructor _annotated; + + public InnerClassProperty(SettableBeanProperty delegate, + Constructor ctor) + { + super(delegate); + _delegate = delegate; + _creator = ctor; + } + + /** + * Constructor used with JDK Serialization; needed to handle transient + * Constructor, wrap/unwrap in/out-of Annotated variant. + */ + protected InnerClassProperty(InnerClassProperty src, AnnotatedConstructor ann) + { + super(src); + _delegate = src._delegate; + _annotated = ann; + _creator = (_annotated == null) ? null : _annotated.getAnnotated(); + if (_creator == null) { + throw new IllegalArgumentException("Missing constructor (broken JDK (de)serialization?)"); + } + } + + protected InnerClassProperty(InnerClassProperty src, JsonDeserializer deser) + { + super(src, deser); + _delegate = src._delegate.withValueDeserializer(deser); + _creator = src._creator; + } + + protected InnerClassProperty(InnerClassProperty src, PropertyName newName) { + super(src, newName); + _delegate = src._delegate.withName(newName); + _creator = src._creator; + } + + @Override + public InnerClassProperty withName(PropertyName newName) { + return new InnerClassProperty(this, newName); + } + + @Override + public InnerClassProperty withValueDeserializer(JsonDeserializer deser) { + return new InnerClassProperty(this, deser); + } + + // // // BeanProperty impl + + @Override + public A getAnnotation(Class acls) { + return _delegate.getAnnotation(acls); + } + + @Override public AnnotatedMember getMember() { return _delegate.getMember(); } + + /* + /********************************************************** + /* Deserialization methods + /********************************************************** + */ + + @Override + public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object bean) + throws IOException + { + JsonToken t = jp.getCurrentToken(); + Object value; + if (t == JsonToken.VALUE_NULL) { + value = _valueDeserializer.getNullValue(ctxt); + } else if (_valueTypeDeserializer != null) { + value = _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer); + } else { // the usual case + try { + value = _creator.newInstance(bean); + } catch (Exception e) { + ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+_creator.getDeclaringClass().getName()+", problem: "+e.getMessage()); + value = null; + } + _valueDeserializer.deserialize(jp, ctxt, value); + } + set(bean, value); + } + + @Override + public Object deserializeSetAndReturn(JsonParser jp, + DeserializationContext ctxt, Object instance) + throws IOException + { + return setAndReturn(instance, deserialize(jp, ctxt)); + } + + @Override + public final void set(Object instance, Object value) throws IOException { + _delegate.set(instance, value); + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException { + return _delegate.setAndReturn(instance, value); + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + // When reading things back, + Object readResolve() { + return new InnerClassProperty(this, _annotated); + } + + Object writeReplace() { + // need to construct a fake instance to support serialization + if (_annotated != null) { + return this; + } + return new InnerClassProperty(this, new AnnotatedConstructor(null, _creator, null, null)); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,145 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * Wrapper property that is used to handle managed (forward) properties + * Basically just needs to delegate first to actual forward property, and + * then to back property. + */ +public final class ManagedReferenceProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + protected final String _referenceName; + + /** + * Flag that indicates whether property to handle is a container type + * (array, Collection, Map) or not. + */ + protected final boolean _isContainer; + + protected final SettableBeanProperty _managedProperty; + + protected final SettableBeanProperty _backProperty; + + public ManagedReferenceProperty(SettableBeanProperty forward, String refName, + SettableBeanProperty backward, Annotations contextAnnotations, boolean isContainer) + { + super(forward.getFullName(), forward.getType(), forward.getWrapperName(), + forward.getValueTypeDeserializer(), contextAnnotations, + forward.getMetadata()); + _referenceName = refName; + _managedProperty = forward; + _backProperty = backward; + _isContainer = isContainer; + } + + protected ManagedReferenceProperty(ManagedReferenceProperty src, JsonDeserializer deser) + { + super(src, deser); + _referenceName = src._referenceName; + _isContainer = src._isContainer; + _managedProperty = src._managedProperty; + _backProperty = src._backProperty; + } + + protected ManagedReferenceProperty(ManagedReferenceProperty src, PropertyName newName) { + super(src, newName); + _referenceName = src._referenceName; + _isContainer = src._isContainer; + _managedProperty = src._managedProperty; + _backProperty = src._backProperty; + } + + @Override + public ManagedReferenceProperty withName(PropertyName newName) { + return new ManagedReferenceProperty(this, newName); + } + + @Override + public ManagedReferenceProperty withValueDeserializer(JsonDeserializer deser) { + return new ManagedReferenceProperty(this, deser); + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + @Override + public A getAnnotation(Class acls) { + return _managedProperty.getAnnotation(acls); + } + + @Override public AnnotatedMember getMember() { return _managedProperty.getMember(); } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) + throws IOException { + set(instance, _managedProperty.deserialize(p, ctxt)); + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance) + throws IOException { + return setAndReturn(instance, deserialize(p, ctxt)); + } + + @Override + public final void set(Object instance, Object value) throws IOException { + setAndReturn(instance, value); + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException + { + /* 04-Feb-2014, tatu: As per [#390], it may be necessary to switch the + * ordering of forward/backward references, and start with back ref. + */ + if (value != null) { + if (_isContainer) { // ok, this gets ugly... but has to do for now + if (value instanceof Object[]) { + for (Object ob : (Object[]) value) { + if (ob != null) { _backProperty.set(ob, instance); } + } + } else if (value instanceof Collection) { + for (Object ob : (Collection) value) { + if (ob != null) { _backProperty.set(ob, instance); } + } + } else if (value instanceof Map) { + for (Object ob : ((Map) value).values()) { + if (ob != null) { _backProperty.set(ob, instance); } + } + } else { + throw new IllegalStateException("Unsupported container type ("+value.getClass().getName() + +") when resolving reference '"+_referenceName+"'"); + } + } else { + _backProperty.set(value, instance); + } + } + // and then the forward reference itself + return _managedProperty.setAndReturn(instance, value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,150 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * This concrete sub-class implements property that is set + * using regular "setter" method. + */ +public final class MethodProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1; + + protected final AnnotatedMethod _annotated; + + /** + * Setter method for modifying property value; used for + * "regular" method-accessible properties. + */ + protected final transient Method _setter; + + public MethodProperty(BeanPropertyDefinition propDef, + JavaType type, TypeDeserializer typeDeser, + Annotations contextAnnotations, AnnotatedMethod method) + { + super(propDef, type, typeDeser, contextAnnotations); + _annotated = method; + _setter = method.getAnnotated(); + } + + protected MethodProperty(MethodProperty src, JsonDeserializer deser) { + super(src, deser); + _annotated = src._annotated; + _setter = src._setter; + } + + protected MethodProperty(MethodProperty src, PropertyName newName) { + super(src, newName); + _annotated = src._annotated; + _setter = src._setter; + } + + /** + * Constructor used for JDK Serialization when reading persisted object + */ + protected MethodProperty(MethodProperty src, Method m) { + super(src); + _annotated = src._annotated; + _setter = m; + } + + @Override + public MethodProperty withName(PropertyName newName) { + return new MethodProperty(this, newName); + } + + @Override + public MethodProperty withValueDeserializer(JsonDeserializer deser) { + return new MethodProperty(this, deser); + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + @Override + public A getAnnotation(Class acls) { + return (_annotated == null) ? null : _annotated.getAnnotation(acls); + } + + @Override public AnnotatedMember getMember() { return _annotated; } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object instance) throws IOException + { + Object value = deserialize(p, ctxt); + try { + _setter.invoke(instance, value); + } catch (Exception e) { + _throwAsIOE(p, e, value); + } + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException + { + Object value = deserialize(p, ctxt); + try { + Object result = _setter.invoke(instance, value); + return (result == null) ? instance : result; + } catch (Exception e) { + _throwAsIOE(p, e, value); + return null; + } + } + + @Override + public final void set(Object instance, Object value) throws IOException + { + try { + _setter.invoke(instance, value); + } catch (Exception e) { + // 15-Sep-2015, tatu: How coud we get a ref to JsonParser? + _throwAsIOE(e, value); + } + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException + { + try { + Object result = _setter.invoke(instance, value); + return (result == null) ? instance : result; + } catch (Exception e) { + // 15-Sep-2015, tatu: How coud we get a ref to JsonParser? + _throwAsIOE(e, value); + return null; + } + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + Object readResolve() { + return new MethodProperty(this, _annotated.getAnnotated()); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/NoClassDefFoundDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/NoClassDefFoundDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/NoClassDefFoundDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,30 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +/** + * A deserializer that stores a {@link NoClassDefFoundError} error + * and throws the stored exception when attempting to deserialize + * a value. Null and empty values can be deserialized without error. + * + * @since 2.5 + */ +public class NoClassDefFoundDeserializer extends JsonDeserializer +{ + private final NoClassDefFoundError _cause; + + public NoClassDefFoundDeserializer(NoClassDefFoundError cause) + { + _cause = cause; + } + + @Override + public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + { + throw _cause; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/NullProvider.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/NullProvider.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/NullProvider.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,39 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; + +/** + * @deprecated + */ +@Deprecated // since 2.6, remove in 2.7 +public final class NullProvider + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + private final Object _nullValue; + + private final boolean _isPrimitive; + + private final Class _rawType; + + public NullProvider(JavaType type, Object nullValue) + { + _nullValue = nullValue; + _isPrimitive = type.isPrimitive(); + _rawType = type.getRawClass(); + } + + public Object nullValue(DeserializationContext ctxt) throws JsonProcessingException + { + if (_isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { + throw ctxt.mappingException("Can not map JSON null into type %s" + +" (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)", + _rawType.getName()); + } + return _nullValue; + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,138 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.annotation.SimpleObjectIdResolver; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; + +/** + * Object that knows how to deserialize Object Ids. + */ +public class ObjectIdReader + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final JavaType _idType; + + public final PropertyName propertyName; + + /** + * Blueprint generator instance: actual instance will be + * fetched from {@link SerializerProvider} using this as + * the key. + */ + public final ObjectIdGenerator generator; + + public final ObjectIdResolver resolver; + + /** + * Deserializer used for deserializing id values. + */ + protected final JsonDeserializer _deserializer; + + public final SettableBeanProperty idProperty; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + @SuppressWarnings("unchecked") + protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator gen, + JsonDeserializer deser, SettableBeanProperty idProp, ObjectIdResolver resolver) + { + _idType = t; + propertyName = propName; + generator = gen; + this.resolver = resolver; + _deserializer = (JsonDeserializer) deser; + idProperty = idProp; + } + + @Deprecated // since 2.4 + protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator gen, + JsonDeserializer deser, SettableBeanProperty idProp) + { + this(t,propName, gen, deser, idProp, new SimpleObjectIdResolver()); + } + + /** + * Factory method called by {@link com.fasterxml.jackson.databind.ser.std.BeanSerializerBase} + * with the initial information based on standard settings for the type + * for which serializer is being built. + */ + public static ObjectIdReader construct(JavaType idType, PropertyName propName, + ObjectIdGenerator generator, JsonDeserializer deser, + SettableBeanProperty idProp, ObjectIdResolver resolver) + { + return new ObjectIdReader(idType, propName, generator, deser, idProp, resolver); + } + + @Deprecated // since 2.4 + public static ObjectIdReader construct(JavaType idType, PropertyName propName, + ObjectIdGenerator generator, JsonDeserializer deser, + SettableBeanProperty idProp) + { + return construct(idType, propName, generator, deser, idProp, new SimpleObjectIdResolver()); + } + + /* + /********************************************************** + /* API + /********************************************************** + */ + + public JsonDeserializer getDeserializer() { + return _deserializer; + } + + public JavaType getIdType() { + return _idType; + } + + /** + * Convenience method, equivalent to calling: + * + * readerInstance.generator.maySerializeAsObject(); + * + * and used to determine whether Object Ids handled by the underlying + * generator may be in form of (JSON) Objects. + * Used for optimizing handling in cases where method returns false. + * + * @since 2.5 + */ + public boolean maySerializeAsObject() { + return generator.maySerializeAsObject(); + } + + /** + * Convenience method, equivalent to calling: + * + * readerInstance.generator.isValidReferencePropertyName(name, parser); + * + * and used to determine whether Object Ids handled by the underlying + * generator may be in form of (JSON) Objects. + * Used for optimizing handling in cases where method returns false. + * + * @since 2.5 + */ + public boolean isValidReferencePropertyName(String name, JsonParser parser) { + return generator.isValidReferencePropertyName(name, parser); + } + + /** + * Method called to read value that is expected to be an Object Reference + * (that is, value of an Object Id used to refer to another object). + * + * @since 2.3 + */ + public Object readObjectReference(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserializer.deserialize(jp, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdReferenceProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,118 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; + +public class ObjectIdReferenceProperty extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + private final SettableBeanProperty _forward; + + public ObjectIdReferenceProperty(SettableBeanProperty forward, ObjectIdInfo objectIdInfo) + { + super(forward); + _forward = forward; + _objectIdInfo = objectIdInfo; + } + + public ObjectIdReferenceProperty(ObjectIdReferenceProperty src, JsonDeserializer deser) + { + super(src, deser); + _forward = src._forward; + _objectIdInfo = src._objectIdInfo; + } + + public ObjectIdReferenceProperty(ObjectIdReferenceProperty src, PropertyName newName) + { + super(src, newName); + _forward = src._forward; + _objectIdInfo = src._objectIdInfo; + } + + @Override + public SettableBeanProperty withValueDeserializer(JsonDeserializer deser) { + return new ObjectIdReferenceProperty(this, deser); + } + + @Override + public SettableBeanProperty withName(PropertyName newName) { + return new ObjectIdReferenceProperty(this, newName); + } + + @Override + public A getAnnotation(Class acls) { + return _forward.getAnnotation(acls); + } + + @Override + public AnnotatedMember getMember() { + return _forward.getMember(); + } + + @Override + public int getCreatorIndex() { + return _forward.getCreatorIndex(); + } + + @Override + public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException { + deserializeSetAndReturn(p, ctxt, instance); + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException + { + try { + return setAndReturn(instance, deserialize(p, ctxt)); + } catch (UnresolvedForwardReference reference) { + boolean usingIdentityInfo = (_objectIdInfo != null) || (_valueDeserializer.getObjectIdReader() != null); + if (!usingIdentityInfo) { + throw JsonMappingException.from(p, "Unresolved forward reference but no identity info.", reference); + } + reference.getRoid().appendReferring(new PropertyReferring(this, reference, _type.getRawClass(), instance)); + return null; + } + } + + @Override + public void set(Object instance, Object value) throws IOException { + _forward.set(instance, value); + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException { + return _forward.setAndReturn(instance, value); + } + + public final static class PropertyReferring extends Referring { + private final ObjectIdReferenceProperty _parent; + public final Object _pojo; + + public PropertyReferring(ObjectIdReferenceProperty parent, + UnresolvedForwardReference ref, Class type, Object ob) + { + super(ref, type); + _parent = parent; + _pojo = ob; + } + + @Override + public void handleResolvedForwardReference(Object id, Object value) throws IOException + { + if (!hasId(id)) { + throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id + + "] that wasn't previously seen as unresolved."); + } + _parent.set(_pojo, value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,114 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; + +/** + * Specialized {@link SettableBeanProperty} implementation used + * for virtual property that represents Object Id that is used + * for some POJO types (or properties). + */ +public final class ObjectIdValueProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + protected final ObjectIdReader _objectIdReader; + + public ObjectIdValueProperty(ObjectIdReader objectIdReader, + PropertyMetadata metadata) + { + super(objectIdReader.propertyName, objectIdReader.getIdType(), metadata, + objectIdReader.getDeserializer()); + _objectIdReader = objectIdReader; + } + + protected ObjectIdValueProperty(ObjectIdValueProperty src, JsonDeserializer deser) + { + super(src, deser); + _objectIdReader = src._objectIdReader; + } + + protected ObjectIdValueProperty(ObjectIdValueProperty src, PropertyName newName) { + super(src, newName); + _objectIdReader = src._objectIdReader; + } + + @Override + public ObjectIdValueProperty withName(PropertyName newName) { + return new ObjectIdValueProperty(this, newName); + } + + @Override + public ObjectIdValueProperty withValueDeserializer(JsonDeserializer deser) { + return new ObjectIdValueProperty(this, deser); + } + + // // // BeanProperty impl + + @Override + public A getAnnotation(Class acls) { + return null; + } + + @Override public AnnotatedMember getMember() { return null; } + + /* + /********************************************************** + /* Deserialization methods + /********************************************************** + */ + + @Override + public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object instance) throws IOException + { + deserializeSetAndReturn(p, ctxt, instance); + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException + { + /* 02-Apr-2015, tatu: Actually, as per [databind#742], let it be; + * missing or null id is needed for some cases, such as cases where id + * will be generated externally, at a later point, and is not available + * quite yet. Typical use case is with DB inserts. + */ + // note: no null checks (unlike usually); deserializer should fail if one found + if (p.hasToken(JsonToken.VALUE_NULL)) { + return null; + } + Object id = _valueDeserializer.deserialize(p, ctxt); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); + roid.bindItem(instance); + // also: may need to set a property value as well + SettableBeanProperty idProp = _objectIdReader.idProperty; + if (idProp != null) { + return idProp.setAndReturn(instance, id); + } + return instance; + } + + @Override + public void set(Object instance, Object value) throws IOException { + setAndReturn(instance, value); + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException + { + SettableBeanProperty idProp = _objectIdReader.idProperty; + if (idProp == null) { + throw new UnsupportedOperationException( + "Should not call set() on ObjectIdProperty that has no SettableBeanProperty"); + } + return idProp.setAndReturn(instance, value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,149 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; + +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; + +/** + * Object that is used to collect arguments for non-default creator + * (non-default-constructor, or argument-taking factory method) + * before creator can be called. + * Since ordering of JSON properties is not guaranteed, this may + * require buffering of values other than ones being passed to + * creator. + */ +public final class PropertyBasedCreator +{ + protected final ValueInstantiator _valueInstantiator; + + /** + * Map that contains property objects for either constructor or factory + * method (whichever one is null: one property for each + * parameter for that one), keyed by logical property name + */ + protected final HashMap _propertyLookup; + + /** + * Number of properties: usually same as size of {@link #_propertyLookup}, + * but not necessarily, when we have unnamed injectable properties. + */ + protected final int _propertyCount; + + /** + * Array that contains properties that expect value to inject, if any; + * null if no injectable values are expected. + */ + protected final SettableBeanProperty[] _allProperties; + + /* + /********************************************************** + /* Construction, initialization + /********************************************************** + */ + + protected PropertyBasedCreator(ValueInstantiator valueInstantiator, + SettableBeanProperty[] creatorProps) + { + _valueInstantiator = valueInstantiator; + _propertyLookup = new HashMap(); + final int len = creatorProps.length; + _propertyCount = len; + _allProperties = new SettableBeanProperty[len]; + for (int i = 0; i < len; ++i) { + SettableBeanProperty prop = creatorProps[i]; + _allProperties[i] = prop; + _propertyLookup.put(prop.getName(), prop); + } + } + + /** + * Factory method used for building actual instances: resolves deserializers + * and checks for "null values". + */ + public static PropertyBasedCreator construct(DeserializationContext ctxt, + ValueInstantiator valueInstantiator, SettableBeanProperty[] srcProps) + throws JsonMappingException + { + final int len = srcProps.length; + SettableBeanProperty[] creatorProps = new SettableBeanProperty[len]; + for (int i = 0; i < len; ++i) { + SettableBeanProperty prop = srcProps[i]; + if (!prop.hasValueDeserializer()) { + prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop)); + } + creatorProps[i] = prop; + } + return new PropertyBasedCreator(valueInstantiator, creatorProps); + } + + // 05-May-2015, tatu: Does not seem to be used, commented out in 2.6 + /* + public void assignDeserializer(SettableBeanProperty prop, JsonDeserializer deser) { + prop = prop.withValueDeserializer(deser); + _properties.put(prop.getName(), prop); + } + */ + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + public Collection properties() { + return _propertyLookup.values(); + } + + public SettableBeanProperty findCreatorProperty(String name) { + return _propertyLookup.get(name); + } + + public SettableBeanProperty findCreatorProperty(int propertyIndex) { + for (SettableBeanProperty prop : _propertyLookup.values()) { + if (prop.getPropertyIndex() == propertyIndex) { + return prop; + } + } + return null; + } + + /* + /********************************************************** + /* Building process + /********************************************************** + */ + + /** + * Method called when starting to build a bean instance. + * + * @since 2.1 (added ObjectIdReader parameter -- existed in previous versions without) + */ + public PropertyValueBuffer startBuilding(JsonParser p, DeserializationContext ctxt, + ObjectIdReader oir) { + return new PropertyValueBuffer(p, ctxt, _propertyCount, oir); + } + + public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException + { + Object bean = _valueInstantiator.createFromObjectWith(ctxt, + buffer.getParameters(_allProperties)); + // returning null isn't quite legal, but let's let caller deal with that + if (bean != null) { + // Object Id to handle? + bean = buffer.handleIdValue(ctxt, bean); + + // Anything buffered? + for (PropertyValue pv = buffer.buffered(); pv != null; pv = pv.next) { + pv.assign(bean); + } + } + return bean; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,40 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +// Simple placeholder +public class PropertyBasedObjectIdGenerator + extends ObjectIdGenerators.PropertyGenerator +{ + private static final long serialVersionUID = 1L; + + public PropertyBasedObjectIdGenerator(Class scope) { + super(scope); + } + + @Override + public Object generateId(Object forPojo) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIdGenerator forScope(Class scope) { + return (scope == _scope) ? this : new PropertyBasedObjectIdGenerator(scope); + } + + @Override + public ObjectIdGenerator newForSerialization(Object context) { + return this; + } + + @Override + public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) { + if (key == null) { + return null; + } + // should we use general type for all; or type of property itself? + return new IdKey(getClass(), _scope, key); + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyValue.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyValue.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyValue.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,118 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import com.fasterxml.jackson.databind.deser.SettableAnyProperty; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; + +/** + * Base class for property values that need to be buffered during + * deserialization. + */ +public abstract class PropertyValue +{ + public final PropertyValue next; + + /** + * Value to assign when POJO has been instantiated. + */ + public final Object value; + + protected PropertyValue(PropertyValue next, Object value) + { + this.next = next; + this.value = value; + } + + /** + * Method called to assign stored value of this property to specified + * bean instance + */ + public abstract void assign(Object bean) + throws IOException, JsonProcessingException; + + /* + /********************************************************** + /* Concrete property value classes + /********************************************************** + */ + + /** + * Property value that used when assigning value to property using + * a setter method or direct field access. + */ + final static class Regular + extends PropertyValue + { + final SettableBeanProperty _property; + + public Regular(PropertyValue next, Object value, + SettableBeanProperty prop) + { + super(next, value); + _property = prop; + } + + @Override + public void assign(Object bean) + throws IOException, JsonProcessingException + { + _property.set(bean, value); + } + } + + /** + * Property value type used when storing entries to be added + * to a POJO using "any setter" (method that takes name and + * value arguments, allowing setting multiple different + * properties using single method). + */ + final static class Any + extends PropertyValue + { + final SettableAnyProperty _property; + final String _propertyName; + + public Any(PropertyValue next, Object value, + SettableAnyProperty prop, + String propName) + { + super(next, value); + _property = prop; + _propertyName = propName; + } + + @Override + public void assign(Object bean) + throws IOException, JsonProcessingException + { + _property.set(bean, _propertyName, value); + } + } + + /** + * Property value type used when storing entries to be added + * to a Map. + */ + final static class Map + extends PropertyValue + { + final Object _key; + + public Map(PropertyValue next, Object value, Object key) + { + super(next, value); + _key = key; + } + + @SuppressWarnings("unchecked") + @Override + public void assign(Object bean) + throws IOException, JsonProcessingException + { + ((java.util.Map) bean).put(_key, value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,254 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.BitSet; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.deser.SettableAnyProperty; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; + +/** + * Simple container used for temporarily buffering a set of + * PropertyValues. + * Using during construction of beans (and Maps) that use Creators, + * and hence need buffering before instance (that will have properties + * to assign values to) is constructed. + */ +public class PropertyValueBuffer +{ + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected final JsonParser _parser; + protected final DeserializationContext _context; + + protected final ObjectIdReader _objectIdReader; + + /* + /********************************************************** + /* Accumulated properties, other stuff + /********************************************************** + */ + + /** + * Buffer used for storing creator parameters for constructing + * instance. + */ + protected final Object[] _creatorParameters; + + /** + * Number of creator parameters for which we have not yet received + * values. + */ + protected int _paramsNeeded; + + /** + * Bitflag used to track parameters found from incoming data + * when number of parameters is + * less than 32 (fits in int). + */ + protected int _paramsSeen; + + /** + * Bitflag used to track parameters found from incoming data + * when number of parameters is + * 32 or higher. + */ + protected final BitSet _paramsSeenBig; + + /** + * If we get non-creator parameters before or between + * creator parameters, those need to be buffered. Buffer + * is just a simple linked list + */ + protected PropertyValue _buffered; + + /** + * In case there is an Object Id property to handle, this is the value + * we have for it. + */ + protected Object _idValue; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public PropertyValueBuffer(JsonParser jp, DeserializationContext ctxt, int paramCount, + ObjectIdReader oir) + { + _parser = jp; + _context = ctxt; + _paramsNeeded = paramCount; + _objectIdReader = oir; + _creatorParameters = new Object[paramCount]; + if (paramCount < 32) { + _paramsSeenBig = null; + } else { + _paramsSeenBig = new BitSet(); + } + } + + /** + * Method called to do necessary post-processing such as injection of values + * and verification of values for required properties, + * after either {@link #assignParameter(SettableBeanProperty, Object)} + * returns true (to indicate all creator properties are found), or when + * then whole JSON Object has been processed, + */ + protected Object[] getParameters(SettableBeanProperty[] props) + throws JsonMappingException + { + // quick check to see if anything else is needed + if (_paramsNeeded > 0) { + if (_paramsSeenBig == null) { + int mask = _paramsSeen; + // not optimal, could use `Integer.trailingZeroes()`, but for now should not + // really matter for common cases + for (int ix = 0, len = _creatorParameters.length; ix < len; ++ix, mask >>= 1) { + if ((mask & 1) == 0) { + _creatorParameters[ix] = _findMissing(props[ix]); + } + } + } else { + final int len = _creatorParameters.length; + for (int ix = 0; (ix = _paramsSeenBig.nextClearBit(ix)) < len; ++ix) { + _creatorParameters[ix] = _findMissing(props[ix]); + } + } + } + return _creatorParameters; + } + + protected Object _findMissing(SettableBeanProperty prop) throws JsonMappingException + { + // First: do we have injectable value? + Object injectableValueId = prop.getInjectableValueId(); + if (injectableValueId != null) { + return _context.findInjectableValue(prop.getInjectableValueId(), + prop, null); + } + // Second: required? + if (prop.isRequired()) { + throw _context.mappingException("Missing required creator property '%s' (index %d)", + prop.getName(), prop.getCreatorIndex()); + } + if (_context.isEnabled(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)) { + throw _context.mappingException("Missing creator property '%s' (index %d); DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES enabled", + prop.getName(), prop.getCreatorIndex()); + } + // Third: default value + JsonDeserializer deser = prop.getValueDeserializer(); + return deser.getNullValue(_context); + } + + /* + /********************************************************** + /* Other methods + /********************************************************** + */ + + + /** + * Helper method called to see if given non-creator property is the "id property"; + * and if so, handle appropriately. + * + * @since 2.1 + */ + public boolean readIdProperty(String propName) throws IOException + { + if ((_objectIdReader != null) && propName.equals(_objectIdReader.propertyName.getSimpleName())) { + _idValue = _objectIdReader.readObjectReference(_parser, _context); + return true; + } + return false; + } + + /** + * Helper method called to handle Object Id value collected earlier, if any + */ + public Object handleIdValue(final DeserializationContext ctxt, Object bean) throws IOException + { + if (_objectIdReader != null) { + if (_idValue != null) { + ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator, _objectIdReader.resolver); + roid.bindItem(bean); + // also: may need to set a property value as well + SettableBeanProperty idProp = _objectIdReader.idProperty; + if (idProp != null) { + return idProp.setAndReturn(bean, _idValue); + } + } else { + // TODO: is this an error case? + throw ctxt.mappingException("No _idValue when handleIdValue called, on instance of %s", + bean.getClass().getName()); + } + } + return bean; + } + + protected PropertyValue buffered() { return _buffered; } + + public boolean isComplete() { return _paramsNeeded <= 0; } + + /** + * @return True if we have received all creator parameters + * + * @since 2.6 + */ + public boolean assignParameter(SettableBeanProperty prop, Object value) + { + final int ix = prop.getCreatorIndex(); + _creatorParameters[ix] = value; + + if (_paramsSeenBig == null) { + int old = _paramsSeen; + int newValue = (old | (1 << ix)); + if (old != newValue) { + _paramsSeen = newValue; + if (--_paramsNeeded <= 0) { + return true; + } + } + } else { + if (!_paramsSeenBig.get(ix)) { + if (--_paramsNeeded <= 0) { + return true; + } + _paramsSeenBig.set(ix); + } + } + return false; + } + + /** + * @deprecated Since 2.6 + */ + @Deprecated + public boolean assignParameter(int index, Object value) { + // !!! TODO: remove from 2.7 + _creatorParameters[index] = value; + return false; + } + + public void bufferProperty(SettableBeanProperty prop, Object value) { + _buffered = new PropertyValue.Regular(_buffered, value, prop); + } + + public void bufferAnyProperty(SettableAnyProperty prop, String propName, Object value) { + _buffered = new PropertyValue.Any(_buffered, value, prop, propName); + } + + public void bufferMapProperty(Object key, Object value) { + _buffered = new PropertyValue.Map(_buffered, value, key); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,155 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference; + +/** + * Simple value container for containing information about single Object Id + * during deserialization + */ +public class ReadableObjectId +{ + /** + * @deprecated Change visibility, if possible; prefer using {@link #resolve()}, which is able + * to handle external id resolving mechanism. + */ + @Deprecated // at least since 2.5. Remove from 2.7 + public Object item; + + @Deprecated + public final Object id; + + protected final ObjectIdGenerator.IdKey _key; + + protected LinkedList _referringProperties; + + protected ObjectIdResolver _resolver; + + @Deprecated // at least since 2.5. Remove from 2.7 + public ReadableObjectId(Object id) { + this.id = id; + _key = null; + } + + public ReadableObjectId(ObjectIdGenerator.IdKey key) { + _key = key; + id = key.key; + } + + public void setResolver(ObjectIdResolver resolver) { + _resolver = resolver; + } + + public ObjectIdGenerator.IdKey getKey() { + return _key; + } + + public void appendReferring(Referring currentReferring) { + if (_referringProperties == null) { + _referringProperties = new LinkedList(); + } + _referringProperties.add(currentReferring); + } + + /** + * Method called to assign actual POJO to which ObjectId refers to: will + * also handle referring properties, if any, by assigning POJO. + */ + public void bindItem(Object ob) throws IOException + { + _resolver.bindItem(_key, ob); + item = ob; + if (_referringProperties != null) { + Iterator it = _referringProperties.iterator(); + _referringProperties = null; + while (it.hasNext()) { + it.next().handleResolvedForwardReference(id, ob); + } + } + } + + public Object resolve(){ + return (item = _resolver.resolveId(_key)); + } + + public boolean hasReferringProperties() { + return (_referringProperties != null) && !_referringProperties.isEmpty(); + } + + public Iterator referringProperties() { + if (_referringProperties == null) { + return Collections. emptyList().iterator(); + } + return _referringProperties.iterator(); + } + + /** + * Method called by {@link DeserializationContext} at the end of deserialization + * if this Object Id was not resolved during normal processing. Call is made to + * allow custom implementations to use alternative resolution strategies; currently + * the only way to make use of this functionality is by sub-classing + * {@link ReadableObjectId} and overriding this method. + *

+ * Default implementation simply returns false to indicate that resolution + * attempt did not succeed. + * + * @return True, if resolution succeeded (and no error needs to be reported); false to + * indicate resolution did not succeed. + * + * @since 2.6 + */ + public boolean tryToResolveUnresolved(DeserializationContext ctxt) + { + return false; + } + + /** + * Allow access to the resolver in case anybody wants to use it directly, for + * examples from + * {@link com.fasterxml.jackson.databind.deser.DefaultDeserializationContext#tryToResolveUnresolvedObjectId}. + * + * @return The registered resolver + * + * @since 2.7 + */ + public ObjectIdResolver getResolver() { + return _resolver; + } + + @Override + public String toString() { + return String.valueOf(_key); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + public static abstract class Referring { + private final UnresolvedForwardReference _reference; + private final Class _beanType; + + public Referring(UnresolvedForwardReference ref, Class beanType) { + _reference = ref; + _beanType = beanType; + } + + public JsonLocation getLocation() { return _reference.getLocation(); } + public Class getBeanType() { return _beanType; } + + public abstract void handleResolvedForwardReference(Object id, Object value) throws IOException; + public boolean hasId(Object id) { + return id.equals(_reference.getUnresolvedId()); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,141 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * This concrete sub-class implements Collection or Map property that is + * indirectly by getting the property value and directly modifying it. + */ +public final class SetterlessProperty + extends SettableBeanProperty +{ + private static final long serialVersionUID = 1L; + + protected final AnnotatedMethod _annotated; + + /** + * Get method for accessing property value used to access property + * (of Collection or Map type) to modify. + */ + protected final Method _getter; + + public SetterlessProperty(BeanPropertyDefinition propDef, JavaType type, + TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedMethod method) { + super(propDef, type, typeDeser, contextAnnotations); + _annotated = method; + _getter = method.getAnnotated(); + } + + protected SetterlessProperty(SetterlessProperty src, JsonDeserializer deser) { + super(src, deser); + _annotated = src._annotated; + _getter = src._getter; + } + + protected SetterlessProperty(SetterlessProperty src, PropertyName newName) { + super(src, newName); + _annotated = src._annotated; + _getter = src._getter; + } + + @Override + public SetterlessProperty withName(PropertyName newName) { + return new SetterlessProperty(this, newName); + } + + @Override + public SetterlessProperty withValueDeserializer(JsonDeserializer deser) { + return new SetterlessProperty(this, deser); + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + @Override + public A getAnnotation(Class acls) { + return _annotated.getAnnotation(acls); + } + + @Override public AnnotatedMember getMember() { return _annotated; } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object instance) throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NULL) { + /* Hmmh. Is this a problem? We won't be setting anything, so it's + * equivalent of empty Collection/Map in this case + */ + return; + } + + // For [#501] fix we need to implement this but: + if (_valueTypeDeserializer != null) { + throw JsonMappingException.from(p, + "Problem deserializing 'setterless' property (\""+getName()+"\"): no way to handle typed deser with setterless yet"); +// return _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer); + } + + // Ok: then, need to fetch Collection/Map to modify: + Object toModify; + try { + toModify = _getter.invoke(instance); + } catch (Exception e) { + _throwAsIOE(p, e); + return; // never gets here + } + /* Note: null won't work, since we can't then inject anything + * in. At least that's not good in common case. However, + * theoretically the case where we get JSON null might + * be compatible. If so, implementation could be changed. + */ + if (toModify == null) { + throw JsonMappingException.from(p, + "Problem deserializing 'setterless' property '"+getName()+"': get method returned null"); + } + _valueDeserializer.deserialize(p, ctxt, toModify); + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, + DeserializationContext ctxt, Object instance) throws IOException + { + deserializeAndSet(p, ctxt, instance); + return instance; + } + + @Override + public final void set(Object instance, Object value) throws IOException { + throw new UnsupportedOperationException("Should never call 'set' on setterless property"); + } + + @Override + public Object setAndReturn(Object instance, Object value) throws IOException + { + set(instance, value); + return null; + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,62 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Simple deserializer that will call configured type deserializer, passing + * in configured data deserializer, and exposing it all as a simple + * deserializer. + * This is necessary when there is no "parent" deserializer which could handle + * details of calling a {@link TypeDeserializer}, most commonly used with + * root values. + */ +public final class TypeWrappedDeserializer + extends JsonDeserializer + implements java.io.Serializable // since 2.5 +{ + private static final long serialVersionUID = 1L; + + final protected TypeDeserializer _typeDeserializer; + final protected JsonDeserializer _deserializer; + + @SuppressWarnings("unchecked") + public TypeWrappedDeserializer(TypeDeserializer typeDeser, JsonDeserializer deser) + { + super(); + _typeDeserializer = typeDeser; + _deserializer = (JsonDeserializer) deser; + } + + @Override + public Class handledType() { + return _deserializer.handledType(); + } + + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + { + return _deserializer.deserializeWithType(jp, ctxt, _typeDeserializer); + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) throws IOException + { + // should never happen? (if it can, could call on that object) + throw new IllegalStateException("Type-wrapped deserializer's deserializeWithType should never get called"); + } + + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt, + Object intoValue) throws IOException + { + /* 01-Mar-2013, tatu: Hmmh. Tough call as to what to do... need + * to delegate, but will this work reliably? Let's just hope so: + */ + return _deserializer.deserialize(jp, ctxt, intoValue); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.util.NameTransformer; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Object that is responsible for handling acrobatics related to + * deserializing "unwrapped" values; sets of properties that are + * embedded (inlined) as properties of parent JSON object. + */ +public class UnwrappedPropertyHandler +{ + protected final List _properties; + + public UnwrappedPropertyHandler() { + _properties = new ArrayList(); + } + protected UnwrappedPropertyHandler(List props) { + _properties = props; + } + + public void addProperty(SettableBeanProperty property) { + _properties.add(property); + } + + public UnwrappedPropertyHandler renameAll(NameTransformer transformer) + { + ArrayList newProps = new ArrayList(_properties.size()); + for (SettableBeanProperty prop : _properties) { + String newName = transformer.transform(prop.getName()); + prop = prop.withSimpleName(newName); + JsonDeserializer deser = prop.getValueDeserializer(); + if (deser != null) { + @SuppressWarnings("unchecked") + JsonDeserializer newDeser = (JsonDeserializer) + deser.unwrappingDeserializer(transformer); + if (newDeser != deser) { + prop = prop.withValueDeserializer(newDeser); + } + } + newProps.add(prop); + } + return new UnwrappedPropertyHandler(newProps); + } + + @SuppressWarnings("resource") + public Object processUnwrapped(JsonParser originalParser, DeserializationContext ctxt, Object bean, + TokenBuffer buffered) + throws IOException, JsonProcessingException + { + for (int i = 0, len = _properties.size(); i < len; ++i) { + SettableBeanProperty prop = _properties.get(i); + JsonParser jp = buffered.asParser(); + jp.nextToken(); + prop.deserializeAndSet(jp, ctxt, bean); + } + return bean; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,54 @@ +package com.fasterxml.jackson.databind.deser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.PropertyMetadata; +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * Class that encapsulates details of value injection that occurs before + * deserialization of a POJO. Details include information needed to find + * injectable value (logical id) as well as method used for assigning + * value (setter or field) + */ +public class ValueInjector + extends BeanProperty.Std +{ + /** + * Identifier used for looking up value to inject + */ + protected final Object _valueId; + + public ValueInjector(PropertyName propName, JavaType type, + Annotations contextAnnotations, AnnotatedMember mutator, + Object valueId) + { + super(propName, type, null, contextAnnotations, mutator, + PropertyMetadata.STD_OPTIONAL); + _valueId = valueId; + } + + @Deprecated // since 2.3 + public ValueInjector(String propName, JavaType type, + Annotations contextAnnotations, AnnotatedMember mutator, + Object valueId) + { + this(new PropertyName(propName), type, contextAnnotations, mutator, valueId); + } + + public Object findValue(DeserializationContext context, Object beanInstance) + { + return context.findInjectableValue(_valueId, this, beanInstance); + } + + public void inject(DeserializationContext context, Object beanInstance) + throws IOException + { + _member.setValue(beanInstance, findValue(context, beanInstance)); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/impl/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,9 @@ +/** + * Contains those implementation classes of deserialization part of + * data binding that are not considered part of public or semi-public + * interfaces. Use of these classes by non-core classes is discouraged, + * although occasionally this may be necessary. + * Note that backwards-compatibility of these classes is not guaranteed + * between minor releases (but is between patch releases). + */ +package com.fasterxml.jackson.databind.deser.impl; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,5 @@ +/** + * Contains implementation classes of deserialization part of + * data binding. + */ +package com.fasterxml.jackson.databind.deser; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,138 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ArrayBlockingQueue; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * We need a custom deserializer both because {@link ArrayBlockingQueue} has no + * default constructor AND because it has size limit used for constructing + * underlying storage automatically. + */ +public class ArrayBlockingQueueDeserializer + extends CollectionDeserializer +{ + private static final long serialVersionUID = 1; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public ArrayBlockingQueueDeserializer(JavaType collectionType, + JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser, + ValueInstantiator valueInstantiator) + { + super(collectionType, valueDeser, valueTypeDeser, valueInstantiator); + } + + /** + * Constructor used when creating contextualized instances. + */ + protected ArrayBlockingQueueDeserializer(JavaType collectionType, + JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser, + ValueInstantiator valueInstantiator, + JsonDeserializer delegateDeser, Boolean unwrapSingle) + { + super(collectionType, valueDeser, valueTypeDeser, valueInstantiator, + delegateDeser, unwrapSingle); + } + + /** + * Copy-constructor that can be used by sub-classes to allow + * copy-on-write styling copying of settings of an existing instance. + */ + protected ArrayBlockingQueueDeserializer(ArrayBlockingQueueDeserializer src) { + super(src); + } + + /** + * Fluent-factory method call to construct contextual instance. + */ + @Override + @SuppressWarnings("unchecked") + protected ArrayBlockingQueueDeserializer withResolved(JsonDeserializer dd, + JsonDeserializer vd, TypeDeserializer vtd, Boolean unwrapSingle) + { + if ((dd == _delegateDeserializer) && (vd == _valueDeserializer) && (vtd == _valueTypeDeserializer) + && (_unwrapSingle == unwrapSingle)) { + return this; + } + return new ArrayBlockingQueueDeserializer(_collectionType, + (JsonDeserializer) vd, vtd, + _valueInstantiator, (JsonDeserializer) dd, unwrapSingle); + + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public Collection deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + { + if (_delegateDeserializer != null) { + return (Collection) _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(jp, ctxt)); + } + if (jp.getCurrentToken() == JsonToken.VALUE_STRING) { + String str = jp.getText(); + if (str.length() == 0) { + return (Collection) _valueInstantiator.createFromString(ctxt, str); + } + } + return deserialize(jp, ctxt, null); + } + + @Override + public Collection deserialize(JsonParser jp, DeserializationContext ctxt, Collection result0) throws IOException + { + // Ok: must point to START_ARRAY (or equivalent) + if (!jp.isExpectedStartArrayToken()) { + return handleNonArray(jp, ctxt, new ArrayBlockingQueue(1)); + } + ArrayList tmp = new ArrayList(); + + JsonDeserializer valueDes = _valueDeserializer; + JsonToken t; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + + try { + while ((t = jp.nextToken()) != JsonToken.END_ARRAY) { + Object value; + + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(jp, ctxt); + } else { + value = valueDes.deserializeWithType(jp, ctxt, typeDeser); + } + tmp.add(value); + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, tmp, tmp.size()); + } + if (result0 != null) { + result0.addAll(tmp); + return result0; + } + return new ArrayBlockingQueue(tmp.size(), false, tmp); + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromArray(jp, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/AtomicBooleanDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/AtomicBooleanDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/AtomicBooleanDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,19 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; + +public class AtomicBooleanDeserializer extends StdScalarDeserializer +{ + private static final long serialVersionUID = 1L; + + public AtomicBooleanDeserializer() { super(AtomicBoolean.class); } + + @Override + public AtomicBoolean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return new AtomicBoolean(_parseBooleanPrimitive(jp, ctxt)); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/AtomicReferenceDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,108 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +public class AtomicReferenceDeserializer + extends StdDeserializer> + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1L; + + /** + * Type of value that we reference + */ + protected final JavaType _referencedType; + + protected final TypeDeserializer _valueTypeDeserializer; + + protected final JsonDeserializer _valueDeserializer; + + /** + * @param referencedType Parameterization of this reference + */ + public AtomicReferenceDeserializer(JavaType referencedType) { + this(referencedType, null, null); + } + + public AtomicReferenceDeserializer(JavaType referencedType, + TypeDeserializer typeDeser, JsonDeserializer deser) + { + super(AtomicReference.class); + _referencedType = referencedType; + _valueDeserializer = deser; + _valueTypeDeserializer = typeDeser; + } + + public AtomicReferenceDeserializer withResolved(TypeDeserializer typeDeser, JsonDeserializer valueDeser) { + if ((valueDeser == _valueDeserializer) && (typeDeser == _valueTypeDeserializer)) { + return this; + } + return new AtomicReferenceDeserializer(_referencedType, typeDeser, valueDeser); + } + + @Override + public AtomicReference getNullValue(DeserializationContext ctxt) { + return new AtomicReference(); + } + + @Deprecated // remove in 2.7 + @Override + public AtomicReference getNullValue() { + return new AtomicReference(); + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException + { + JsonDeserializer deser = _valueDeserializer; + TypeDeserializer typeDeser = _valueTypeDeserializer; + + if (deser == null) { + deser = ctxt.findContextualValueDeserializer(_referencedType, property); + } else { // otherwise directly assigned, probably not contextual yet: + deser = ctxt.handleSecondaryContextualization(deser, property, _referencedType); + } + if (typeDeser != null) { + typeDeser = typeDeser.forProperty(property); + } + return withResolved(typeDeser, deser); + } + + @Override + public AtomicReference deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + Object contents = (_valueTypeDeserializer == null) + ? _valueDeserializer.deserialize(p, ctxt) + : _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); + return new AtomicReference(contents); + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeser) throws IOException + { + final JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NULL) { // can this actually happen? + return getNullValue(ctxt); + } + // 22-Oct-2015, tatu: This handling is probably not needed (or is wrong), but + // could be result of older (pre-2.7) Jackson trying to serialize natural types. + // Because of this, let's allow for now, unless proven problematic + if ((t != null) && t.isScalarValue()) { + return deserialize(p, ctxt); + } + // 19-Apr-2016, tatu: Alas, due to there not really being anything for AtomicReference + // itself, need to just ignore `typeDeser`, use TypeDeserializer we do have for contents + // and it might just work. + + if (_valueTypeDeserializer == null) { + return deserialize(p, ctxt); + } + return new AtomicReference(_valueTypeDeserializer.deserializeTypedFromAny(p, ctxt)); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ByteBufferDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ByteBufferDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ByteBufferDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,30 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.*; +import java.nio.ByteBuffer; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.util.ByteBufferBackedOutputStream; + +public class ByteBufferDeserializer extends StdScalarDeserializer +{ + private static final long serialVersionUID = 1L; + + protected ByteBufferDeserializer() { super(ByteBuffer.class); } + + @Override + public ByteBuffer deserialize(JsonParser parser, DeserializationContext cx) throws IOException { + byte[] b = parser.getBinaryValue(); + return ByteBuffer.wrap(b); + } + + @Override + public ByteBuffer deserialize(JsonParser jp, DeserializationContext ctxt, ByteBuffer intoValue) throws IOException { + // Let's actually read in streaming manner... + OutputStream out = new ByteBufferBackedOutputStream(intoValue); + jp.readBinaryValue(ctxt.getBase64Variant(), out); + out.close(); + return intoValue; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,426 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; +import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Basic serializer that can take JSON "Array" structure and + * construct a {@link java.util.Collection} instance, with typed contents. + *

+ * Note: for untyped content (one indicated by passing Object.class + * as the type), {@link UntypedObjectDeserializer} is used instead. + * It can also construct {@link java.util.List}s, but not with specific + * POJO types, only other containers and primitives/wrappers. + */ +@JacksonStdImpl +public class CollectionDeserializer + extends ContainerDeserializerBase> + implements ContextualDeserializer +{ + private static final long serialVersionUID = -1L; // since 2.5 + + // // Configuration + + protected final JavaType _collectionType; + + /** + * Value deserializer. + */ + protected final JsonDeserializer _valueDeserializer; + + /** + * If element instances have polymorphic type information, this + * is the type deserializer that can handle it + */ + protected final TypeDeserializer _valueTypeDeserializer; + + // // Instance construction settings: + + protected final ValueInstantiator _valueInstantiator; + + /** + * Deserializer that is used iff delegate-based creator is + * to be used for deserializing from JSON Object. + */ + protected final JsonDeserializer _delegateDeserializer; + + /** + * Specific override for this instance (from proper, or global per-type overrides) + * to indicate whether single value may be taken to mean an unwrapped one-element array + * or not. If null, left to global defaults. + * + * @since 2.7 + */ + protected final Boolean _unwrapSingle; + + // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Constructor for context-free instances, where we do not yet know + * which property is using this deserializer. + */ + public CollectionDeserializer(JavaType collectionType, + JsonDeserializer valueDeser, + TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator) + { + this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null); + } + + /** + * Constructor used when creating contextualized instances. + */ + protected CollectionDeserializer(JavaType collectionType, + JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser, + ValueInstantiator valueInstantiator, + JsonDeserializer delegateDeser, + Boolean unwrapSingle) + { + super(collectionType); + _collectionType = collectionType; + _valueDeserializer = valueDeser; + _valueTypeDeserializer = valueTypeDeser; + _valueInstantiator = valueInstantiator; + _delegateDeserializer = delegateDeser; + _unwrapSingle = unwrapSingle; + } + + /** + * Copy-constructor that can be used by sub-classes to allow + * copy-on-write styling copying of settings of an existing instance. + */ + protected CollectionDeserializer(CollectionDeserializer src) + { + super(src._collectionType); + _collectionType = src._collectionType; + _valueDeserializer = src._valueDeserializer; + _valueTypeDeserializer = src._valueTypeDeserializer; + _valueInstantiator = src._valueInstantiator; + _delegateDeserializer = src._delegateDeserializer; + _unwrapSingle = src._unwrapSingle; + } + + /** + * Fluent-factory method call to construct contextual instance. + * + * @since 2.7 + */ + @SuppressWarnings("unchecked") + protected CollectionDeserializer withResolved(JsonDeserializer dd, + JsonDeserializer vd, TypeDeserializer vtd, + Boolean unwrapSingle) + { + if ((dd == _delegateDeserializer) && (vd == _valueDeserializer) && (vtd == _valueTypeDeserializer) + && (_unwrapSingle == unwrapSingle)) { + return this; + } + return new CollectionDeserializer(_collectionType, + (JsonDeserializer) vd, vtd, + _valueInstantiator, (JsonDeserializer) dd, unwrapSingle); + } + + /** + * @deprecated Since 2.7 as it does not pass `unwrapSingle` + */ + @Deprecated // since 2.7 -- will not retain "unwrapSingle" setting + protected CollectionDeserializer withResolved(JsonDeserializer dd, + JsonDeserializer vd, TypeDeserializer vtd) + { + return withResolved(dd, vd, vtd, _unwrapSingle); + } + + // Important: do NOT cache if polymorphic values + @Override // since 2.5 + public boolean isCachable() { + // 26-Mar-2015, tatu: As per [databind#735], need to be careful + return (_valueDeserializer == null) + && (_valueTypeDeserializer == null) + && (_delegateDeserializer == null) + ; + } + + /* + /********************************************************** + /* Validation, post-processing (ResolvableDeserializer) + /********************************************************** + */ + + /** + * Method called to finalize setup of this deserializer, + * when it is known for which property deserializer is needed + * for. + */ + @Override + public CollectionDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + // May need to resolve types for delegate-based creators: + JsonDeserializer delegateDeser = null; + if ((_valueInstantiator != null) && _valueInstantiator.canCreateUsingDelegate()) { + JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig()); + if (delegateType == null) { + throw new IllegalArgumentException("Invalid delegate-creator definition for "+_collectionType + +": value instantiator ("+_valueInstantiator.getClass().getName() + +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'"); + } + delegateDeser = findDeserializer(ctxt, delegateType, property); + } + // [databind#1043]: allow per-property allow-wrapping of single overrides: + // 11-Dec-2015, tatu: Should we pass basic `Collection.class`, or more refined? Mostly + // comes down to "List vs Collection" I suppose... for now, pass Collection + Boolean unwrapSingle = findFormatFeature(ctxt, property, Collection.class, + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + // also, often value deserializer is resolved here: + JsonDeserializer valueDeser = _valueDeserializer; + + // May have a content converter + valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser); + final JavaType vt = _collectionType.getContentType(); + if (valueDeser == null) { + valueDeser = ctxt.findContextualValueDeserializer(vt, property); + } else { // if directly assigned, probably not yet contextual, so: + valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt); + } + // and finally, type deserializer needs context as well + TypeDeserializer valueTypeDeser = _valueTypeDeserializer; + if (valueTypeDeser != null) { + valueTypeDeser = valueTypeDeser.forProperty(property); + } + return withResolved(delegateDeser, valueDeser, valueTypeDeser, unwrapSingle); + } + + /* + /********************************************************** + /* ContainerDeserializerBase API + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _collectionType.getContentType(); + } + + @Override + public JsonDeserializer getContentDeserializer() { + return _valueDeserializer; + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public Collection deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_delegateDeserializer != null) { + return (Collection) _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + /* [JACKSON-620]: empty String may be ok; bit tricky to check, however, since + * there is also possibility of "auto-wrapping" of single-element arrays. + * Hence we only accept empty String here. + */ + if (p.hasToken(JsonToken.VALUE_STRING)) { + String str = p.getText(); + if (str.length() == 0) { + return (Collection) _valueInstantiator.createFromString(ctxt, str); + } + } + return deserialize(p, ctxt, (Collection) _valueInstantiator.createUsingDefault(ctxt)); + } + + @Override + public Collection deserialize(JsonParser p, DeserializationContext ctxt, + Collection result) + throws IOException + { + // Ok: must point to START_ARRAY (or equivalent) + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt, result); + } + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(result); + + JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + CollectionReferringAccumulator referringAccumulator = + (valueDes.getObjectIdReader() == null) ? null : + new CollectionReferringAccumulator(_collectionType.getContentType().getRawClass(), result); + + JsonToken t; + while ((t = p.nextToken()) != JsonToken.END_ARRAY) { + try { + Object value; + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(p, ctxt); + } else { + value = valueDes.deserializeWithType(p, ctxt, typeDeser); + } + if (referringAccumulator != null) { + referringAccumulator.add(value); + } else { + result.add(value); + } + } catch (UnresolvedForwardReference reference) { + if (referringAccumulator == null) { + throw JsonMappingException + .from(p, "Unresolved forward reference but no identity info", reference); + } + Referring ref = referringAccumulator.handleUnresolvedReference(reference); + reference.getRoid().appendReferring(ref); + } catch (Exception e) { + boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS); + if (!wrap && e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw JsonMappingException.wrapWithPath(e, result, result.size()); + } + } + return result; + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromArray(jp, ctxt); + } + + /** + * Helper method called when current token is no START_ARRAY. Will either + * throw an exception, or try to handle value as if member of implicit + * array, depending on configuration. + */ + protected final Collection handleNonArray(JsonParser p, DeserializationContext ctxt, + Collection result) + throws IOException + { + // Implicit arrays from single values? + boolean canWrap = (_unwrapSingle == Boolean.TRUE) || + ((_unwrapSingle == null) && + ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + if (!canWrap) { + throw ctxt.mappingException(_collectionType.getRawClass()); + } + JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + JsonToken t = p.getCurrentToken(); + + Object value; + + try { + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(p, ctxt); + } else { + value = valueDes.deserializeWithType(p, ctxt, typeDeser); + } + } catch (Exception e) { + // note: pass Object.class, not Object[].class, as we need element type for error info + throw JsonMappingException.wrapWithPath(e, Object.class, result.size()); + } + result.add(value); + return result; + } + + public final static class CollectionReferringAccumulator { + private final Class _elementType; + private final Collection _result; + + /** + * A list of {@link CollectionReferring} to maintain ordering. + */ + private List _accumulator = new ArrayList(); + + public CollectionReferringAccumulator(Class elementType, Collection result) { + _elementType = elementType; + _result = result; + } + + public void add(Object value) + { + if (_accumulator.isEmpty()) { + _result.add(value); + } else { + CollectionReferring ref = _accumulator.get(_accumulator.size() - 1); + ref.next.add(value); + } + } + + public Referring handleUnresolvedReference(UnresolvedForwardReference reference) + { + CollectionReferring id = new CollectionReferring(this, reference, _elementType); + _accumulator.add(id); + return id; + } + + public void resolveForwardReference(Object id, Object value) throws IOException + { + Iterator iterator = _accumulator.iterator(); + // Resolve ordering after resolution of an id. This mean either: + // 1- adding to the result collection in case of the first unresolved id. + // 2- merge the content of the resolved id with its previous unresolved id. + Collection previous = _result; + while (iterator.hasNext()) { + CollectionReferring ref = iterator.next(); + if (ref.hasId(id)) { + iterator.remove(); + previous.add(value); + previous.addAll(ref.next); + return; + } + previous = ref.next; + } + + throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id + + "] that wasn't previously seen as unresolved."); + } + } + + /** + * Helper class to maintain processing order of value. The resolved + * object associated with {@link #_id} comes before the values in + * {@link #next}. + */ + private final static class CollectionReferring extends Referring { + private final CollectionReferringAccumulator _parent; + public final List next = new ArrayList(); + + CollectionReferring(CollectionReferringAccumulator parent, + UnresolvedForwardReference reference, Class contentType) + { + super(reference, contentType); + _parent = parent; + } + + @Override + public void handleResolvedForwardReference(Object id, Object value) throws IOException { + _parent.resolveForwardReference(id, value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,85 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; + +/** + * Intermediate base deserializer class that adds more shared accessor + * so that other classes can access information about contained (value) types + */ +@SuppressWarnings("serial") +public abstract class ContainerDeserializerBase + extends StdDeserializer +{ + protected ContainerDeserializerBase(JavaType selfType) { + super(selfType); + } + + /* + /********************************************************** + /* Overrides + /********************************************************** + */ + + @Override + public SettableBeanProperty findBackReference(String refName) { + JsonDeserializer valueDeser = getContentDeserializer(); + if (valueDeser == null) { + throw new IllegalArgumentException("Can not handle managed/back reference '"+refName + +"': type: container deserializer of type "+getClass().getName()+" returned null for 'getContentDeserializer()'"); + } + return valueDeser.findBackReference(refName); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Accessor for declared type of contained value elements; either exact + * type, or one of its supertypes. + */ + public abstract JavaType getContentType(); + + /** + * Accesor for deserializer use for deserializing content values. + */ + public abstract JsonDeserializer getContentDeserializer(); + + /* + /********************************************************** + /* Shared methods for sub-classes + /********************************************************** + */ + + /** + * Helper method called by various Map(-like) deserializers. + */ + protected void wrapAndThrow(Throwable t, Object ref, String key) throws IOException + { + // to handle StackOverflow: + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors and "plain" IOExceptions to be passed as is + if (t instanceof Error) { + throw (Error) t; + } + // ... except for mapping exceptions + if (t instanceof IOException && !(t instanceof JsonMappingException)) { + throw (IOException) t; + } + // for [databind#1141] + if (key == null) { + key = "N/A"; + } + throw JsonMappingException.wrapWithPath(t, ref, key); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,314 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.sql.Timestamp; +import java.text.*; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.util.StdDateFormat; + +/** + * Container class for core JDK date/time type deserializers. + */ +@SuppressWarnings("serial") +public class DateDeserializers +{ + private final static HashSet _classNames = new HashSet(); + static { + Class[] numberTypes = new Class[] { + Calendar.class, + GregorianCalendar.class, + java.sql.Date.class, + java.util.Date.class, + Timestamp.class, + }; + for (Class cls : numberTypes) { + _classNames.add(cls.getName()); + } + } + + public static JsonDeserializer find(Class rawType, String clsName) + { + if (_classNames.contains(clsName)) { + // Start with the most common type + if (rawType == Calendar.class) { + return new CalendarDeserializer(); + } + if (rawType == java.util.Date.class) { + return DateDeserializer.instance; + } + if (rawType == java.sql.Date.class) { + return new SqlDateDeserializer(); + } + if (rawType == Timestamp.class) { + return new TimestampDeserializer(); + } + if (rawType == GregorianCalendar.class) { + return new CalendarDeserializer(GregorianCalendar.class); + } + } + return null; + } + + /* + /********************************************************** + /* Intermediate class for Date-based ones + /********************************************************** + */ + + protected abstract static class DateBasedDeserializer + extends StdScalarDeserializer + implements ContextualDeserializer + { + /** + * Specific format to use, if non-null; if null will + * just use default format. + */ + protected final DateFormat _customFormat; + + /** + * Let's also keep format String for reference, to use for error messages + */ + protected final String _formatString; + + protected DateBasedDeserializer(Class clz) { + super(clz); + _customFormat = null; + _formatString = null; + } + + protected DateBasedDeserializer(DateBasedDeserializer base, + DateFormat format, String formatStr) { + super(base._valueClass); + _customFormat = format; + _formatString = formatStr; + } + + protected abstract DateBasedDeserializer withDateFormat(DateFormat df, String formatStr); + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) + throws JsonMappingException + { + if (property != null) { + JsonFormat.Value format = ctxt.getAnnotationIntrospector().findFormat((Annotated) property.getMember()); + if (format != null) { + TimeZone tz = format.getTimeZone(); + // First: fully custom pattern? + if (format.hasPattern()) { + final String pattern = format.getPattern(); + final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale(); + SimpleDateFormat df = new SimpleDateFormat(pattern, loc); + if (tz == null) { + tz = ctxt.getTimeZone(); + } + df.setTimeZone(tz); + return withDateFormat(df, pattern); + } + // But if not, can still override timezone + if (tz != null) { + DateFormat df = ctxt.getConfig().getDateFormat(); + // one shortcut: with our custom format, can simplify handling a bit + if (df.getClass() == StdDateFormat.class) { + final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale(); + StdDateFormat std = (StdDateFormat) df; + std = std.withTimeZone(tz); + std = std.withLocale(loc); + df = std; + } else { + // otherwise need to clone, re-set timezone: + df = (DateFormat) df.clone(); + df.setTimeZone(tz); + } + return withDateFormat(df, _formatString); + } + } + } + return this; + } + + @Override + protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_customFormat != null) { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_STRING) { + String str = p.getText().trim(); + if (str.length() == 0) { + return (Date) getEmptyValue(ctxt); + } + synchronized (_customFormat) { + try { + return _customFormat.parse(str); + } catch (ParseException e) { + throw new IllegalArgumentException("Failed to parse Date value '"+str + +"' (format: \""+_formatString+"\"): "+e.getMessage()); + } + } + } + // [databind#381] + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Date parsed = _parseDate(p, ctxt); + t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'java.util.Date' value but there was more than a single value in the array"); + } + return parsed; + } + } + return super._parseDate(p, ctxt); + } + } + + /* + /********************************************************** + /* Deserializer implementations for Date types + /********************************************************** + */ + + @JacksonStdImpl + public static class CalendarDeserializer extends DateBasedDeserializer + { + /** + * We may know actual expected type; if so, it will be + * used for instantiation. + */ + protected final Class _calendarClass; + + public CalendarDeserializer() { + super(Calendar.class); + _calendarClass = null; + } + + public CalendarDeserializer(Class cc) { + super(cc); + _calendarClass = cc; + } + + public CalendarDeserializer(CalendarDeserializer src, DateFormat df, String formatString) { + super(src, df, formatString); + _calendarClass = src._calendarClass; + } + + @Override + protected CalendarDeserializer withDateFormat(DateFormat df, String formatString) { + return new CalendarDeserializer(this, df, formatString); + } + + @Override + public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + Date d = _parseDate(p, ctxt); + if (d == null) { + return null; + } + if (_calendarClass == null) { + return ctxt.constructCalendar(d); + } + try { + Calendar c = _calendarClass.newInstance(); + c.setTimeInMillis(d.getTime()); + TimeZone tz = ctxt.getTimeZone(); + if (tz != null) { + c.setTimeZone(tz); + } + return c; + } catch (Exception e) { + throw ctxt.instantiationException(_calendarClass, e); + } + } + } + + /** + * Simple deserializer for handling {@link java.util.Date} values. + *

+ * One way to customize Date formats accepted is to override method + * {@link DeserializationContext#parseDate} that this basic + * deserializer calls. + */ + public static class DateDeserializer extends DateBasedDeserializer + { + public final static DateDeserializer instance = new DateDeserializer(); + + public DateDeserializer() { super(Date.class); } + public DateDeserializer(DateDeserializer base, DateFormat df, String formatString) { + super(base, df, formatString); + } + + @Override + protected DateDeserializer withDateFormat(DateFormat df, String formatString) { + return new DateDeserializer(this, df, formatString); + } + + @Override + public java.util.Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _parseDate(jp, ctxt); + } + } + + /** + * Compared to plain old {@link java.util.Date}, SQL version is easier + * to deal with: mostly because it is more limited. + */ + public static class SqlDateDeserializer + extends DateBasedDeserializer + { + public SqlDateDeserializer() { super(java.sql.Date.class); } + public SqlDateDeserializer(SqlDateDeserializer src, DateFormat df, String formatString) { + super(src, df, formatString); + } + + @Override + protected SqlDateDeserializer withDateFormat(DateFormat df, String formatString) { + return new SqlDateDeserializer(this, df, formatString); + } + + @Override + public java.sql.Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + Date d = _parseDate(jp, ctxt); + return (d == null) ? null : new java.sql.Date(d.getTime()); + } + } + + /** + * Simple deserializer for handling {@link java.sql.Timestamp} values. + *

+ * One way to customize Timestamp formats accepted is to override method + * {@link DeserializationContext#parseDate} that this basic + * deserializer calls. + */ + public static class TimestampDeserializer extends DateBasedDeserializer + { + public TimestampDeserializer() { super(Timestamp.class); } + public TimestampDeserializer(TimestampDeserializer src, DateFormat df, String formatString) { + super(src, df, formatString); + } + + @Override + protected TimestampDeserializer withDateFormat(DateFormat df, String formatString) { + return new TimestampDeserializer(this, df, formatString); + } + + @Override + public java.sql.Timestamp deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + { + return new Timestamp(_parseDate(jp, ctxt).getTime()); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,176 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Base class that simplifies implementations of {@link JsonDeserializer}s + * that mostly delegate functionality to another deserializer implementation + * (possibly forming a chaing of deserializers delegating functionality + * in some cases) + * + * @since 2.1 + */ +public abstract class DelegatingDeserializer + extends StdDeserializer + implements ContextualDeserializer, ResolvableDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final JsonDeserializer _delegatee; + + /* + /********************************************************************** + /* Construction + /********************************************************************** + */ + + public DelegatingDeserializer(JsonDeserializer delegatee) + { + super(_figureType(delegatee)); + _delegatee = delegatee; + } + + protected abstract JsonDeserializer newDelegatingInstance(JsonDeserializer newDelegatee); + + private static Class _figureType(JsonDeserializer deser) + { + Class cls = deser.handledType(); + if (cls != null) { + return cls; + } + return Object.class; + } + + /* + /********************************************************************** + /* Overridden methods for contextualization, resolving + /********************************************************************** + */ + + @Override + public void resolve(DeserializationContext ctxt) throws JsonMappingException { + if (_delegatee instanceof ResolvableDeserializer) { + ((ResolvableDeserializer) _delegatee).resolve(ctxt); + } + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + throws JsonMappingException + { + JavaType vt = ctxt.constructType(_delegatee.handledType()); + JsonDeserializer del = ctxt.handleSecondaryContextualization(_delegatee, + property, vt); + if (del == _delegatee) { + return this; + } + return newDelegatingInstance(del); + } + + /** + * @deprecated Since 2.3, use {@link #newDelegatingInstance} instead + */ + @Deprecated + protected JsonDeserializer _createContextual(DeserializationContext ctxt, + BeanProperty property, JsonDeserializer newDelegatee) + { + if (newDelegatee == _delegatee) { + return this; + } + return newDelegatingInstance(newDelegatee); + } + + @Override + public SettableBeanProperty findBackReference(String logicalName) { + // [Issue#253]: Hope this works.... + return _delegatee.findBackReference(logicalName); + } + + /* + /********************************************************************** + /* Overridden deserialization methods + /********************************************************************** + */ + + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + return _delegatee.deserialize(jp, ctxt); + } + + @SuppressWarnings("unchecked") + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt, + Object intoValue) + throws IOException, JsonProcessingException + { + return ((JsonDeserializer)_delegatee).deserialize(jp, ctxt, intoValue); + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException, JsonProcessingException + { + return _delegatee.deserializeWithType(jp, ctxt, typeDeserializer); + } + + /* + /********************************************************************** + /* Overridden other methods + /********************************************************************** + */ + + @Override + public JsonDeserializer replaceDelegatee(JsonDeserializer delegatee) + { + if (delegatee == _delegatee) { + return this; + } + return newDelegatingInstance(delegatee); + } + + @Override + public Object getNullValue(DeserializationContext ctxt) throws JsonMappingException { + return _delegatee.getNullValue(ctxt); + } + + @Override + public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { + return _delegatee.getEmptyValue(ctxt); + } + + @Override + @Deprecated // remove in 2.7 + public Object getNullValue() { return _delegatee.getNullValue(); } + + // Remove in 2.7 + @Override + @Deprecated // remove in 2.7 + public Object getEmptyValue() { return _delegatee.getEmptyValue(); } + + + @Override + public Collection getKnownPropertyNames() { return _delegatee.getKnownPropertyNames(); } + + @Override + public boolean isCachable() { return _delegatee.isCachable(); } + + @Override + public ObjectIdReader getObjectIdReader() { return _delegatee.getObjectIdReader(); } + + @Override + public JsonDeserializer getDelegatee() { + return _delegatee; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,286 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.lang.reflect.Method; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.CompactStringObjectMap; +import com.fasterxml.jackson.databind.util.EnumResolver; + +/** + * Deserializer class that can deserialize instances of + * specified Enum class from Strings and Integers. + */ +@JacksonStdImpl // was missing until 2.6 +public class EnumDeserializer + extends StdScalarDeserializer +{ + private static final long serialVersionUID = 1L; + + /** + * @since 2.6 + */ + protected Object[] _enumsByIndex; + + /** + * @since 2.7.3 + */ + protected final CompactStringObjectMap _lookupByName; + + /** + * Alternatively, we may need a different lookup object if "use toString" + * is defined. + * + * @since 2.7.3 + */ + protected CompactStringObjectMap _lookupByToString; + + public EnumDeserializer(EnumResolver byNameResolver) + { + super(byNameResolver.getEnumClass()); + _lookupByName = byNameResolver.constructLookup(); + _enumsByIndex = byNameResolver.getRawEnums(); + } + + /** + * Factory method used when Enum instances are to be deserialized + * using a creator (static factory method) + * + * @return Deserializer based on given factory method, if type was suitable; + * null if type can not be used + */ + public static JsonDeserializer deserializerForCreator(DeserializationConfig config, + Class enumClass, AnnotatedMethod factory) + { + // note: caller has verified there's just one arg; but we must verify its type + Class paramClass = factory.getRawParameterType(0); + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(factory.getMember(), + config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return new FactoryBasedDeserializer(enumClass, factory, paramClass); + } + + /* + /********************************************************** + /* Default JsonDeserializer implementation + /********************************************************** + */ + + /** + * Because of costs associated with constructing Enum resolvers, + * let's cache instances by default. + */ + @Override + public boolean isCachable() { return true; } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + JsonToken curr = p.getCurrentToken(); + + // Usually should just get string value: + if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) { + CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING) + ? _getToStringLookup() : _lookupByName; + final String name = p.getText(); + Object result = lookup.find(name); + if (result == null) { + return _deserializeAltString(p, ctxt, lookup, name); + } + return result; + } + // But let's consider int acceptable as well (if within ordinal range) + if (curr == JsonToken.VALUE_NUMBER_INT) { + // ... unless told not to do that + int index = p.getIntValue(); + if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) { + _failOnNumber(ctxt, p, index); + } + if (index >= 0 && index <= _enumsByIndex.length) { + return _enumsByIndex[index]; + } + if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { + throw ctxt.weirdNumberException(index, _enumClass(), + "index value outside legal index range [0.."+(_enumsByIndex.length-1)+"]"); + } + return null; + } + return _deserializeOther(p, ctxt); + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + private final Object _deserializeAltString(JsonParser p, DeserializationContext ctxt, + CompactStringObjectMap lookup, String name) throws IOException + { + name = name.trim(); + if (name.length() == 0) { + if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { + return null; + } + } else { + // [databind#149]: Allow use of 'String' indexes as well + char c = name.charAt(0); + if (c >= '0' && c <= '9') { + try { + int ix = Integer.parseInt(name); + if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) { + _failOnNumber(ctxt, p, ix); + } + if (ix >= 0 && ix <= _enumsByIndex.length) { + return _enumsByIndex[ix]; + } + } catch (NumberFormatException e) { + // fine, ignore, was not an integer + } + } + } + if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { + throw ctxt.weirdStringException(name, _enumClass(), + "value not one of declared Enum instance names: "+lookup.keys()); + } + return null; + } + + protected Object _deserializeOther(JsonParser p, DeserializationContext ctxt) throws IOException + { + JsonToken curr = p.getCurrentToken(); + // [databind#381] + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + && p.isExpectedStartArrayToken()) { + p.nextToken(); + final Object parsed = deserialize(p, ctxt); + curr = p.nextToken(); + if (curr != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single '" + _enumClass().getName() + "' value but there was more than a single value in the array"); + } + return parsed; + } + throw ctxt.mappingException(_enumClass()); + } + + protected void _failOnNumber(DeserializationContext ctxt, JsonParser p, int index) + throws IOException + { + throw InvalidFormatException.from(p, + String.format("Not allowed to deserialize Enum value out of JSON number (%d): disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow", + index), + index, _enumClass()); + } + + protected Class _enumClass() { + return handledType(); + } + + protected CompactStringObjectMap _getToStringLookup() + { + CompactStringObjectMap lookup = _lookupByToString; + // note: exact locking not needed; all we care for here is to try to + // reduce contention for the initial resolution + if (lookup == null) { + synchronized (this) { + lookup = EnumResolver.constructUnsafeUsingToString(_enumClass()) + .constructLookup(); + } + _lookupByToString = lookup; + } + return lookup; + } + + /* + /********************************************************** + /* Additional helper classes + /********************************************************** + */ + + /** + * Deserializer that uses a single-String static factory method + * for locating Enum values by String id. + */ + protected static class FactoryBasedDeserializer + extends StdDeserializer + implements ContextualDeserializer + { + private static final long serialVersionUID = 1; + + // Marker type; null if String expected; otherwise numeric wrapper + protected final Class _inputType; + protected final Method _factory; + protected final JsonDeserializer _deser; + + public FactoryBasedDeserializer(Class cls, AnnotatedMethod f, + Class inputType) + { + super(cls); + _factory = f.getAnnotated(); + _inputType = inputType; + _deser = null; + } + + protected FactoryBasedDeserializer(FactoryBasedDeserializer base, + JsonDeserializer deser) { + super(base._valueClass); + _inputType = base._inputType; + _factory = base._factory; + _deser = deser; + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + throws JsonMappingException + { + if ((_deser == null) && (_inputType != String.class)) { + return new FactoryBasedDeserializer(this, + ctxt.findContextualValueDeserializer(ctxt.constructType(_inputType), property)); + } + return this; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + Object value; + if (_deser != null) { + value = _deser.deserialize(p, ctxt); + } else { + JsonToken curr = p.getCurrentToken(); + if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) { + value = p.getText(); + } else { + value = p.getValueAsString(); + } + } + try { + return _factory.invoke(_valueClass, value); + } catch (Exception e) { + Throwable t = ClassUtil.getRootCause(e); + if (t instanceof IOException) { + throw (IOException) t; + } + throw ctxt.instantiationException(_valueClass, t); + } + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + if (_deser == null) { // String never has type info + return deserialize(p, ctxt); + } + return typeDeserializer.deserializeTypedFromAny(p, ctxt); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,188 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Deserializer for {@link EnumMap} values. + *

+ * Note: casting within this class is all messed up -- just could not figure out a way + * to properly deal with recursive definition of "EnumMap<K extends Enum<K>, V> + */ +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class EnumMapDeserializer + extends ContainerDeserializerBase> + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1; + + protected final JavaType _mapType; + + protected final Class _enumClass; + + protected KeyDeserializer _keyDeserializer; + + protected JsonDeserializer _valueDeserializer; + + /** + * If value instances have polymorphic type information, this + * is the type deserializer that can handle it + */ + protected final TypeDeserializer _valueTypeDeserializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public EnumMapDeserializer(JavaType mapType, KeyDeserializer keyDeserializer, JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser) + { + super(mapType); + _mapType = mapType; + _enumClass = mapType.getKeyType().getRawClass(); + _keyDeserializer = keyDeserializer; + _valueDeserializer = (JsonDeserializer) valueDeser; + _valueTypeDeserializer = valueTypeDeser; + } + + public EnumMapDeserializer withResolved(KeyDeserializer keyDeserializer, JsonDeserializer valueDeserializer, TypeDeserializer valueTypeDeser) + { + if ((keyDeserializer == _keyDeserializer) && (valueDeserializer == _valueDeserializer) && (valueTypeDeser == _valueTypeDeserializer)) { + return this; + } + return new EnumMapDeserializer(_mapType, keyDeserializer, valueDeserializer, _valueTypeDeserializer); + } + + /** + * Method called to finalize setup of this deserializer, + * when it is known for which property deserializer is needed for. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException + { + // note: instead of finding key deserializer, with enums we actually + // work with regular deserializers (less code duplication; but not + // quite as clean as it ought to be) + KeyDeserializer kd = _keyDeserializer; + if (kd == null) { + kd = ctxt.findKeyDeserializer(_mapType.getKeyType(), property); + } + JsonDeserializer vd = _valueDeserializer; + final JavaType vt = _mapType.getContentType(); + if (vd == null) { + vd = ctxt.findContextualValueDeserializer(vt, property); + } else { // if directly assigned, probably not yet contextual, so: + vd = ctxt.handleSecondaryContextualization(vd, property, vt); + } + TypeDeserializer vtd = _valueTypeDeserializer; + if (vtd != null) { + vtd = vtd.forProperty(property); + } + return withResolved(kd, vd, vtd); + } + + /** + * Because of costs associated with constructing Enum resolvers, + * let's cache instances by default. + */ + @Override + public boolean isCachable() { + // Important: do NOT cache if polymorphic values + return (_valueDeserializer == null) + && (_keyDeserializer == null) + && (_valueTypeDeserializer == null); + } + + /* + /********************************************************** + /* ContainerDeserializerBase API + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _mapType.getContentType(); + } + + @Override + public JsonDeserializer getContentDeserializer() { + return _valueDeserializer; + } + + /* + /********************************************************** + /* Actual deserialization + /********************************************************** + */ + + @Override + public EnumMap deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + // Ok: must point to START_OBJECT + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + return _deserializeFromEmpty(jp, ctxt); + } + EnumMap result = constructMap(); + final JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + + while ((jp.nextToken()) == JsonToken.FIELD_NAME) { + String keyName = jp.getCurrentName(); // just for error message + // but we need to let key deserializer handle it separately, nonetheless + Enum key = (Enum) _keyDeserializer.deserializeKey(keyName, ctxt); + if (key == null) { + if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { + throw ctxt.weirdStringException(keyName, _enumClass, "value not one of declared Enum instance names for " + +_mapType.getKeyType()); + } + /* 24-Mar-2012, tatu: Null won't work as a key anyway, so let's + * just skip the entry then. But we must skip the value as well, if so. + */ + jp.nextToken(); + jp.skipChildren(); + continue; + } + // And then the value... + JsonToken t = jp.nextToken(); + /* note: MUST check for nulls separately: deserializers will + * not handle them (and maybe fail or return bogus data) + */ + Object value; + + try { + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(jp, ctxt); + } else { + value = valueDes.deserializeWithType(jp, ctxt, typeDeser); + } + } catch (Exception e) { + wrapAndThrow(e, result, keyName); + return null; + } + result.put(key, value); + } + return result; + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) + throws IOException, JsonProcessingException + { + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromObject(jp, ctxt); + } + + protected EnumMap constructMap() { + return new EnumMap(_enumClass); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,198 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Standard deserializer for {@link EnumSet}s. + *

+ * Note: casting within this class is all messed up -- just could not figure out a way + * to properly deal with recursive definition of "EnumSet<K extends Enum<K>, V> + */ +@SuppressWarnings("rawtypes") +public class EnumSetDeserializer + extends StdDeserializer> + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1L; // since 2.5 + + protected final JavaType _enumType; + + protected final Class _enumClass; + + protected JsonDeserializer> _enumDeserializer; + + /** + * Specific override for this instance (from proper, or global per-type overrides) + * to indicate whether single value may be taken to mean an unwrapped one-element array + * or not. If null, left to global defaults. + * + * @since 2.7 + */ + protected final Boolean _unwrapSingle; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + @SuppressWarnings("unchecked" ) + public EnumSetDeserializer(JavaType enumType, JsonDeserializer deser) + { + super(EnumSet.class); + _enumType = enumType; + _enumClass = (Class) enumType.getRawClass(); + // sanity check + if (!_enumClass.isEnum()) { + throw new IllegalArgumentException("Type "+enumType+" not Java Enum type"); + } + _enumDeserializer = (JsonDeserializer>) deser; + _unwrapSingle = null; + } + + /** + * @since 2.7 + */ + @SuppressWarnings("unchecked" ) + protected EnumSetDeserializer(EnumSetDeserializer base, + JsonDeserializer deser, Boolean unwrapSingle) { + super(EnumSet.class); + _enumType = base._enumType; + _enumClass = base._enumClass; + _enumDeserializer = (JsonDeserializer>) deser; + _unwrapSingle = unwrapSingle; + } + + public EnumSetDeserializer withDeserializer(JsonDeserializer deser) { + if (_enumDeserializer == deser) { + return this; + } + return new EnumSetDeserializer(this, deser, _unwrapSingle); + } + + public EnumSetDeserializer withResolved(JsonDeserializer deser, Boolean unwrapSingle) { + if ((_unwrapSingle == unwrapSingle) && (_enumDeserializer == deser)) { + return this; + } + return new EnumSetDeserializer(this, deser, unwrapSingle); + } + + /** + * Because of costs associated with constructing Enum resolvers, + * let's cache instances by default. + */ + @Override + public boolean isCachable() { + // One caveat: content deserializer should prevent caching + if (_enumType.getValueHandler() != null) { + return false; + } + return true; + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + Boolean unwrapSingle = findFormatFeature(ctxt, property, EnumSet.class, + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + JsonDeserializer deser = _enumDeserializer; + if (deser == null) { + deser = ctxt.findContextualValueDeserializer(_enumType, property); + } else { // if directly assigned, probably not yet contextual, so: + deser = ctxt.handleSecondaryContextualization(deser, property, _enumType); + } + return withResolved(deser, unwrapSingle); + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public EnumSet deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // Ok: must point to START_ARRAY (or equivalent) + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + EnumSet result = constructSet(); + JsonToken t; + + try { + while ((t = p.nextToken()) != JsonToken.END_ARRAY) { + /* What to do with nulls? Fail or ignore? Fail, for now + * (note: would fail if we passed it to EnumDeserializer, too, + * but in general nulls should never be passed to non-container + * deserializers) + */ + if (t == JsonToken.VALUE_NULL) { + throw ctxt.mappingException(_enumClass); + } + Enum value = _enumDeserializer.deserialize(p, ctxt); + /* 24-Mar-2012, tatu: As per [JACKSON-810], may actually get nulls; + * but EnumSets don't allow nulls so need to skip. + */ + if (value != null) { + result.add(value); + } + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, result, result.size()); + } + return result; + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException, JsonProcessingException + { + return typeDeserializer.deserializeTypedFromArray(p, ctxt); + } + + @SuppressWarnings("unchecked") + private EnumSet constructSet() + { + // superbly ugly... but apparently necessary + return EnumSet.noneOf(_enumClass); + } + + @SuppressWarnings("unchecked") + protected EnumSet handleNonArray(JsonParser p, DeserializationContext ctxt) + throws IOException + { + boolean canWrap = (_unwrapSingle == Boolean.TRUE) || + ((_unwrapSingle == null) && + ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + + if (!canWrap) { + throw ctxt.mappingException(EnumSet.class); + } + + EnumSet result = constructSet(); + // First: since `null`s not allowed, slightly simpler... + if (p.hasToken(JsonToken.VALUE_NULL)) { + throw ctxt.mappingException(_enumClass); + } + try { + Enum value = _enumDeserializer.deserialize(p, ctxt); + if (value != null) { + result.add(value); + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, result, result.size()); + } + return result; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,291 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.*; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Currency; +import java.util.Locale; +import java.util.TimeZone; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Base class for simple deserializers that only accept JSON String + * values as the source. + */ +@SuppressWarnings("serial") +public abstract class FromStringDeserializer extends StdScalarDeserializer +{ + public static Class[] types() { + return new Class[] { + File.class, + URL.class, + URI.class, + Class.class, + JavaType.class, + Currency.class, + Pattern.class, + Locale.class, + Charset.class, + TimeZone.class, + InetAddress.class, + InetSocketAddress.class, + }; + } + + /* + /********************************************************** + /* Deserializer implementations + /********************************************************** + */ + + protected FromStringDeserializer(Class vc) { + super(vc); + } + + /** + * Factory method for trying to find a deserializer for one of supported + * types that have simple from-String serialization. + */ + public static Std findDeserializer(Class rawType) + { + int kind = 0; + if (rawType == File.class) { + kind = Std.STD_FILE; + } else if (rawType == URL.class) { + kind = Std.STD_URL; + } else if (rawType == URI.class) { + kind = Std.STD_URI; + } else if (rawType == Class.class) { + kind = Std.STD_CLASS; + } else if (rawType == JavaType.class) { + kind = Std.STD_JAVA_TYPE; + } else if (rawType == Currency.class) { + kind = Std.STD_CURRENCY; + } else if (rawType == Pattern.class) { + kind = Std.STD_PATTERN; + } else if (rawType == Locale.class) { + kind = Std.STD_LOCALE; + } else if (rawType == Charset.class) { + kind = Std.STD_CHARSET; + } else if (rawType == TimeZone.class) { + kind = Std.STD_TIME_ZONE; + } else if (rawType == InetAddress.class) { + kind = Std.STD_INET_ADDRESS; + } else if (rawType == InetSocketAddress.class) { + kind = Std.STD_INET_SOCKET_ADDRESS; + } else { + return null; + } + return new Std(rawType, kind); + } + + /* + /********************************************************** + /* Deserializer implementations + /********************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // Issue#381 + if (p.getCurrentToken() == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final T value = deserialize(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array"); + } + return value; + } + // 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion: + String text = p.getValueAsString(); + if (text != null) { // has String representation + if (text.length() == 0 || (text = text.trim()).length() == 0) { + // 04-Feb-2013, tatu: Usually should become null; but not always + return _deserializeFromEmptyString(); + } + Exception cause = null; + try { + T result = _deserialize(text, ctxt); + if (result != null) { + return result; + } + } catch (IllegalArgumentException iae) { + cause = iae; + } + String msg = "not a valid textual representation"; + if (cause != null) { + String m2 = cause.getMessage(); + if (m2 != null) { + msg = msg + ", problem: "+m2; + } + } + JsonMappingException e = ctxt.weirdStringException(text, _valueClass, msg); + if (cause != null) { + e.initCause(cause); + } + throw e; + // nothing to do here, yet? We'll fail anyway + } + if (p.getCurrentToken() == JsonToken.VALUE_EMBEDDED_OBJECT) { + // Trivial cases; null to null, instance of type itself returned as is + Object ob = p.getEmbeddedObject(); + if (ob == null) { + return null; + } + if (_valueClass.isAssignableFrom(ob.getClass())) { + return (T) ob; + } + return _deserializeEmbedded(ob, ctxt); + } + throw ctxt.mappingException(_valueClass); + } + + protected abstract T _deserialize(String value, DeserializationContext ctxt) throws IOException; + + protected T _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws IOException { + // default impl: error out + throw ctxt.mappingException("Don't know how to convert embedded Object of type %s into %s", + ob.getClass().getName(), _valueClass.getName()); + } + + protected T _deserializeFromEmptyString() throws IOException { + return null; + } + + /* + /********************************************************** + /* A general-purpose implementation + /********************************************************** + */ + + /** + * "Chameleon" deserializer that works on simple types that are deserialized + * from a simple String. + * + * @since 2.4 + */ + public static class Std extends FromStringDeserializer + { + private static final long serialVersionUID = 1; + + public final static int STD_FILE = 1; + public final static int STD_URL = 2; + public final static int STD_URI = 3; + public final static int STD_CLASS = 4; + public final static int STD_JAVA_TYPE = 5; + public final static int STD_CURRENCY = 6; + public final static int STD_PATTERN = 7; + public final static int STD_LOCALE = 8; + public final static int STD_CHARSET = 9; + public final static int STD_TIME_ZONE = 10; + public final static int STD_INET_ADDRESS = 11; + public final static int STD_INET_SOCKET_ADDRESS = 12; + + protected final int _kind; + + protected Std(Class valueType, int kind) { + super(valueType); + _kind = kind; + } + + @Override + protected Object _deserialize(String value, DeserializationContext ctxt) throws IOException + { + switch (_kind) { + case STD_FILE: + return new File(value); + case STD_URL: + return new URL(value); + case STD_URI: + return URI.create(value); + case STD_CLASS: + try { + return ctxt.findClass(value); + } catch (Exception e) { + throw ctxt.instantiationException(_valueClass, ClassUtil.getRootCause(e)); + } + case STD_JAVA_TYPE: + return ctxt.getTypeFactory().constructFromCanonical(value); + case STD_CURRENCY: + // will throw IAE if unknown: + return Currency.getInstance(value); + case STD_PATTERN: + // will throw IAE (or its subclass) if malformed + return Pattern.compile(value); + case STD_LOCALE: + { + int ix = value.indexOf('_'); + if (ix < 0) { // single argument + return new Locale(value); + } + String first = value.substring(0, ix); + value = value.substring(ix+1); + ix = value.indexOf('_'); + if (ix < 0) { // two pieces + return new Locale(first, value); + } + String second = value.substring(0, ix); + return new Locale(first, second, value.substring(ix+1)); + } + case STD_CHARSET: + return Charset.forName(value); + case STD_TIME_ZONE: + return TimeZone.getTimeZone(value); + case STD_INET_ADDRESS: + return InetAddress.getByName(value); + case STD_INET_SOCKET_ADDRESS: + if (value.startsWith("[")) { + // bracketed IPv6 (with port number) + + int i = value.lastIndexOf(']'); + if (i == -1) { + throw new InvalidFormatException(ctxt.getParser(), + "Bracketed IPv6 address must contain closing bracket", + value, InetSocketAddress.class); + } + + int j = value.indexOf(':', i); + int port = j > -1 ? Integer.parseInt(value.substring(j + 1)) : 0; + return new InetSocketAddress(value.substring(0, i + 1), port); + } else { + int ix = value.indexOf(':'); + if (ix >= 0 && value.indexOf(':', ix + 1) < 0) { + // host:port + int port = Integer.parseInt(value.substring(ix+1)); + return new InetSocketAddress(value.substring(0, ix), port); + } + // host or unbracketed IPv6, without port number + return new InetSocketAddress(value, 0); + } + } + throw new IllegalArgumentException(); + } + + @Override + protected Object _deserializeFromEmptyString() throws IOException { + // As per [databind#398], URI requires special handling + if (_kind == STD_URI) { + return URI.create(""); + } + // As per [databind#1123], Locale too + if (_kind == STD_LOCALE) { + return Locale.ROOT; + } + return super._deserializeFromEmptyString(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,51 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.fasterxml.jackson.databind.*; + +/** + * Container class that contains serializers for JDK types that + * require special handling for some reason. + */ +public class JdkDeserializers +{ + private final static HashSet _classNames = new HashSet(); + static { + // note: can skip primitive types; other ways to check them: + Class[] types = new Class[] { + UUID.class, + AtomicBoolean.class, + StackTraceElement.class, + ByteBuffer.class + }; + for (Class cls : types) { _classNames.add(cls.getName()); } + for (Class cls : FromStringDeserializer.types()) { _classNames.add(cls.getName()); } + } + + public static JsonDeserializer find(Class rawType, String clsName) + { + if (_classNames.contains(clsName)) { + JsonDeserializer d = FromStringDeserializer.findDeserializer(rawType); + if (d != null) { + return d; + } + if (rawType == UUID.class) { + return new UUIDDeserializer(); + } + if (rawType == StackTraceElement.class) { + return new StackTraceElementDeserializer(); + } + if (rawType == AtomicBoolean.class) { + // (note: AtomicInteger/Long work due to single-arg constructor. For now? + return new AtomicBooleanDeserializer(); + } + if (rawType == ByteBuffer.class) { + return new ByteBufferDeserializer(); + } + } + return null; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,59 @@ +package com.fasterxml.jackson.databind.deser.std; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.PropertyMetadata; +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.deser.CreatorProperty; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; + +/** + * For {@link JsonLocation}, we should be able to just implement + * {@link ValueInstantiator} (not that explicit one would be very + * hard but...) + */ +public class JsonLocationInstantiator extends ValueInstantiator +{ + @Override + public String getValueTypeDesc() { + return JsonLocation.class.getName(); + } + + @Override + public boolean canCreateFromObjectWith() { return true; } + + @Override + public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { + JavaType intType = config.constructType(Integer.TYPE); + JavaType longType = config.constructType(Long.TYPE); + return new SettableBeanProperty[] { + creatorProp("sourceRef", config.constructType(Object.class), 0), + creatorProp("byteOffset", longType, 1), + creatorProp("charOffset", longType, 2), + creatorProp("lineNr", intType, 3), + creatorProp("columnNr", intType, 4) + }; + } + + private static CreatorProperty creatorProp(String name, JavaType type, int index) { + return new CreatorProperty(PropertyName.construct(name), type, null, + null, null, null, index, null, PropertyMetadata.STD_REQUIRED); + } + + @Override + public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) { + return new JsonLocation(args[0], _long(args[1]), _long(args[2]), + _int(args[3]), _int(args[4])); + } + + private final static long _long(Object o) { + return (o == null) ? 0L : ((Number) o).longValue(); + } + + private final static int _int(Object o) { + return (o == null) ? 0 : ((Number) o).intValue(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,396 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.node.*; +import com.fasterxml.jackson.databind.util.RawValue; + +/** + * Deserializer that can build instances of {@link JsonNode} from any + * JSON content, using appropriate {@link JsonNode} type. + */ +@SuppressWarnings("serial") +public class JsonNodeDeserializer + extends BaseNodeDeserializer +{ + /** + * Singleton instance of generic deserializer for {@link JsonNode}. + * Only used for types other than JSON Object and Array. + */ + private final static JsonNodeDeserializer instance = new JsonNodeDeserializer(); + + protected JsonNodeDeserializer() { super(JsonNode.class); } + + /** + * Factory method for accessing deserializer for specific node type + */ + public static JsonDeserializer getDeserializer(Class nodeClass) + { + if (nodeClass == ObjectNode.class) { + return ObjectDeserializer.getInstance(); + } + if (nodeClass == ArrayNode.class) { + return ArrayDeserializer.getInstance(); + } + // For others, generic one works fine + return instance; + } + + /* + /********************************************************** + /* Actual deserializer implementations + /********************************************************** + */ + + @Override + public JsonNode getNullValue(DeserializationContext ctxt) { + return NullNode.getInstance(); + } + + @Override + @Deprecated // since 2.6, remove from 2.7 + public JsonNode getNullValue() { + return NullNode.getInstance(); + } + + /** + * Implementation that will produce types of any JSON nodes; not just one + * deserializer is registered to handle (in case of more specialized handler). + * Overridden by typed sub-classes for more thorough checking + */ + @Override + public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_START_OBJECT: + return deserializeObject(p, ctxt, ctxt.getNodeFactory()); + case JsonTokenId.ID_START_ARRAY: + return deserializeArray(p, ctxt, ctxt.getNodeFactory()); + default: + return deserializeAny(p, ctxt, ctxt.getNodeFactory()); + } + } + + /* + /********************************************************** + /* Specific instances for more accurate types + /********************************************************** + */ + + final static class ObjectDeserializer + extends BaseNodeDeserializer + { + private static final long serialVersionUID = 1L; + + protected final static ObjectDeserializer _instance = new ObjectDeserializer(); + + protected ObjectDeserializer() { super(ObjectNode.class); } + + public static ObjectDeserializer getInstance() { return _instance; } + + @Override + public ObjectNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (p.isExpectedStartObjectToken() || p.hasToken(JsonToken.FIELD_NAME)) { + return deserializeObject(p, ctxt, ctxt.getNodeFactory()); + } + // 23-Sep-2015, tatu: Ugh. We may also be given END_OBJECT (similar to FIELD_NAME), + // if caller has advanced to the first token of Object, but for empty Object + if (p.hasToken(JsonToken.END_OBJECT)) { + return ctxt.getNodeFactory().objectNode(); + } + throw ctxt.mappingException(ObjectNode.class); + } + } + + final static class ArrayDeserializer + extends BaseNodeDeserializer + { + private static final long serialVersionUID = 1L; + + protected final static ArrayDeserializer _instance = new ArrayDeserializer(); + + protected ArrayDeserializer() { super(ArrayNode.class); } + + public static ArrayDeserializer getInstance() { return _instance; } + + @Override + public ArrayNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (p.isExpectedStartArrayToken()) { + return deserializeArray(p, ctxt, ctxt.getNodeFactory()); + } + throw ctxt.mappingException(ArrayNode.class); + } + } +} + +/** + * Base class for all actual {@link JsonNode} deserializer + * implementations + */ +@SuppressWarnings("serial") +abstract class BaseNodeDeserializer + extends StdDeserializer +{ + public BaseNodeDeserializer(Class vc) { + super(vc); + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + /* Output can be as JSON Object, Array or scalar: no way to know + * a priori. So: + */ + return typeDeserializer.deserializeTypedFromAny(p, ctxt); + } + + /* 07-Nov-2014, tatu: When investigating [databind#604], realized that it makes + * sense to also mark this is cachable, since lookup not exactly free, and + * since it's not uncommon to "read anything" + */ + @Override + public boolean isCachable() { return true; } + + /* + /********************************************************** + /* Overridable methods + /********************************************************** + */ + + protected void _reportProblem(JsonParser p, String msg) throws JsonMappingException { + throw JsonMappingException.from(p, msg); + } + + /** + * Method called when there is a duplicate value for a field. + * By default we don't care, and the last value is used. + * Can be overridden to provide alternate handling, such as throwing + * an exception, or choosing different strategy for combining values + * or choosing which one to keep. + * + * @param fieldName Name of the field for which duplicate value was found + * @param objectNode Object node that contains values + * @param oldValue Value that existed for the object node before newValue + * was added + * @param newValue Newly added value just added to the object node + */ + protected void _handleDuplicateField(JsonParser p, DeserializationContext ctxt, + JsonNodeFactory nodeFactory, + String fieldName, ObjectNode objectNode, + JsonNode oldValue, JsonNode newValue) + throws JsonProcessingException + { + // [Issue#237]: Report an error if asked to do so: + if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)) { + _reportProblem(p, "Duplicate field '"+fieldName+"' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled"); + } + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected final ObjectNode deserializeObject(JsonParser p, DeserializationContext ctxt, + final JsonNodeFactory nodeFactory) throws IOException + { + ObjectNode node = nodeFactory.objectNode(); + String key; + if (p.isExpectedStartObjectToken()) { + key = p.nextFieldName(); + } else { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_OBJECT) { + return node; + } + if (t != JsonToken.FIELD_NAME) { + throw ctxt.mappingException(handledType(), p.getCurrentToken()); + } + key = p.getCurrentName(); + } + for (; key != null; key = p.nextFieldName()) { + JsonNode value; + JsonToken t = p.nextToken(); + switch (t.id()) { + case JsonTokenId.ID_START_OBJECT: + value = deserializeObject(p, ctxt, nodeFactory); + break; + case JsonTokenId.ID_START_ARRAY: + value = deserializeArray(p, ctxt, nodeFactory); + break; + case JsonTokenId.ID_EMBEDDED_OBJECT: + value = _fromEmbedded(p, ctxt, nodeFactory); + break; + case JsonTokenId.ID_STRING: + value = nodeFactory.textNode(p.getText()); + break; + case JsonTokenId.ID_NUMBER_INT: + value = _fromInt(p, ctxt, nodeFactory); + break; + case JsonTokenId.ID_TRUE: + value = nodeFactory.booleanNode(true); + break; + case JsonTokenId.ID_FALSE: + value = nodeFactory.booleanNode(false); + break; + case JsonTokenId.ID_NULL: + value = nodeFactory.nullNode(); + break; + default: + value = deserializeAny(p, ctxt, nodeFactory); + } + JsonNode old = node.replace(key, value); + if (old != null) { + _handleDuplicateField(p, ctxt, nodeFactory, + key, node, old, value); + } + } + return node; + } + + protected final ArrayNode deserializeArray(JsonParser p, DeserializationContext ctxt, + final JsonNodeFactory nodeFactory) throws IOException + { + ArrayNode node = nodeFactory.arrayNode(); + while (true) { + JsonToken t = p.nextToken(); + if (t == null) { + throw ctxt.mappingException("Unexpected end-of-input when binding data into ArrayNode"); + } + switch (t.id()) { + case JsonTokenId.ID_START_OBJECT: + node.add(deserializeObject(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_START_ARRAY: + node.add(deserializeArray(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_END_ARRAY: + return node; + case JsonTokenId.ID_EMBEDDED_OBJECT: + node.add(_fromEmbedded(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_STRING: + node.add(nodeFactory.textNode(p.getText())); + break; + case JsonTokenId.ID_NUMBER_INT: + node.add(_fromInt(p, ctxt, nodeFactory)); + break; + case JsonTokenId.ID_TRUE: + node.add(nodeFactory.booleanNode(true)); + break; + case JsonTokenId.ID_FALSE: + node.add(nodeFactory.booleanNode(false)); + break; + case JsonTokenId.ID_NULL: + node.add(nodeFactory.nullNode()); + break; + default: + node.add(deserializeAny(p, ctxt, nodeFactory)); + break; + } + } + } + + protected final JsonNode deserializeAny(JsonParser p, DeserializationContext ctxt, + final JsonNodeFactory nodeFactory) throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_START_OBJECT: + case JsonTokenId.ID_END_OBJECT: // for empty JSON Objects we may point to this + case JsonTokenId.ID_FIELD_NAME: + return deserializeObject(p, ctxt, nodeFactory); + case JsonTokenId.ID_START_ARRAY: + return deserializeArray(p, ctxt, nodeFactory); + case JsonTokenId.ID_EMBEDDED_OBJECT: + return _fromEmbedded(p, ctxt, nodeFactory); + case JsonTokenId.ID_STRING: + return nodeFactory.textNode(p.getText()); + case JsonTokenId.ID_NUMBER_INT: + return _fromInt(p, ctxt, nodeFactory); + case JsonTokenId.ID_NUMBER_FLOAT: + return _fromFloat(p, ctxt, nodeFactory); + case JsonTokenId.ID_TRUE: + return nodeFactory.booleanNode(true); + case JsonTokenId.ID_FALSE: + return nodeFactory.booleanNode(false); + case JsonTokenId.ID_NULL: + return nodeFactory.nullNode(); + + // These states can not be mapped; input stream is + // off by an event or two + + //case END_OBJECT: + //case END_ARRAY: + default: + throw ctxt.mappingException(handledType()); + } + } + + protected final JsonNode _fromInt(JsonParser p, DeserializationContext ctxt, + JsonNodeFactory nodeFactory) throws IOException + { + JsonParser.NumberType nt; + int feats = ctxt.getDeserializationFeatures(); + if ((feats & F_MASK_INT_COERCIONS) != 0) { + if (DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.enabledIn(feats)) { + nt = JsonParser.NumberType.BIG_INTEGER; + } else if (DeserializationFeature.USE_LONG_FOR_INTS.enabledIn(feats)) { + nt = JsonParser.NumberType.LONG; + } else { + nt = p.getNumberType(); + } + } else { + nt = p.getNumberType(); + } + if (nt == JsonParser.NumberType.INT) { + return nodeFactory.numberNode(p.getIntValue()); + } + if (nt == JsonParser.NumberType.LONG) { + return nodeFactory.numberNode(p.getLongValue()); + } + return nodeFactory.numberNode(p.getBigIntegerValue()); + } + + protected final JsonNode _fromFloat(JsonParser p, DeserializationContext ctxt, + final JsonNodeFactory nodeFactory) throws IOException + { + JsonParser.NumberType nt = p.getNumberType(); + if (nt == JsonParser.NumberType.BIG_DECIMAL + || ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return nodeFactory.numberNode(p.getDecimalValue()); + } + return nodeFactory.numberNode(p.getDoubleValue()); + } + + protected final JsonNode _fromEmbedded(JsonParser p, DeserializationContext ctxt, + JsonNodeFactory nodeFactory) throws IOException + { + // [JACKSON-796] + Object ob = p.getEmbeddedObject(); + if (ob == null) { // should this occur? + return nodeFactory.nullNode(); + } + Class type = ob.getClass(); + if (type == byte[].class) { // most common special case + return nodeFactory.binaryNode((byte[]) ob); + } + // [databind#743]: Don't forget RawValue + if (ob instanceof RawValue) { + return nodeFactory.rawValueNode((RawValue) ob); + } + if (ob instanceof JsonNode) { + // [Issue#433]: but could also be a JsonNode hiding in there! + return (JsonNode) ob; + } + // any other special handling needed? + return nodeFactory.pojoNode(ob); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,677 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator; +import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer; +import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.ArrayBuilders; + +/** + * Basic serializer that can take JSON "Object" structure and + * construct a {@link java.util.Map} instance, with typed contents. + *

+ * Note: for untyped content (one indicated by passing Object.class + * as the type), {@link UntypedObjectDeserializer} is used instead. + * It can also construct {@link java.util.Map}s, but not with specific + * POJO types, only other containers and primitives/wrappers. + */ +@JacksonStdImpl +public class MapDeserializer + extends ContainerDeserializerBase> + implements ContextualDeserializer, ResolvableDeserializer +{ + private static final long serialVersionUID = 1L; + + // // Configuration: typing, deserializers + + protected final JavaType _mapType; + + /** + * Key deserializer to use; either passed via constructor + * (when indicated by annotations), or resolved when + * {@link #resolve} is called; + */ + protected final KeyDeserializer _keyDeserializer; + + /** + * Flag set to indicate that the key type is + * {@link java.lang.String} (or {@link java.lang.Object}, for + * which String is acceptable), and that the + * default Jackson key deserializer would be used. + * If both are true, can optimize handling. + */ + protected boolean _standardStringKey; + + /** + * Value deserializer. + */ + protected final JsonDeserializer _valueDeserializer; + + /** + * If value instances have polymorphic type information, this + * is the type deserializer that can handle it + */ + protected final TypeDeserializer _valueTypeDeserializer; + + // // Instance construction settings: + + protected final ValueInstantiator _valueInstantiator; + + protected final boolean _hasDefaultCreator; + + /** + * Deserializer that is used iff delegate-based creator is + * to be used for deserializing from JSON Object. + */ + protected JsonDeserializer _delegateDeserializer; + + /** + * If the Map is to be instantiated using non-default constructor + * or factory method + * that takes one or more named properties as argument(s), + * this creator is used for instantiation. + */ + protected PropertyBasedCreator _propertyBasedCreator; + + // // Any properties to ignore if seen? + + protected HashSet _ignorableProperties; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator, + KeyDeserializer keyDeser, JsonDeserializer valueDeser, + TypeDeserializer valueTypeDeser) + { + super(mapType); + _mapType = mapType; + _keyDeserializer = keyDeser; + _valueDeserializer = valueDeser; + _valueTypeDeserializer = valueTypeDeser; + _valueInstantiator = valueInstantiator; + _hasDefaultCreator = valueInstantiator.canCreateUsingDefault(); + _delegateDeserializer = null; + _propertyBasedCreator = null; + _standardStringKey = _isStdKeyDeser(mapType, keyDeser); + } + + /** + * Copy-constructor that can be used by sub-classes to allow + * copy-on-write styling copying of settings of an existing instance. + */ + protected MapDeserializer(MapDeserializer src) + { + super(src._mapType); + _mapType = src._mapType; + _keyDeserializer = src._keyDeserializer; + _valueDeserializer = src._valueDeserializer; + _valueTypeDeserializer = src._valueTypeDeserializer; + _valueInstantiator = src._valueInstantiator; + _propertyBasedCreator = src._propertyBasedCreator; + _delegateDeserializer = src._delegateDeserializer; + _hasDefaultCreator = src._hasDefaultCreator; + // should we make a copy here? + _ignorableProperties = src._ignorableProperties; + + _standardStringKey = src._standardStringKey; + } + + protected MapDeserializer(MapDeserializer src, + KeyDeserializer keyDeser, JsonDeserializer valueDeser, + TypeDeserializer valueTypeDeser, + HashSet ignorable) + { + super(src._mapType); + _mapType = src._mapType; + _keyDeserializer = keyDeser; + _valueDeserializer = valueDeser; + _valueTypeDeserializer = valueTypeDeser; + _valueInstantiator = src._valueInstantiator; + _propertyBasedCreator = src._propertyBasedCreator; + _delegateDeserializer = src._delegateDeserializer; + _hasDefaultCreator = src._hasDefaultCreator; + _ignorableProperties = ignorable; + + _standardStringKey = _isStdKeyDeser(_mapType, keyDeser); + } + + /** + * Fluent factory method used to create a copy with slightly + * different settings. When sub-classing, MUST be overridden. + */ + @SuppressWarnings("unchecked") + protected MapDeserializer withResolved(KeyDeserializer keyDeser, + TypeDeserializer valueTypeDeser, JsonDeserializer valueDeser, + HashSet ignorable) + { + + if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser) + && (_valueTypeDeserializer == valueTypeDeser) && (_ignorableProperties == ignorable)) { + return this; + } + return new MapDeserializer(this, + keyDeser, (JsonDeserializer) valueDeser, valueTypeDeser, ignorable); + } + + /** + * Helper method used to check whether we can just use the default key + * deserialization, where JSON String becomes Java String. + */ + protected final boolean _isStdKeyDeser(JavaType mapType, KeyDeserializer keyDeser) + { + if (keyDeser == null) { + return true; + } + JavaType keyType = mapType.getKeyType(); + if (keyType == null) { // assumed to be Object + return true; + } + Class rawKeyType = keyType.getRawClass(); + return ((rawKeyType == String.class || rawKeyType == Object.class) + && isDefaultKeyDeserializer(keyDeser)); + } + + public void setIgnorableProperties(String[] ignorable) { + _ignorableProperties = (ignorable == null || ignorable.length == 0) ? + null : ArrayBuilders.arrayToSet(ignorable); + } + + /* + /********************************************************** + /* Validation, post-processing (ResolvableDeserializer) + /********************************************************** + */ + + @Override + public void resolve(DeserializationContext ctxt) throws JsonMappingException + { + // May need to resolve types for delegate- and/or property-based creators: + if (_valueInstantiator.canCreateUsingDelegate()) { + JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig()); + if (delegateType == null) { + throw new IllegalArgumentException("Invalid delegate-creator definition for "+_mapType + +": value instantiator ("+_valueInstantiator.getClass().getName() + +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'"); + } + /* Theoretically should be able to get CreatorProperty for delegate + * parameter to pass; but things get tricky because DelegateCreator + * may contain injectable values. So, for now, let's pass nothing. + */ + _delegateDeserializer = findDeserializer(ctxt, delegateType, null); + } + if (_valueInstantiator.canCreateFromObjectWith()) { + SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig()); + _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps); + } + _standardStringKey = _isStdKeyDeser(_mapType, _keyDeserializer); + } + + /** + * Method called to finalize setup of this deserializer, + * when it is known for which property deserializer is needed for. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + KeyDeserializer kd = _keyDeserializer; + if (kd == null) { + kd = ctxt.findKeyDeserializer(_mapType.getKeyType(), property); + } else { + if (kd instanceof ContextualKeyDeserializer) { + kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property); + } + } + JsonDeserializer vd = _valueDeserializer; + // #125: May have a content converter + if (property != null) { + vd = findConvertingContentDeserializer(ctxt, property, vd); + } + final JavaType vt = _mapType.getContentType(); + if (vd == null) { + vd = ctxt.findContextualValueDeserializer(vt, property); + } else { // if directly assigned, probably not yet contextual, so: + vd = ctxt.handleSecondaryContextualization(vd, property, vt); + } + TypeDeserializer vtd = _valueTypeDeserializer; + if (vtd != null) { + vtd = vtd.forProperty(property); + } + HashSet ignored = _ignorableProperties; + AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + if (intr != null && property != null) { + AnnotatedMember member = property.getMember(); + if (member != null) { + String[] moreToIgnore = intr.findPropertiesToIgnore(member, false); + if (moreToIgnore != null) { + ignored = (ignored == null) ? new HashSet() : new HashSet(ignored); + for (String str : moreToIgnore) { + ignored.add(str); + } + } + } + } + return withResolved(kd, vtd, vd, ignored); + } + + /* + /********************************************************** + /* ContainerDeserializerBase API + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _mapType.getContentType(); + } + + @Override + public JsonDeserializer getContentDeserializer() { + return _valueDeserializer; + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + /** + * Turns out that these are expensive enough to create so that caching + * does make sense. + *

+ * IMPORTANT: but, note, that instances CAN NOT BE CACHED if there is + * a value type deserializer; this caused an issue with 2.4.4 of + * JAXB Annotations (failing a test). + * It is also possible that some other settings could make deserializers + * un-cacheable; but on the other hand, caching can make a big positive + * difference with performance... so it's a hard choice. + * + * @since 2.4.4 + */ + @Override + public boolean isCachable() { + /* As per [databind#735], existence of value or key deserializer (only passed + * if annotated to use non-standard one) should also prevent caching. + */ + return (_valueDeserializer == null) + && (_keyDeserializer == null) + && (_valueTypeDeserializer == null) + && (_ignorableProperties == null); + } + + @Override + @SuppressWarnings("unchecked") + public Map deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (_propertyBasedCreator != null) { + return _deserializeUsingCreator(p, ctxt); + } + if (_delegateDeserializer != null) { + return (Map) _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + if (!_hasDefaultCreator) { + throw ctxt.instantiationException(getMapClass(), "No default constructor found"); + } + // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT + JsonToken t = p.getCurrentToken(); + if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) { + // [JACKSON-620] (empty) String may be ok however: + if (t == JsonToken.VALUE_STRING) { + return (Map) _valueInstantiator.createFromString(ctxt, p.getText()); + } + // slightly redundant (since String was passed above), but + return _deserializeFromEmpty(p, ctxt); + } + final Map result = (Map) _valueInstantiator.createUsingDefault(ctxt); + if (_standardStringKey) { + _readAndBindStringMap(p, ctxt, result); + return result; + } + _readAndBind(p, ctxt, result); + return result; + } + + @Override + public Map deserialize(JsonParser p, DeserializationContext ctxt, + Map result) + throws IOException + { + // [databind#631]: Assign current value, to be accessible by custom serializers + p.setCurrentValue(result); + + // Ok: must point to START_OBJECT or FIELD_NAME + JsonToken t = p.getCurrentToken(); + if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) { + throw ctxt.mappingException(getMapClass()); + } + if (_standardStringKey) { + _readAndBindStringMap(p, ctxt, result); + return result; + } + _readAndBind(p, ctxt, result); + return result; + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException, JsonProcessingException + { + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromObject(jp, ctxt); + } + + /* + /********************************************************** + /* Other public accessors + /********************************************************** + */ + + @SuppressWarnings("unchecked") + public final Class getMapClass() { return (Class>) _mapType.getRawClass(); } + + @Override public JavaType getValueType() { return _mapType; } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected final void _readAndBind(JsonParser p, DeserializationContext ctxt, + Map result) throws IOException + { + final KeyDeserializer keyDes = _keyDeserializer; + final JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + + MapReferringAccumulator referringAccumulator = null; + boolean useObjectId = valueDes.getObjectIdReader() != null; + if (useObjectId) { + referringAccumulator = new MapReferringAccumulator(_mapType.getContentType().getRawClass(), result); + } + + String keyStr; + if (p.isExpectedStartObjectToken()) { + keyStr = p.nextFieldName(); + } else { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_OBJECT) { + return; + } + if (t != JsonToken.FIELD_NAME) { + throw ctxt.mappingException(_mapType.getRawClass(), p.getCurrentToken()); + } + keyStr = p.getCurrentName(); + } + + for (; keyStr != null; keyStr = p.nextFieldName()) { + Object key = keyDes.deserializeKey(keyStr, ctxt); + // And then the value... + JsonToken t = p.nextToken(); + if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) { + p.skipChildren(); + continue; + } + try { + // Note: must handle null explicitly here; value deserializers won't + Object value; + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(p, ctxt); + } else { + value = valueDes.deserializeWithType(p, ctxt, typeDeser); + } + if (useObjectId) { + referringAccumulator.put(key, value); + } else { + result.put(key, value); + } + } catch (UnresolvedForwardReference reference) { + handleUnresolvedReference(p, referringAccumulator, key, reference); + } catch (Exception e) { + wrapAndThrow(e, result, keyStr); + } + } + } + + /** + * Optimized method used when keys can be deserialized as plain old + * {@link java.lang.String}s, and there is no custom deserialized + * specified. + */ + protected final void _readAndBindStringMap(JsonParser p, DeserializationContext ctxt, + Map result) throws IOException + { + final JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + MapReferringAccumulator referringAccumulator = null; + boolean useObjectId = (valueDes.getObjectIdReader() != null); + if (useObjectId) { + referringAccumulator = new MapReferringAccumulator(_mapType.getContentType().getRawClass(), result); + } + + String key; + if (p.isExpectedStartObjectToken()) { + key = p.nextFieldName(); + } else { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_OBJECT) { + return; + } + if (t != JsonToken.FIELD_NAME) { + throw ctxt.mappingException(_mapType.getRawClass(), p.getCurrentToken()); + } + key = p.getCurrentName(); + } + + for (; key != null; key = p.nextFieldName()) { + JsonToken t = p.nextToken(); + if (_ignorableProperties != null && _ignorableProperties.contains(key)) { + p.skipChildren(); + continue; + } + try { + // Note: must handle null explicitly here; value deserializers won't + Object value; + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(p, ctxt); + } else { + value = valueDes.deserializeWithType(p, ctxt, typeDeser); + } + if (useObjectId) { + referringAccumulator.put(key, value); + } else { + result.put(key, value); + } + } catch (UnresolvedForwardReference reference) { + handleUnresolvedReference(p, referringAccumulator, key, reference); + } catch (Exception e) { + wrapAndThrow(e, result, key); + } + } + // 23-Mar-2015, tatu: TODO: verify we got END_OBJECT? + } + + @SuppressWarnings("unchecked") + public Map _deserializeUsingCreator(JsonParser p, DeserializationContext ctxt) throws IOException + { + final PropertyBasedCreator creator = _propertyBasedCreator; + // null -> no ObjectIdReader for Maps (yet?) + PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null); + + final JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + + String key; + if (p.isExpectedStartObjectToken()) { + key = p.nextFieldName(); + } else if (p.hasToken(JsonToken.FIELD_NAME)) { + key = p.getCurrentName(); + } else { + key = null; + } + + for (; key != null; key = p.nextFieldName()) { + JsonToken t = p.nextToken(); // to get to value + if (_ignorableProperties != null && _ignorableProperties.contains(key)) { + p.skipChildren(); // and skip it (in case of array/object) + continue; + } + // creator property? + SettableBeanProperty prop = creator.findCreatorProperty(key); + if (prop != null) { + // Last property to set? + if (buffer.assignParameter(prop, prop.deserialize(p, ctxt))) { + p.nextToken(); + Map result; + try { + result = (Map)creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _mapType.getRawClass(), key); + return null; + } + _readAndBind(p, ctxt, result); + return result; + } + continue; + } + // other property? needs buffering + Object actualKey = _keyDeserializer.deserializeKey(key, ctxt); + Object value; + + try { + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(p, ctxt); + } else { + value = valueDes.deserializeWithType(p, ctxt, typeDeser); + } + } catch (Exception e) { + wrapAndThrow(e, _mapType.getRawClass(), key); + return null; + } + buffer.bufferMapProperty(actualKey, value); + } + // end of JSON object? + // if so, can just construct and leave... + try { + return (Map)creator.build(ctxt, buffer); + } catch (Exception e) { + wrapAndThrow(e, _mapType.getRawClass(), key); + return null; + } + } + + @Deprecated // since 2.5 + protected void wrapAndThrow(Throwable t, Object ref) throws IOException { + wrapAndThrow(t, ref, null); + } + + private void handleUnresolvedReference(JsonParser jp, MapReferringAccumulator accumulator, + Object key, UnresolvedForwardReference reference) + throws JsonMappingException + { + if (accumulator == null) { + throw JsonMappingException.from(jp, "Unresolved forward reference but no identity info.", reference); + } + Referring referring = accumulator.handleUnresolvedReference(reference, key); + reference.getRoid().appendReferring(referring); + } + + private final static class MapReferringAccumulator { + private final Class _valueType; + private Map _result; + /** + * A list of {@link MapReferring} to maintain ordering. + */ + private List _accumulator = new ArrayList(); + + public MapReferringAccumulator(Class valueType, Map result) { + _valueType = valueType; + _result = result; + } + + public void put(Object key, Object value) + { + if (_accumulator.isEmpty()) { + _result.put(key, value); + } else { + MapReferring ref = _accumulator.get(_accumulator.size() - 1); + ref.next.put(key, value); + } + } + + public Referring handleUnresolvedReference(UnresolvedForwardReference reference, Object key) + { + MapReferring id = new MapReferring(this, reference, _valueType, key); + _accumulator.add(id); + return id; + } + + public void resolveForwardReference(Object id, Object value) throws IOException + { + Iterator iterator = _accumulator.iterator(); + // Resolve ordering after resolution of an id. This means either: + // 1- adding to the result map in case of the first unresolved id. + // 2- merge the content of the resolved id with its previous unresolved id. + Map previous = _result; + while (iterator.hasNext()) { + MapReferring ref = iterator.next(); + if (ref.hasId(id)) { + iterator.remove(); + previous.put(ref.key, value); + previous.putAll(ref.next); + return; + } + previous = ref.next; + } + + throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id + + "] that wasn't previously seen as unresolved."); + } + } + + /** + * Helper class to maintain processing order of value. + * The resolved object associated with {@link #key} comes before the values in + * {@link #next}. + */ + final static class MapReferring extends Referring { + private final MapReferringAccumulator _parent; + + public final Map next = new LinkedHashMap(); + public final Object key; + + MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref, + Class valueType, Object key) + { + super(ref, valueType); + _parent = parent; + this.key = key; + } + + @Override + public void handleResolvedForwardReference(Object id, Object value) throws IOException { + _parent.resolveForwardReference(id, value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,247 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Basic serializer that can take JSON "Object" structure and + * construct a {@link java.util.Map} instance, with typed contents. + *

+ * Note: for untyped content (one indicated by passing Object.class + * as the type), {@link UntypedObjectDeserializer} is used instead. + * It can also construct {@link java.util.Map}s, but not with specific + * POJO types, only other containers and primitives/wrappers. + */ +@JacksonStdImpl +public class MapEntryDeserializer + extends ContainerDeserializerBase> + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1; + + // // Configuration: typing, deserializers + + protected final JavaType _type; + + /** + * Key deserializer to use; either passed via constructor + * (when indicated by annotations), or resolved when + * {@link #createContextual} is called; + */ + protected final KeyDeserializer _keyDeserializer; + + /** + * Value deserializer. + */ + protected final JsonDeserializer _valueDeserializer; + + /** + * If value instances have polymorphic type information, this + * is the type deserializer that can handle it + */ + protected final TypeDeserializer _valueTypeDeserializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public MapEntryDeserializer(JavaType type, + KeyDeserializer keyDeser, JsonDeserializer valueDeser, + TypeDeserializer valueTypeDeser) + { + super(type); + if (type.containedTypeCount() != 2) { // sanity check + throw new IllegalArgumentException("Missing generic type information for "+type); + } + _type = type; + _keyDeserializer = keyDeser; + _valueDeserializer = valueDeser; + _valueTypeDeserializer = valueTypeDeser; + } + + /** + * Copy-constructor that can be used by sub-classes to allow + * copy-on-write styling copying of settings of an existing instance. + */ + protected MapEntryDeserializer(MapEntryDeserializer src) + { + super(src._type); + _type = src._type; + _keyDeserializer = src._keyDeserializer; + _valueDeserializer = src._valueDeserializer; + _valueTypeDeserializer = src._valueTypeDeserializer; + } + + protected MapEntryDeserializer(MapEntryDeserializer src, + KeyDeserializer keyDeser, JsonDeserializer valueDeser, + TypeDeserializer valueTypeDeser) + { + super(src._type); + _type = src._type; + _keyDeserializer = keyDeser; + _valueDeserializer = valueDeser; + _valueTypeDeserializer = valueTypeDeser; + } + + /** + * Fluent factory method used to create a copy with slightly + * different settings. When sub-classing, MUST be overridden. + */ + @SuppressWarnings("unchecked") + protected MapEntryDeserializer withResolved(KeyDeserializer keyDeser, + TypeDeserializer valueTypeDeser, JsonDeserializer valueDeser) + { + + if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser) + && (_valueTypeDeserializer == valueTypeDeser)) { + return this; + } + return new MapEntryDeserializer(this, + keyDeser, (JsonDeserializer) valueDeser, valueTypeDeser); + } + + /* + /********************************************************** + /* Validation, post-processing (ResolvableDeserializer) + /********************************************************** + */ + + /** + * Method called to finalize setup of this deserializer, + * when it is known for which property deserializer is needed for. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + KeyDeserializer kd = _keyDeserializer; + if (kd == null) { + kd = ctxt.findKeyDeserializer(_type.containedType(0), property); + } else { + if (kd instanceof ContextualKeyDeserializer) { + kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property); + } + } + JsonDeserializer vd = _valueDeserializer; + vd = findConvertingContentDeserializer(ctxt, property, vd); + JavaType contentType = _type.containedType(1); + if (vd == null) { + vd = ctxt.findContextualValueDeserializer(contentType, property); + } else { // if directly assigned, probably not yet contextual, so: + vd = ctxt.handleSecondaryContextualization(vd, property, contentType); + } + TypeDeserializer vtd = _valueTypeDeserializer; + if (vtd != null) { + vtd = vtd.forProperty(property); + } + return withResolved(kd, vtd, vd); + } + + /* + /********************************************************** + /* ContainerDeserializerBase API + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _type.containedType(1); + } + + @Override + public JsonDeserializer getContentDeserializer() { + return _valueDeserializer; + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + @Override + public Map.Entry deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + { + // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT + JsonToken t = jp.getCurrentToken(); + if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) { + // [JACKSON-620] (empty) String may be ok however: + // slightly redundant (since String was passed above), but + return _deserializeFromEmpty(jp, ctxt); + } + if (t == JsonToken.START_OBJECT) { + t = jp.nextToken(); + } + if (t != JsonToken.FIELD_NAME) { + if (t == JsonToken.END_OBJECT) { + throw ctxt.mappingException("Can not deserialize a Map.Entry out of empty JSON Object"); + } + throw ctxt.mappingException(handledType(), t); + } + + final KeyDeserializer keyDes = _keyDeserializer; + final JsonDeserializer valueDes = _valueDeserializer; + final TypeDeserializer typeDeser = _valueTypeDeserializer; + + final String keyStr = jp.getCurrentName(); + Object key = keyDes.deserializeKey(keyStr, ctxt); + Object value = null; + // And then the value... + t = jp.nextToken(); + try { + // Note: must handle null explicitly here; value deserializers won't + if (t == JsonToken.VALUE_NULL) { + value = valueDes.getNullValue(ctxt); + } else if (typeDeser == null) { + value = valueDes.deserialize(jp, ctxt); + } else { + value = valueDes.deserializeWithType(jp, ctxt, typeDeser); + } + } catch (Exception e) { + wrapAndThrow(e, Map.Entry.class, keyStr); + } + + // Close, but also verify that we reached the END_OBJECT + t = jp.nextToken(); + if (t != JsonToken.END_OBJECT) { + if (t == JsonToken.FIELD_NAME) { // most likely + throw ctxt.mappingException("Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '"+jp.getCurrentName()+"')"); + } + // how would this occur? + throw ctxt.mappingException("Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t); + } + return new AbstractMap.SimpleEntry(key, value); + } + + @Override + public Map.Entry deserialize(JsonParser jp, DeserializationContext ctxt, + Map.Entry result) throws IOException + { + throw new IllegalStateException("Can not update Map.Entry values"); + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException, JsonProcessingException + { + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromObject(jp, ctxt); + } + + /* + /********************************************************** + /* Other public accessors + /********************************************************** + */ + + @Override public JavaType getValueType() { return _type; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,64 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Bogus deserializer that will simply skip all content there is to map + * and returns Java null reference. + * + * @since 2.2 + */ +public class NullifyingDeserializer + extends StdDeserializer +{ + private static final long serialVersionUID = 1L; + + public final static NullifyingDeserializer instance = new NullifyingDeserializer(); + + public NullifyingDeserializer() { super(Object.class); } + + /* + /********************************************************** + /* Deserializer API + /********************************************************** + */ + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // 29-Jan-2016, tatu: Simple skipping for all other tokens, but FIELD_NAME bit + // special unfortunately + if (p.hasToken(JsonToken.FIELD_NAME)) { + while (true) { + JsonToken t = p.nextToken(); + if ((t == null) || (t == JsonToken.END_OBJECT)) { + break; + } + p.skipChildren(); + } + } else { + p.skipChildren(); + } + return null; + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) throws IOException + { + // Not sure if we need to bother but: + + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_START_ARRAY: + case JsonTokenId.ID_START_OBJECT: + case JsonTokenId.ID_FIELD_NAME: + return typeDeserializer.deserializeTypedFromAny(p, ctxt); + default: + return null; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,631 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashSet; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Container class for deserializers that handle core JDK primitive + * (and matching wrapper) types, as well as standard "big" numeric types. + * Note that this includes types such as {@link java.lang.Boolean} + * and {@link java.lang.Character} which are not strictly numeric, + * but are part of primitive/wrapper types. + */ +public class NumberDeserializers +{ + private final static HashSet _classNames = new HashSet(); + static { + // note: can skip primitive types; other ways to check them: + Class[] numberTypes = new Class[] { + Boolean.class, + Byte.class, + Short.class, + Character.class, + Integer.class, + Long.class, + Float.class, + Double.class, + // and more generic ones + Number.class, BigDecimal.class, BigInteger.class + }; + for (Class cls : numberTypes) { + _classNames.add(cls.getName()); + } + } + + public static JsonDeserializer find(Class rawType, String clsName) { + if (rawType.isPrimitive()) { + if (rawType == Integer.TYPE) { + return IntegerDeserializer.primitiveInstance; + } + if (rawType == Boolean.TYPE) { + return BooleanDeserializer.primitiveInstance; + } + if (rawType == Long.TYPE) { + return LongDeserializer.primitiveInstance; + } + if (rawType == Double.TYPE) { + return DoubleDeserializer.primitiveInstance; + } + if (rawType == Character.TYPE) { + return CharacterDeserializer.primitiveInstance; + } + if (rawType == Byte.TYPE) { + return ByteDeserializer.primitiveInstance; + } + if (rawType == Short.TYPE) { + return ShortDeserializer.primitiveInstance; + } + if (rawType == Float.TYPE) { + return FloatDeserializer.primitiveInstance; + } + } else if (_classNames.contains(clsName)) { + // Start with most common types; int, boolean, long, double + if (rawType == Integer.class) { + return IntegerDeserializer.wrapperInstance; + } + if (rawType == Boolean.class) { + return BooleanDeserializer.wrapperInstance; + } + if (rawType == Long.class) { + return LongDeserializer.wrapperInstance; + } + if (rawType == Double.class) { + return DoubleDeserializer.wrapperInstance; + } + if (rawType == Character.class) { + return CharacterDeserializer.wrapperInstance; + } + if (rawType == Byte.class) { + return ByteDeserializer.wrapperInstance; + } + if (rawType == Short.class) { + return ShortDeserializer.wrapperInstance; + } + if (rawType == Float.class) { + return FloatDeserializer.wrapperInstance; + } + if (rawType == Number.class) { + return NumberDeserializer.instance; + } + if (rawType == BigDecimal.class) { + return BigDecimalDeserializer.instance; + } + if (rawType == BigInteger.class) { + return BigIntegerDeserializer.instance; + } + } else { + return null; + } + // should never occur + throw new IllegalArgumentException("Internal error: can't find deserializer for "+rawType.getName()); + } + + /* + /********************************************************** + /* Then one intermediate base class for things that have + /* both primitive and wrapper types + /********************************************************** + */ + + protected abstract static class PrimitiveOrWrapperDeserializer + extends StdScalarDeserializer + { + private static final long serialVersionUID = 1L; + + protected final T _nullValue; + protected final boolean _primitive; + + protected PrimitiveOrWrapperDeserializer(Class vc, T nvl) { + super(vc); + _nullValue = nvl; + _primitive = vc.isPrimitive(); + } + + @Override + public final T getNullValue(DeserializationContext ctxt) throws JsonMappingException + { + if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { + throw ctxt.mappingException( + "Can not map JSON null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)", + handledType().toString()); + } + return _nullValue; + } + + @Override + @Deprecated // remove in 2.7 + public final T getNullValue() { + return _nullValue; + } + + @Override + public T getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { + // [databind#1095]: Should not allow coercion from into null from Empty String + // either, if `null` not allowed + if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { + throw ctxt.mappingException( + "Can not map Empty String as null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)", + handledType().toString()); + } + return _nullValue; + } + } + + /* + /********************************************************** + /* Then primitive/wrapper types + /********************************************************** + */ + + @JacksonStdImpl + public final static class BooleanDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static BooleanDeserializer primitiveInstance = new BooleanDeserializer(Boolean.TYPE, Boolean.FALSE); + final static BooleanDeserializer wrapperInstance = new BooleanDeserializer(Boolean.class, null); + + public BooleanDeserializer(Class cls, Boolean nvl) + { + super(cls, nvl); + } + + @Override + public Boolean deserialize(JsonParser j, DeserializationContext ctxt) throws IOException + { + return _parseBoolean(j, ctxt); + } + + // Since we can never have type info ("natural type"; String, Boolean, Integer, Double): + // (is it an error to even call this version?) + @Override + public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + return _parseBoolean(p, ctxt); + } + } + + @JacksonStdImpl + public static class ByteDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static ByteDeserializer primitiveInstance = new ByteDeserializer(Byte.TYPE, (byte) 0); + final static ByteDeserializer wrapperInstance = new ByteDeserializer(Byte.class, null); + + public ByteDeserializer(Class cls, Byte nvl) + { + super(cls, nvl); + } + + @Override + public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + return _parseByte(p, ctxt); + } + } + + @JacksonStdImpl + public static class ShortDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static ShortDeserializer primitiveInstance = new ShortDeserializer(Short.TYPE, Short.valueOf((short)0)); + final static ShortDeserializer wrapperInstance = new ShortDeserializer(Short.class, null); + + public ShortDeserializer(Class cls, Short nvl) + { + super(cls, nvl); + } + + @Override + public Short deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + return _parseShort(jp, ctxt); + } + } + + @JacksonStdImpl + public static class CharacterDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static CharacterDeserializer primitiveInstance = new CharacterDeserializer(Character.TYPE, '\0'); + final static CharacterDeserializer wrapperInstance = new CharacterDeserializer(Character.class, null); + + public CharacterDeserializer(Class cls, Character nvl) + { + super(cls, nvl); + } + + @Override + public Character deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value + int value = p.getIntValue(); + if (value >= 0 && value <= 0xFFFF) { + return Character.valueOf((char) value); + } + break; + case JsonTokenId.ID_STRING: // this is the usual type + // But does it have to be exactly one char? + String text = p.getText(); + if (text.length() == 1) { + return Character.valueOf(text.charAt(0)); + } + // actually, empty should become null? + if (text.length() == 0) { + return (Character) getEmptyValue(ctxt); + } + break; + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Character C = deserialize(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array" + ); + } + return C; + } + } + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + } + + @JacksonStdImpl + public final static class IntegerDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.TYPE, Integer.valueOf(0)); + final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.class, null); + + public IntegerDeserializer(Class cls, Integer nvl) { + super(cls, nvl); + } + + // since 2.6, slightly faster lookups for this very common type + @Override + public boolean isCachable() { return true; } + + @Override + public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return p.getIntValue(); + } + return _parseInteger(p, ctxt); + } + + // Since we can never have type info ("natural type"; String, Boolean, Integer, Double): + // (is it an error to even call this version?) + @Override + public Integer deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) throws IOException + { + if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return p.getIntValue(); + } + return _parseInteger(p, ctxt); + } + } + + @JacksonStdImpl + public final static class LongDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static LongDeserializer primitiveInstance = new LongDeserializer(Long.TYPE, Long.valueOf(0L)); + final static LongDeserializer wrapperInstance = new LongDeserializer(Long.class, null); + + public LongDeserializer(Class cls, Long nvl) { + super(cls, nvl); + } + + // since 2.6, slightly faster lookups for this very common type + @Override + public boolean isCachable() { return true; } + + @Override + public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return p.getLongValue(); + } + return _parseLong(p, ctxt); + } + } + + @JacksonStdImpl + public static class FloatDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static FloatDeserializer primitiveInstance = new FloatDeserializer(Float.TYPE, 0.f); + final static FloatDeserializer wrapperInstance = new FloatDeserializer(Float.class, null); + + public FloatDeserializer(Class cls, Float nvl) { + super(cls, nvl); + } + + @Override + public Float deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + return _parseFloat(p, ctxt); + } + } + + @JacksonStdImpl + public static class DoubleDeserializer + extends PrimitiveOrWrapperDeserializer + { + private static final long serialVersionUID = 1L; + + final static DoubleDeserializer primitiveInstance = new DoubleDeserializer(Double.TYPE, 0.d); + final static DoubleDeserializer wrapperInstance = new DoubleDeserializer(Double.class, null); + + public DoubleDeserializer(Class cls, Double nvl) { + super(cls, nvl); + } + + @Override + public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _parseDouble(jp, ctxt); + } + + // Since we can never have type info ("natural type"; String, Boolean, Integer, Double): + // (is it an error to even call this version?) + @Override + public Double deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) throws IOException + { + return _parseDouble(jp, ctxt); + } + } + + /** + * For type Number.class, we can just rely on type + * mappings that plain {@link JsonParser#getNumberValue} returns. + *

+ * There is one additional complication: some numeric + * types (specifically, int/Integer and double/Double) are "non-typed"; + * meaning that they will NEVER be output with type information. + * But other numeric types may need such type information. + * This is why {@link #deserializeWithType} must be overridden. + */ + @SuppressWarnings("serial") + @JacksonStdImpl + public static class NumberDeserializer + extends StdScalarDeserializer + { + public final static NumberDeserializer instance = new NumberDeserializer(); + + public NumberDeserializer() { + super(Number.class); + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_INT: + if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { + return _coerceIntegral(p, ctxt); + } + return p.getNumberValue(); + + case JsonTokenId.ID_NUMBER_FLOAT: + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return p.getDecimalValue(); + } + return Double.valueOf(p.getDoubleValue()); + + case JsonTokenId.ID_STRING: + /* Textual values are more difficult... not parsing itself, but figuring + * out 'minimal' type to use + */ + String text = p.getText().trim(); + if (text.length() == 0) { + return getEmptyValue(ctxt); + } + if (_hasTextualNull(text)) { + return getNullValue(ctxt); + } + if (_isPosInf(text)) { + return Double.POSITIVE_INFINITY; + } + if (_isNegInf(text)) { + return Double.NEGATIVE_INFINITY; + } + if (_isNaN(text)) { + return Double.NaN; + } + try { + if (!_isIntNumber(text)) { + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return new BigDecimal(text); + } + return new Double(text); + } + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) { + return new BigInteger(text); + } + long value = Long.parseLong(text); + if (!ctxt.isEnabled(DeserializationFeature.USE_LONG_FOR_INTS)) { + if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) { + return Integer.valueOf((int) value); + } + } + return Long.valueOf(value); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid number"); + } + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Object value = deserialize(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single '" + _valueClass.getName() + "' value but there was more than a single value in the array" + ); + } + return value; + } + break; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + + /** + * As mentioned in class Javadoc, there is additional complexity in + * handling potentially mixed type information here. Because of this, + * we must actually check for "raw" integers and doubles first, before + * calling type deserializer. + */ + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + switch (jp.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_INT: + case JsonTokenId.ID_NUMBER_FLOAT: + case JsonTokenId.ID_STRING: + // can not point to type information: hence must be non-typed (int/double) + return deserialize(jp, ctxt); + } + return typeDeserializer.deserializeTypedFromScalar(jp, ctxt); + } + } + + /* + /********************************************************** + /* And then bit more complicated (but non-structured) number + /* types + /********************************************************** + */ + + /** + * This is bit trickier to implement efficiently, while avoiding + * overflow problems. + */ + @SuppressWarnings("serial") + @JacksonStdImpl + public static class BigIntegerDeserializer + extends StdScalarDeserializer + { + public final static BigIntegerDeserializer instance = new BigIntegerDeserializer(); + + public BigIntegerDeserializer() { super(BigInteger.class); } + + @SuppressWarnings("incomplete-switch") + @Override + public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_INT: + switch (p.getNumberType()) { + case INT: + case LONG: + case BIG_INTEGER: + return p.getBigIntegerValue(); + } + break; + case JsonTokenId.ID_NUMBER_FLOAT: + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "java.math.BigInteger"); + } + return p.getDecimalValue().toBigInteger(); + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final BigInteger value = deserialize(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'BigInteger' value but there was more than a single value in the array" + ); + } + return value; + } + break; + case JsonTokenId.ID_STRING: // let's do implicit re-parse + String text = p.getText().trim(); + if (text.length() == 0) { + return null; + } + try { + return new BigInteger(text); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid representation"); + } + } + // String is ok too, can easily convert; otherwise, no can do: + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + } + + @SuppressWarnings("serial") + @JacksonStdImpl + public static class BigDecimalDeserializer + extends StdScalarDeserializer + { + public final static BigDecimalDeserializer instance = new BigDecimalDeserializer(); + + public BigDecimalDeserializer() { super(BigDecimal.class); } + + @Override + public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_INT: + case JsonTokenId.ID_NUMBER_FLOAT: + return p.getDecimalValue(); + case JsonTokenId.ID_STRING: + String text = p.getText().trim(); + if (text.length() == 0) { + return null; + } + try { + return new BigDecimal(text); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid representation"); + } + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final BigDecimal value = deserialize(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'BigDecimal' value but there was more than a single value in the array" + ); + } + return value; + } + break; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,298 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.lang.reflect.Array; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.type.ArrayType; +import com.fasterxml.jackson.databind.util.ObjectBuffer; + +/** + * Basic serializer that can serialize non-primitive arrays. + */ +@JacksonStdImpl +public class ObjectArrayDeserializer + extends ContainerDeserializerBase + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1L; + + // // Configuration + + /** + * Full generic type of the array being deserialized + */ + protected final ArrayType _arrayType; + + /** + * Flag that indicates whether the component type is Object or not. + * Used for minor optimization when constructing result. + */ + protected final boolean _untyped; + + /** + * Type of contained elements: needed for constructing actual + * result array + */ + protected final Class _elementClass; + + /** + * Element deserializer + */ + protected JsonDeserializer _elementDeserializer; + + /** + * If element instances have polymorphic type information, this + * is the type deserializer that can handle it + */ + protected final TypeDeserializer _elementTypeDeserializer; + + /** + * Specific override for this instance (from proper, or global per-type overrides) + * to indicate whether single value may be taken to mean an unwrapped one-element array + * or not. If null, left to global defaults. + * + * @since 2.7 + */ + protected final Boolean _unwrapSingle; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public ObjectArrayDeserializer(ArrayType arrayType, + JsonDeserializer elemDeser, TypeDeserializer elemTypeDeser) + { + super(arrayType); + _arrayType = arrayType; + _elementClass = arrayType.getContentType().getRawClass(); + _untyped = (_elementClass == Object.class); + _elementDeserializer = elemDeser; + _elementTypeDeserializer = elemTypeDeser; + _unwrapSingle = null; + } + + protected ObjectArrayDeserializer(ObjectArrayDeserializer base, + JsonDeserializer elemDeser, TypeDeserializer elemTypeDeser, + Boolean unwrapSingle) + { + super(base._arrayType); + _arrayType = base._arrayType; + _elementClass = base._elementClass; + _untyped = base._untyped; + + _elementDeserializer = elemDeser; + _elementTypeDeserializer = elemTypeDeser; + _unwrapSingle = unwrapSingle; + } + + /** + * Overridable fluent-factory method used to create contextual instances + */ + public ObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser, + JsonDeserializer elemDeser) + { + return withResolved(elemTypeDeser, elemDeser, _unwrapSingle); + } + + /** + * @since 2.7 + */ + @SuppressWarnings("unchecked") + public ObjectArrayDeserializer withResolved(TypeDeserializer elemTypeDeser, + JsonDeserializer elemDeser, Boolean unwrapSingle) + { + if ((unwrapSingle == _unwrapSingle) + && (elemDeser == _elementDeserializer) + && (elemTypeDeser == _elementTypeDeserializer)) { + return this; + } + return new ObjectArrayDeserializer(this, + (JsonDeserializer) elemDeser, elemTypeDeser, unwrapSingle); + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + JsonDeserializer deser = _elementDeserializer; + Boolean unwrapSingle = findFormatFeature(ctxt, property, _arrayType.getRawClass(), + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + // May have a content converter + deser = findConvertingContentDeserializer(ctxt, property, deser); + final JavaType vt = _arrayType.getContentType(); + if (deser == null) { + deser = ctxt.findContextualValueDeserializer(vt, property); + } else { // if directly assigned, probably not yet contextual, so: + deser = ctxt.handleSecondaryContextualization(deser, property, vt); + } + TypeDeserializer elemTypeDeser = _elementTypeDeserializer; + if (elemTypeDeser != null) { + elemTypeDeser = elemTypeDeser.forProperty(property); + } + return withResolved(elemTypeDeser, deser, unwrapSingle); + } + + @Override // since 2.5 + public boolean isCachable() { + // Important: do NOT cache if polymorphic values, or ones with custom deserializer + return (_elementDeserializer == null) && (_elementTypeDeserializer == null); + } + + /* + /********************************************************** + /* ContainerDeserializerBase API + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _arrayType.getContentType(); + } + + @Override + public JsonDeserializer getContentDeserializer() { + return _elementDeserializer; + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + @Override + public Object[] deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // Ok: must point to START_ARRAY (or equivalent) + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + + final ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] chunk = buffer.resetAndStart(); + int ix = 0; + JsonToken t; + final TypeDeserializer typeDeser = _elementTypeDeserializer; + + try { + while ((t = p.nextToken()) != JsonToken.END_ARRAY) { + // Note: must handle null explicitly here; value deserializers won't + Object value; + + if (t == JsonToken.VALUE_NULL) { + value = _elementDeserializer.getNullValue(ctxt); + } else if (typeDeser == null) { + value = _elementDeserializer.deserialize(p, ctxt); + } else { + value = _elementDeserializer.deserializeWithType(p, ctxt, typeDeser); + } + if (ix >= chunk.length) { + chunk = buffer.appendCompletedChunk(chunk); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix); + } + + Object[] result; + + if (_untyped) { + result = buffer.completeAndClearBuffer(chunk, ix); + } else { + result = buffer.completeAndClearBuffer(chunk, ix, _elementClass); + } + ctxt.returnObjectBuffer(buffer); + return result; + } + + @Override + public Object[] deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) + throws IOException + { + /* Should there be separate handling for base64 stuff? + * for now this should be enough: + */ + return (Object[]) typeDeserializer.deserializeTypedFromArray(p, ctxt); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected Byte[] deserializeFromBase64(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // First same as what PrimitiveArrayDeserializers.ByteDeser does: + byte[] b = p.getBinaryValue(ctxt.getBase64Variant()); + // But then need to convert to wrappers + Byte[] result = new Byte[b.length]; + for (int i = 0, len = b.length; i < len; ++i) { + result[i] = Byte.valueOf(b[i]); + } + return result; + } + + protected Object[] handleNonArray(JsonParser p, DeserializationContext ctxt) + throws IOException + { + // Empty String can become null... + if (p.hasToken(JsonToken.VALUE_STRING) + && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { + String str = p.getText(); + if (str.length() == 0) { + return null; + } + } + + // Can we do implicit coercion to a single-element array still? + boolean canWrap = (_unwrapSingle == Boolean.TRUE) || + ((_unwrapSingle == null) && + ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + if (!canWrap) { + // One exception; byte arrays are generally serialized as base64, so that should be handled + if (p.getCurrentToken() == JsonToken.VALUE_STRING + // note: not `byte[]`, but `Byte[]` -- former is primitive array + && _elementClass == Byte.class) { + return deserializeFromBase64(p, ctxt); + } + throw ctxt.mappingException(_arrayType.getRawClass()); + } + JsonToken t = p.getCurrentToken(); + Object value; + + if (t == JsonToken.VALUE_NULL) { + value = _elementDeserializer.getNullValue(ctxt); + } else if (_elementTypeDeserializer == null) { + value = _elementDeserializer.deserialize(p, ctxt); + } else { + value = _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer); + } + // Ok: bit tricky, since we may want T[], not just Object[] + Object[] result; + + if (_untyped) { + result = new Object[1]; + } else { + result = (Object[]) Array.newInstance(_elementClass, 1); + } + result[0] = value; + return result; + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,598 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.ArrayBuilders; + +/** + * Container for deserializers used for instantiating "primitive arrays", + * arrays that contain non-object java primitive types. + */ +@SuppressWarnings("serial") +public abstract class PrimitiveArrayDeserializers extends StdDeserializer + implements ContextualDeserializer // since 2.7 +{ + /** + * Specific override for this instance (from proper, or global per-type overrides) + * to indicate whether single value may be taken to mean an unwrapped one-element array + * or not. If null, left to global defaults. + * + * @since 2.7 + */ + protected final Boolean _unwrapSingle; + + protected PrimitiveArrayDeserializers(Class cls) { + super(cls); + _unwrapSingle = null; + } + + /** + * @since 2.7 + */ + protected PrimitiveArrayDeserializers(PrimitiveArrayDeserializers base, + Boolean unwrapSingle) { + super(base._valueClass); + _unwrapSingle = unwrapSingle; + } + + public static JsonDeserializer forType(Class rawType) + { + // Start with more common types... + if (rawType == Integer.TYPE) { + return IntDeser.instance; + } + if (rawType == Long.TYPE) { + return LongDeser.instance; + } + + if (rawType == Byte.TYPE) { + return new ByteDeser(); + } + if (rawType == Short.TYPE) { + return new ShortDeser(); + } + if (rawType == Float.TYPE) { + return new FloatDeser(); + } + if (rawType == Double.TYPE) { + return new DoubleDeser(); + } + if (rawType == Boolean.TYPE) { + return new BooleanDeser(); + } + if (rawType == Character.TYPE) { + return new CharDeser(); + } + throw new IllegalStateException(); + } + + /** + * @since 2.7 + */ + protected abstract PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle); + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + Boolean unwrapSingle = findFormatFeature(ctxt, property, _valueClass, + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + if (unwrapSingle == _unwrapSingle) { + return this; + } + return withResolved(unwrapSingle); + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) throws IOException + { + /* Should there be separate handling for base64 stuff? + * for now this should be enough: + */ + return typeDeserializer.deserializeTypedFromArray(p, ctxt); + } + + protected T handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException + { + // [JACKSON-620] Empty String can become null... + if (p.hasToken(JsonToken.VALUE_STRING) + && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { + if (p.getText().length() == 0) { + return null; + } + } + boolean canWrap = (_unwrapSingle == Boolean.TRUE) || + ((_unwrapSingle == null) && + ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + if (canWrap) { + return handleSingleElementUnwrapped(p, ctxt); + } + throw ctxt.mappingException(_valueClass); + } + + protected abstract T handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException; + + /* + /******************************************************** + /* Actual deserializers: efficient String[], char[] deserializers + /******************************************************** + */ + + @JacksonStdImpl + final static class CharDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public CharDeser() { super(char[].class); } + protected CharDeser(CharDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + // 11-Dec-2015, tatu: Not sure how re-wrapping would work; omit + return this; + } + + @Override + public char[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + /* Won't take arrays, must get a String (could also + * convert other tokens to Strings... but let's not bother + * yet, doesn't seem to make sense) + */ + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_STRING) { + // note: can NOT return shared internal buffer, must copy: + char[] buffer = p.getTextCharacters(); + int offset = p.getTextOffset(); + int len = p.getTextLength(); + + char[] result = new char[len]; + System.arraycopy(buffer, offset, result, 0, len); + return result; + } + if (p.isExpectedStartArrayToken()) { + // Let's actually build as a String, then get chars + StringBuilder sb = new StringBuilder(64); + while ((t = p.nextToken()) != JsonToken.END_ARRAY) { + if (t != JsonToken.VALUE_STRING) { + throw ctxt.mappingException(Character.TYPE); + } + String str = p.getText(); + if (str.length() != 1) { + throw JsonMappingException.from(p, "Can not convert a JSON String of length "+str.length()+" into a char element of char array"); + } + sb.append(str.charAt(0)); + } + return sb.toString().toCharArray(); + } + // or, maybe an embedded object? + if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { + Object ob = p.getEmbeddedObject(); + if (ob == null) return null; + if (ob instanceof char[]) { + return (char[]) ob; + } + if (ob instanceof String) { + return ((String) ob).toCharArray(); + } + // 04-Feb-2011, tatu: byte[] can be converted; assuming base64 is wanted + if (ob instanceof byte[]) { + return Base64Variants.getDefaultVariant().encode((byte[]) ob, false).toCharArray(); + } + // not recognized, just fall through + } + throw ctxt.mappingException(_valueClass); + } + + @Override + protected char[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + // not sure how this should work so just return `null` so: + throw ctxt.mappingException(_valueClass); + } + } + + /* + /********************************************************** + /* Actual deserializers: primivate array desers + /********************************************************** + */ + + @JacksonStdImpl + final static class BooleanDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public BooleanDeser() { super(boolean[].class); } + protected BooleanDeser(BooleanDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new BooleanDeser(this, unwrapSingle); + } + + @Override + public boolean[] deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.BooleanBuilder builder = ctxt.getArrayBuilders().getBooleanBuilder(); + boolean[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while (p.nextToken() != JsonToken.END_ARRAY) { + // whether we should allow truncating conversions? + boolean value = _parseBooleanPrimitive(p, ctxt); + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected boolean[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + return new boolean[] { _parseBooleanPrimitive(p, ctxt) }; + } + } + + /** + * When dealing with byte arrays we have one more alternative (compared + * to int/long/shorts): base64 encoded data. + */ + @JacksonStdImpl + final static class ByteDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public ByteDeser() { super(byte[].class); } + protected ByteDeser(ByteDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new ByteDeser(this, unwrapSingle); + } + + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + JsonToken t = p.getCurrentToken(); + + // Most likely case: base64 encoded String? + if (t == JsonToken.VALUE_STRING) { + return p.getBinaryValue(ctxt.getBase64Variant()); + } + // 31-Dec-2009, tatu: Also may be hidden as embedded Object + if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { + Object ob = p.getEmbeddedObject(); + if (ob == null) return null; + if (ob instanceof byte[]) { + return (byte[]) ob; + } + } + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.ByteBuilder builder = ctxt.getArrayBuilders().getByteBuilder(); + byte[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while ((t = p.nextToken()) != JsonToken.END_ARRAY) { + // whether we should allow truncating conversions? + byte value; + if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { + // should we catch overflow exceptions? + value = p.getByteValue(); + } else { + // [JACKSON-79]: should probably accept nulls as 0 + if (t != JsonToken.VALUE_NULL) { + throw ctxt.mappingException(_valueClass.getComponentType()); + } + value = (byte) 0; + } + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected byte[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException + { + byte value; + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { + // should we catch overflow exceptions? + value = p.getByteValue(); + } else { + // should probably accept nulls as 'false' + if (t != JsonToken.VALUE_NULL) { + throw ctxt.mappingException(_valueClass.getComponentType()); + } + value = (byte) 0; + } + return new byte[] { value }; + } + } + + @JacksonStdImpl + final static class ShortDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public ShortDeser() { super(short[].class); } + protected ShortDeser(ShortDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new ShortDeser(this, unwrapSingle); + } + + @Override + public short[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.ShortBuilder builder = ctxt.getArrayBuilders().getShortBuilder(); + short[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while (p.nextToken() != JsonToken.END_ARRAY) { + short value = _parseShortPrimitive(p, ctxt); + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected short[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + return new short[] { _parseShortPrimitive(p, ctxt) }; + } + } + + @JacksonStdImpl + final static class IntDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public final static IntDeser instance = new IntDeser(); + + public IntDeser() { super(int[].class); } + protected IntDeser(IntDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new IntDeser(this, unwrapSingle); + } + + @Override + public int[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.IntBuilder builder = ctxt.getArrayBuilders().getIntBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while (p.nextToken() != JsonToken.END_ARRAY) { + // whether we should allow truncating conversions? + int value = _parseIntPrimitive(p, ctxt); + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected int[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + return new int[] { _parseIntPrimitive(p, ctxt) }; + } + } + + @JacksonStdImpl + final static class LongDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public final static LongDeser instance = new LongDeser(); + + public LongDeser() { super(long[].class); } + protected LongDeser(LongDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new LongDeser(this, unwrapSingle); + } + + @Override + public long[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.LongBuilder builder = ctxt.getArrayBuilders().getLongBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while (p.nextToken() != JsonToken.END_ARRAY) { + long value = _parseLongPrimitive(p, ctxt); + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected long[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + return new long[] { _parseLongPrimitive(p, ctxt) }; + } + } + + @JacksonStdImpl + final static class FloatDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public FloatDeser() { super(float[].class); } + protected FloatDeser(FloatDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new FloatDeser(this, unwrapSingle); + } + + @Override + public float[] deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.FloatBuilder builder = ctxt.getArrayBuilders().getFloatBuilder(); + float[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while (p.nextToken() != JsonToken.END_ARRAY) { + // whether we should allow truncating conversions? + float value = _parseFloatPrimitive(p, ctxt); + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected float[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + return new float[] { _parseFloatPrimitive(p, ctxt) }; + } + } + + @JacksonStdImpl + final static class DoubleDeser + extends PrimitiveArrayDeserializers + { + private static final long serialVersionUID = 1L; + + public DoubleDeser() { super(double[].class); } + protected DoubleDeser(DoubleDeser base, Boolean unwrapSingle) { + super(base, unwrapSingle); + } + + @Override + protected PrimitiveArrayDeserializers withResolved(Boolean unwrapSingle) { + return new DoubleDeser(this, unwrapSingle); + } + + @Override + public double[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + ArrayBuilders.DoubleBuilder builder = ctxt.getArrayBuilders().getDoubleBuilder(); + double[] chunk = builder.resetAndStart(); + int ix = 0; + + try { + while (p.nextToken() != JsonToken.END_ARRAY) { + double value = _parseDoublePrimitive(p, ctxt); + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, builder.bufferedSize() + ix); + } + return builder.completeAndClearBuffer(chunk, ix); + } + + @Override + protected double[] handleSingleElementUnwrapped(JsonParser p, + DeserializationContext ctxt) throws IOException { + return new double[] { _parseDoublePrimitive(p, ctxt) }; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StackTraceElementDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,62 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; + +public class StackTraceElementDeserializer + extends StdScalarDeserializer +{ + private static final long serialVersionUID = 1L; + + public StackTraceElementDeserializer() { super(StackTraceElement.class); } + + @Override + public StackTraceElement deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException + { + JsonToken t = jp.getCurrentToken(); + // Must get an Object + if (t == JsonToken.START_OBJECT) { + String className = "", methodName = "", fileName = ""; + int lineNumber = -1; + + while ((t = jp.nextValue()) != JsonToken.END_OBJECT) { + String propName = jp.getCurrentName(); + if ("className".equals(propName)) { + className = jp.getText(); + } else if ("fileName".equals(propName)) { + fileName = jp.getText(); + } else if ("lineNumber".equals(propName)) { + if (t.isNumeric()) { + lineNumber = jp.getIntValue(); + } else { + throw JsonMappingException.from(jp, "Non-numeric token ("+t+") for property 'lineNumber'"); + } + } else if ("methodName".equals(propName)) { + methodName = jp.getText(); + } else if ("nativeMethod".equals(propName)) { + // no setter, not passed via constructor: ignore + } else { + handleUnknownProperty(jp, ctxt, _valueClass, propName); + } + } + return new StackTraceElement(className, methodName, fileName, lineNumber); + } else if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + jp.nextToken(); + final StackTraceElement value = deserialize(jp, ctxt); + if (jp.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'java.lang.StackTraceElement' value but there was more than a single value in the array" + ); + } + return value; + } + + throw ctxt.mappingException(_valueClass, t); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,239 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Deserializer implementation where given Java type is first deserialized + * by a standard Jackson deserializer into a delegate type; and then + * this delegate type is converted using a configured + * {@link Converter} into desired target type. + * Common delegate types to use are {@link java.util.Map} + * and {@link com.fasterxml.jackson.databind.JsonNode}. + *

+ * Note that although types (delegate, target) may be related, they must not be same; trying + * to do this will result in an exception. + *

+ * Since 2.5 There is {@link StdNodeBasedDeserializer} that is a simplified version + * for cases where intermediate type is {@link JsonNode} + * + * @param Target type to convert to, from delegate type + * + * @since 2.1 + * + * @see StdNodeBasedDeserializer + * @see Converter + */ +public class StdDelegatingDeserializer + extends StdDeserializer + implements ContextualDeserializer, ResolvableDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final Converter _converter; + + /** + * Fully resolved delegate type, with generic information if any available. + */ + protected final JavaType _delegateType; + + /** + * Underlying serializer for type T. + */ + protected final JsonDeserializer _delegateDeserializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + @SuppressWarnings("unchecked") + public StdDelegatingDeserializer(Converter converter) + { + super(Object.class); + _converter = (Converter)converter; + _delegateType = null; + _delegateDeserializer = null; + } + + @SuppressWarnings("unchecked") + public StdDelegatingDeserializer(Converter converter, + JavaType delegateType, JsonDeserializer delegateDeserializer) + { + super(delegateType); + _converter = converter; + _delegateType = delegateType; + _delegateDeserializer = (JsonDeserializer) delegateDeserializer; + } + + /** + * @since 2.5 + */ + protected StdDelegatingDeserializer(StdDelegatingDeserializer src) + { + super(src); + _converter = src._converter; + _delegateType = src._delegateType; + _delegateDeserializer = src._delegateDeserializer; + } + + /** + * Method used for creating resolved contextual instances. Must be + * overridden when sub-classing. + */ + protected StdDelegatingDeserializer withDelegate(Converter converter, + JavaType delegateType, JsonDeserializer delegateDeserializer) + { + if (getClass() != StdDelegatingDeserializer.class) { + throw new IllegalStateException("Sub-class "+getClass().getName()+" must override 'withDelegate'"); + } + return new StdDelegatingDeserializer(converter, delegateType, delegateDeserializer); + } + + /* + /********************************************************** + /* Contextualization + /********************************************************** + */ + + @Override + public void resolve(DeserializationContext ctxt) + throws JsonMappingException + { + if (_delegateDeserializer != null && _delegateDeserializer instanceof ResolvableDeserializer) { + ((ResolvableDeserializer) _delegateDeserializer).resolve(ctxt); + } + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) + throws JsonMappingException + { + // First: if already got serializer to delegate to, contextualize it: + if (_delegateDeserializer != null) { + JsonDeserializer deser = ctxt.handleSecondaryContextualization(_delegateDeserializer, + property, _delegateType); + if (deser != _delegateDeserializer) { + return withDelegate(_converter, _delegateType, deser); + } + return this; + } + // Otherwise: figure out what is the fully generic delegate type, then find deserializer + JavaType delegateType = _converter.getInputType(ctxt.getTypeFactory()); + return withDelegate(_converter, delegateType, + ctxt.findContextualValueDeserializer(delegateType, property)); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public JsonDeserializer getDelegatee() { + return _delegateDeserializer; + } + + @Override + public Class handledType() { + return _delegateDeserializer.handledType(); + } + + /* + /********************************************************** + /* Serialization + /********************************************************** + */ + + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + Object delegateValue = _delegateDeserializer.deserialize(p, ctxt); + if (delegateValue == null) { + return null; + } + return convertValue(delegateValue); + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, + TypeDeserializer typeDeserializer) throws IOException + { + /* 12-Apr-2016, tatu: As predicted, earlier handling does not work + * (see [databind#1189] for details). There does not seem to be any compelling + * way to combine polymorphic types, Converters, but the least sucky way + * is probably to use Converter and ignore polymorphic type. Alternative + * would be to try to change `TypeDeserializer` to accept `Converter` to + * invoke... but that is more intrusive, yet not guaranteeing success. + */ + // method called up to 2.7.3: +// Object delegateValue = _delegateDeserializer.deserializeWithType(p, ctxt, typeDeserializer); + + // method called since 2.7.4 + Object delegateValue = _delegateDeserializer.deserialize(p, ctxt); + if (delegateValue == null) { + return null; + } + return convertValue(delegateValue); + } + + @SuppressWarnings("unchecked") + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue) + throws IOException + { + if (_delegateType.getRawClass().isAssignableFrom(intoValue.getClass())){ + return (T) _delegateDeserializer.deserialize(p, ctxt, intoValue); + } + return (T) _handleIncompatibleUpdateValue(p, ctxt, intoValue); + } + + /** + * Overridable handler method called when {@link #deserialize(JsonParser, DeserializationContext, Object)} + * has been called with a value that is not compatible with delegate value. + * Since no conversion are expected for such "updateValue" case, this is normally not + * an operation that can be permitted, and the default behavior is to throw exception. + * Sub-classes may choose to try alternative approach if they have more information on + * exact usage and constraints. + * + * @since 2.6 + */ + protected Object _handleIncompatibleUpdateValue(JsonParser p, DeserializationContext ctxt, Object intoValue) + throws IOException + { + throw new UnsupportedOperationException(String.format + ("Can not update object of type %s (using deserializer for type %s)" + +intoValue.getClass().getName(), _delegateType)); + } + + /* + /********************************************************** + /* Overridable methods + /********************************************************** + */ + + /** + * Method called to convert from "delegate value" (which was deserialized + * from JSON using standard Jackson deserializer for delegate type) + * into desired target type. + *

+ * The default implementation uses configured {@link Converter} to do + * conversion. + * + * @param delegateValue + * + * @return Result of conversion + */ + protected T convertValue(Object delegateValue) { + return _converter.convert(delegateValue); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1099 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonParser.NumberType; +import com.fasterxml.jackson.core.io.NumberInput; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Base class for common deserializers. Contains shared + * base functionality for dealing with primitive values, such + * as (re)parsing from String. + */ +public abstract class StdDeserializer + extends JsonDeserializer + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Bitmask that covers {@link DeserializationFeature#USE_BIG_INTEGER_FOR_INTS} + * and {@link DeserializationFeature#USE_LONG_FOR_INTS}, used for more efficient + * cheks when coercing integral values for untyped deserialization. + * + * @since 2.6 + */ + protected final static int F_MASK_INT_COERCIONS = + DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.getMask() + | DeserializationFeature.USE_LONG_FOR_INTS.getMask(); + + /** + * Type of values this deserializer handles: sometimes + * exact types, other time most specific supertype of + * types deserializer handles (which may be as generic + * as {@link Object} in some case) + */ + final protected Class _valueClass; + + protected StdDeserializer(Class vc) { + _valueClass = vc; + } + + protected StdDeserializer(JavaType valueType) { + _valueClass = (valueType == null) ? null : valueType.getRawClass(); + } + + /** + * Copy-constructor for sub-classes to use, most often when creating + * new instances for {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}. + * + * @since 2.5 + */ + protected StdDeserializer(StdDeserializer src) { + _valueClass = src._valueClass; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public Class handledType() { return _valueClass; } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * @deprecated Since 2.3 use {@link #handledType} instead + */ + @Deprecated + public final Class getValueClass() { return _valueClass; } + + /** + * Exact structured type deserializer handles, if known. + *

+ * Default implementation just returns null. + */ + public JavaType getValueType() { return null; } + + /** + * Method that can be called to determine if given deserializer is the default + * deserializer Jackson uses; as opposed to a custom deserializer installed by + * a module or calling application. Determination is done using + * {@link JacksonStdImpl} annotation on deserializer class. + */ + protected boolean isDefaultDeserializer(JsonDeserializer deserializer) { + return ClassUtil.isJacksonStdImpl(deserializer); + } + + protected boolean isDefaultKeyDeserializer(KeyDeserializer keyDeser) { + return ClassUtil.isJacksonStdImpl(keyDeser); + } + + /* + /********************************************************** + /* Partial JsonDeserializer implementation + /********************************************************** + */ + + /** + * Base implementation that does not assume specific type + * inclusion mechanism. Sub-classes are expected to override + * this method if they are to handle type information. + */ + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + return typeDeserializer.deserializeTypedFromAny(jp, ctxt); + } + + /* + /********************************************************** + /* Helper methods for sub-classes, parsing: while mostly + /* useful for numeric types, can be also useful for dealing + /* with things serialized as numbers (such as Dates). + /********************************************************** + */ + + protected final boolean _parseBooleanPrimitive(JsonParser jp, DeserializationContext ctxt) throws IOException + { + JsonToken t = jp.getCurrentToken(); + if (t == JsonToken.VALUE_TRUE) return true; + if (t == JsonToken.VALUE_FALSE) return false; + if (t == JsonToken.VALUE_NULL) return false; + + // [JACKSON-78]: should accept ints too, (0 == false, otherwise true) + if (t == JsonToken.VALUE_NUMBER_INT) { + // 11-Jan-2012, tatus: May be outside of int... + if (jp.getNumberType() == NumberType.INT) { + return (jp.getIntValue() != 0); + } + return _parseBooleanFromOther(jp, ctxt); + } + // And finally, let's allow Strings to be converted too + if (t == JsonToken.VALUE_STRING) { + String text = jp.getText().trim(); + // [#422]: Allow aliases + if ("true".equals(text) || "True".equals(text)) { + return true; + } + if ("false".equals(text) || "False".equals(text) || text.length() == 0) { + return false; + } + if (_hasTextualNull(text)) { + return false; + } + throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized"); + } + // [databind#381] + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + jp.nextToken(); + final boolean parsed = _parseBooleanPrimitive(jp, ctxt); + t = jp.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'boolean' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt) + throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_TRUE) { + return Boolean.TRUE; + } + if (t == JsonToken.VALUE_FALSE) { + return Boolean.FALSE; + } + // [JACKSON-78]: should accept ints too, (0 == false, otherwise true) + if (t == JsonToken.VALUE_NUMBER_INT) { + // 11-Jan-2012, tatus: May be outside of int... + if (p.getNumberType() == NumberType.INT) { + return (p.getIntValue() == 0) ? Boolean.FALSE : Boolean.TRUE; + } + return Boolean.valueOf(_parseBooleanFromOther(p, ctxt)); + } + if (t == JsonToken.VALUE_NULL) { + return (Boolean) getNullValue(ctxt); + } + // And finally, let's allow Strings to be converted too + if (t == JsonToken.VALUE_STRING) { + String text = p.getText().trim(); + // [#422]: Allow aliases + if ("true".equals(text) || "True".equals(text)) { + return Boolean.TRUE; + } + if ("false".equals(text) || "False".equals(text)) { + return Boolean.FALSE; + } + if (text.length() == 0) { + return (Boolean) getEmptyValue(ctxt); + } + if (_hasTextualNull(text)) { + return (Boolean) getNullValue(ctxt); + } + throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized"); + } + // Issue#381 + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Boolean parsed = _parseBoolean(p, ctxt); + t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Boolean' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected final boolean _parseBooleanFromOther(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (p.getNumberType() == NumberType.LONG) { + return (p.getLongValue() == 0L) ? Boolean.FALSE : Boolean.TRUE; + } + // no really good logic; let's actually resort to textual comparison + String str = p.getText(); + if ("0.0".equals(str) || "0".equals(str)) { + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + protected Byte _parseByte(JsonParser p, DeserializationContext ctxt) + throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NUMBER_INT) { + return p.getByteValue(); + } + if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse + String text = p.getText().trim(); + if (_hasTextualNull(text)) { + return (Byte) getNullValue(ctxt); + } + int value; + try { + int len = text.length(); + if (len == 0) { + return (Byte) getEmptyValue(ctxt); + } + value = NumberInput.parseInt(text); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid Byte value"); + } + // So far so good: but does it fit? + // as per [JACKSON-804], allow range up to 255, inclusive + if (value < Byte.MIN_VALUE || value > 255) { + throw ctxt.weirdStringException(text, _valueClass, "overflow, value can not be represented as 8-bit value"); + } + return Byte.valueOf((byte) value); + } + if (t == JsonToken.VALUE_NUMBER_FLOAT) { + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "Byte"); + } + return p.getByteValue(); + } + if (t == JsonToken.VALUE_NULL) { + return (Byte) getNullValue(ctxt); + } + // Issue#381 + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Byte parsed = _parseByte(p, ctxt); + t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Byte' value but there was more than a single value in the array"); + } + return parsed; + } + throw ctxt.mappingException(_valueClass, t); + } + + protected Short _parseShort(JsonParser p, DeserializationContext ctxt) + throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NUMBER_INT) { + return p.getShortValue(); + } + if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse + String text = p.getText().trim(); + int value; + try { + int len = text.length(); + if (len == 0) { + return (Short) getEmptyValue(ctxt); + } + if (_hasTextualNull(text)) { + return (Short) getNullValue(ctxt); + } + value = NumberInput.parseInt(text); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid Short value"); + } + // So far so good: but does it fit? + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw ctxt.weirdStringException(text, _valueClass, "overflow, value can not be represented as 16-bit value"); + } + return Short.valueOf((short) value); + } + if (t == JsonToken.VALUE_NUMBER_FLOAT) { + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "Short"); + } + return p.getShortValue(); + } + if (t == JsonToken.VALUE_NULL) { + return (Short) getNullValue(ctxt); + } + // Issue#381 + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Short parsed = _parseShort(p, ctxt); + t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Short' value but there was more than a single value in the array"); + } + return parsed; + } + throw ctxt.mappingException(_valueClass, t); + } + + protected final short _parseShortPrimitive(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + int value = _parseIntPrimitive(jp, ctxt); + // So far so good: but does it fit? + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw ctxt.weirdStringException(String.valueOf(value), + _valueClass, "overflow, value can not be represented as 16-bit value"); + } + return (short) value; + } + + protected final int _parseIntPrimitive(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return p.getIntValue(); + } + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse + String text = p.getText().trim(); + if (_hasTextualNull(text)) { + return 0; + } + try { + int len = text.length(); + if (len > 9) { + long l = Long.parseLong(text); + if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) { + throw ctxt.weirdStringException(text, _valueClass, + "Overflow: numeric value ("+text+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")"); + } + return (int) l; + } + if (len == 0) { + return 0; + } + return NumberInput.parseInt(text); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid int value"); + } + } + if (t == JsonToken.VALUE_NUMBER_FLOAT) { + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "int"); + } + return p.getValueAsInt(); + } + if (t == JsonToken.VALUE_NULL) { + return 0; + } + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final int parsed = _parseIntPrimitive(p, ctxt); + t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'int' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt) + throws IOException + { + switch (p.getCurrentTokenId()) { + // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path + case JsonTokenId.ID_NUMBER_INT: + return Integer.valueOf(p.getIntValue()); + case JsonTokenId.ID_NUMBER_FLOAT: // coercing may work too + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "Integer"); + } + return Integer.valueOf(p.getValueAsInt()); + case JsonTokenId.ID_STRING: // let's do implicit re-parse + String text = p.getText().trim(); + try { + int len = text.length(); + if (_hasTextualNull(text)) { + return (Integer) getNullValue(ctxt); + } + if (len > 9) { + long l = Long.parseLong(text); + if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) { + throw ctxt.weirdStringException(text, _valueClass, + "Overflow: numeric value ("+text+") out of range of Integer ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")"); + } + return Integer.valueOf((int) l); + } + if (len == 0) { + return (Integer) getEmptyValue(ctxt); + } + return Integer.valueOf(NumberInput.parseInt(text)); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(text, _valueClass, "not a valid Integer value"); + } + case JsonTokenId.ID_NULL: + return (Integer) getNullValue(ctxt); + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Integer parsed = _parseInteger(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Integer' value but there was more than a single value in the array"); + } + return parsed; + } + break; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + + protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throws IOException + { + switch (p.getCurrentTokenId()) { + // NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path + case JsonTokenId.ID_NUMBER_INT: + return p.getLongValue(); + case JsonTokenId.ID_NUMBER_FLOAT: + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "Long"); + } + return p.getValueAsLong(); + case JsonTokenId.ID_STRING: + // let's allow Strings to be converted too + // !!! 05-Jan-2009, tatu: Should we try to limit value space, JDK is too lenient? + String text = p.getText().trim(); + if (text.length() == 0) { + return (Long) getEmptyValue(ctxt); + } + if (_hasTextualNull(text)) { + return (Long) getNullValue(ctxt); + } + try { + return Long.valueOf(NumberInput.parseLong(text)); + } catch (IllegalArgumentException iae) { } + throw ctxt.weirdStringException(text, _valueClass, "not a valid Long value"); + case JsonTokenId.ID_NULL: + return (Long) getNullValue(ctxt); + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Long parsed = _parseLong(p, ctxt); + JsonToken t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Long' value but there was more than a single value in the array"); + } + return parsed; + } + break; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + + protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ctxt) + throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_INT: + return p.getLongValue(); + case JsonTokenId.ID_NUMBER_FLOAT: + if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) { + _failDoubleToIntCoercion(p, ctxt, "long"); + } + return p.getValueAsLong(); + case JsonTokenId.ID_STRING: + String text = p.getText().trim(); + if (text.length() == 0 || _hasTextualNull(text)) { + return 0L; + } + try { + return NumberInput.parseLong(text); + } catch (IllegalArgumentException iae) { } + throw ctxt.weirdStringException(text, _valueClass, "not a valid long value"); + case JsonTokenId.ID_NULL: + return 0L; + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final long parsed = _parseLongPrimitive(p, ctxt); + JsonToken t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'long' value but there was more than a single value in the array"); + } + return parsed; + } + break; + } + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + + protected final Float _parseFloat(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + // We accept couple of different types; obvious ones first: + JsonToken t = jp.getCurrentToken(); + + if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too + return jp.getFloatValue(); + } + // And finally, let's allow Strings to be converted too + if (t == JsonToken.VALUE_STRING) { + String text = jp.getText().trim(); + if (text.length() == 0) { + return (Float) getEmptyValue(ctxt); + } + if (_hasTextualNull(text)) { + return (Float) getNullValue(ctxt); + } + switch (text.charAt(0)) { + case 'I': + if (_isPosInf(text)) { + return Float.POSITIVE_INFINITY; + } + break; + case 'N': + if (_isNaN(text)) { + return Float.NaN; + } + break; + case '-': + if (_isNegInf(text)) { + return Float.NEGATIVE_INFINITY; + } + break; + } + try { + return Float.parseFloat(text); + } catch (IllegalArgumentException iae) { } + throw ctxt.weirdStringException(text, _valueClass, "not a valid Float value"); + } + if (t == JsonToken.VALUE_NULL) { + return (Float) getNullValue(ctxt); + } + // Issue#381 + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + jp.nextToken(); + final Float parsed = _parseFloat(jp, ctxt); + t = jp.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Byte' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected final float _parseFloatPrimitive(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + JsonToken t = jp.getCurrentToken(); + + if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too + return jp.getFloatValue(); + } + if (t == JsonToken.VALUE_STRING) { + String text = jp.getText().trim(); + if (text.length() == 0 || _hasTextualNull(text)) { + return 0.0f; + } + switch (text.charAt(0)) { + case 'I': + if (_isPosInf(text)) { + return Float.POSITIVE_INFINITY; + } + break; + case 'N': + if (_isNaN(text)) { return Float.NaN; } + break; + case '-': + if (_isNegInf(text)) { + return Float.NEGATIVE_INFINITY; + } + break; + } + try { + return Float.parseFloat(text); + } catch (IllegalArgumentException iae) { } + throw ctxt.weirdStringException(text, _valueClass, "not a valid float value"); + } + if (t == JsonToken.VALUE_NULL) { + return 0.0f; + } + // Issue#381 + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + jp.nextToken(); + final float parsed = _parseFloatPrimitive(jp, ctxt); + t = jp.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'float' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected final Double _parseDouble(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + JsonToken t = jp.getCurrentToken(); + + if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too + return jp.getDoubleValue(); + } + if (t == JsonToken.VALUE_STRING) { + String text = jp.getText().trim(); + if (text.length() == 0) { + return (Double) getEmptyValue(ctxt); + } + if (_hasTextualNull(text)) { + return (Double) getNullValue(ctxt); + } + switch (text.charAt(0)) { + case 'I': + if (_isPosInf(text)) { + return Double.POSITIVE_INFINITY; + } + break; + case 'N': + if (_isNaN(text)) { + return Double.NaN; + } + break; + case '-': + if (_isNegInf(text)) { + return Double.NEGATIVE_INFINITY; + } + break; + } + try { + return parseDouble(text); + } catch (IllegalArgumentException iae) { } + throw ctxt.weirdStringException(text, _valueClass, "not a valid Double value"); + } + if (t == JsonToken.VALUE_NULL) { + return (Double) getNullValue(ctxt); + } + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + jp.nextToken(); + final Double parsed = _parseDouble(jp, ctxt); + t = jp.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Double' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected final double _parseDoublePrimitive(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + // We accept couple of different types; obvious ones first: + JsonToken t = jp.getCurrentToken(); + + if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too + return jp.getDoubleValue(); + } + // And finally, let's allow Strings to be converted too + if (t == JsonToken.VALUE_STRING) { + String text = jp.getText().trim(); + if (text.length() == 0 || _hasTextualNull(text)) { + return 0.0; + } + switch (text.charAt(0)) { + case 'I': + if (_isPosInf(text)) { + return Double.POSITIVE_INFINITY; + } + break; + case 'N': + if (_isNaN(text)) { + return Double.NaN; + } + break; + case '-': + if (_isNegInf(text)) { + return Double.NEGATIVE_INFINITY; + } + break; + } + try { + return parseDouble(text); + } catch (IllegalArgumentException iae) { } + throw ctxt.weirdStringException(text, _valueClass, "not a valid double value"); + } + if (t == JsonToken.VALUE_NULL) { + return 0.0; + } + // Issue#381 + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + jp.nextToken(); + final double parsed = _parseDoublePrimitive(jp, ctxt); + t = jp.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'Byte' value but there was more than a single value in the array"); + } + return parsed; + } + // Otherwise, no can do: + throw ctxt.mappingException(_valueClass, t); + } + + protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt) + throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_NUMBER_INT) { + return new java.util.Date(p.getLongValue()); + } + if (t == JsonToken.VALUE_NULL) { + return (java.util.Date) getNullValue(ctxt); + } + if (t == JsonToken.VALUE_STRING) { + String value = null; + try { + // As per [JACKSON-203], take empty Strings to mean + value = p.getText().trim(); + if (value.length() == 0) { + return (Date) getEmptyValue(ctxt); + } + if (_hasTextualNull(value)) { + return (java.util.Date) getNullValue(ctxt); + } + return ctxt.parseDate(value); + } catch (IllegalArgumentException iae) { + throw ctxt.weirdStringException(value, _valueClass, + "not a valid representation (error: "+iae.getMessage()+")"); + } + } + // [databind#381] + if (t == JsonToken.START_ARRAY) { + if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final Date parsed = _parseDate(p, ctxt); + t = p.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'java.util.Date' value but there was more than a single value in the array"); + } + return parsed; + } + } + throw ctxt.mappingException(_valueClass, t); + } + + /** + * Helper method for encapsulating calls to low-level double value parsing; single place + * just because we need a work-around that must be applied to all calls. + */ + protected final static double parseDouble(String numStr) throws NumberFormatException + { + // avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE? + if (NumberInput.NASTY_SMALL_DOUBLE.equals(numStr)) { + return Double.MIN_NORMAL; // since 2.7; was MIN_VALUE prior + } + return Double.parseDouble(numStr); + } + + /** + * Helper method used for accessing String value, if possible, doing + * necessary conversion or throwing exception as necessary. + * + * @since 2.1 + */ + protected final String _parseString(JsonParser p, DeserializationContext ctxt) throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.VALUE_STRING) { + return p.getText(); + } + // [databind#381] + if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final String parsed = _parseString(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'String' value but there was more than a single value in the array"); + } + return parsed; + } + String value = p.getValueAsString(); + if (value != null) { + return value; + } + throw ctxt.mappingException(String.class, p.getCurrentToken()); + } + + /** + * Helper method that may be used to support fallback for Empty String / Empty Array + * non-standard representations; usually for things serialized as JSON Objects. + * + * @since 2.5 + */ + protected T _deserializeFromEmpty(JsonParser jp, DeserializationContext ctxt) + throws IOException + { + JsonToken t = jp.getCurrentToken(); + if (t == JsonToken.START_ARRAY) { + if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { + t = jp.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + throw ctxt.mappingException(handledType(), JsonToken.START_ARRAY); + } + } else if (t == JsonToken.VALUE_STRING) { + if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { + String str = jp.getText().trim(); + if (str.isEmpty()) { + return null; + } + } + } + throw ctxt.mappingException(handledType()); + } + + /** + * Helper method called to determine if we are seeing String value of + * "null", and, further, that it should be coerced to null just like + * null token. + * + * @since 2.3 + */ + protected boolean _hasTextualNull(String value) { + return "null".equals(value); + } + + protected final boolean _isNegInf(String text) { + return "-Infinity".equals(text) || "-INF".equals(text); + } + + protected final boolean _isPosInf(String text) { + return "Infinity".equals(text) || "INF".equals(text); + } + + protected final boolean _isNaN(String text) { return "NaN".equals(text); } + + /* + /**************************************************** + /* Helper methods for sub-classes, coercions + /**************************************************** + */ + + /** + * Helper method called in case where an integral number is encountered, but + * config settings suggest that a coercion may be needed to "upgrade" + * {@link java.lang.Number} into "bigger" type like {@link java.lang.Long} or + * {@link java.math.BigInteger} + * + * @see DeserializationFeature#USE_BIG_INTEGER_FOR_INTS + * @see DeserializationFeature#USE_LONG_FOR_INTS + * + * @since 2.6 + */ + protected Object _coerceIntegral(JsonParser p, DeserializationContext ctxt) throws IOException + { + int feats = ctxt.getDeserializationFeatures(); + if (DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.enabledIn(feats)) { + return p.getBigIntegerValue(); + } + if (DeserializationFeature.USE_LONG_FOR_INTS.enabledIn(feats)) { + return p.getLongValue(); + } + return p.getBigIntegerValue(); // should be optimal, whatever it is + } + + /* + /**************************************************** + /* Helper methods for sub-classes, resolving dependencies + /**************************************************** + */ + + /** + * Helper method used to locate deserializers for properties the + * type this deserializer handles contains (usually for properties of + * bean types) + * + * @param type Type of property to deserialize + * @param property Actual property object (field, method, constuctor parameter) used + * for passing deserialized values; provided so deserializer can be contextualized if necessary + */ + protected JsonDeserializer findDeserializer(DeserializationContext ctxt, + JavaType type, BeanProperty property) + throws JsonMappingException + { + return ctxt.findContextualValueDeserializer(type, property); + } + + /** + * Helper method to check whether given text refers to what looks like a clean simple + * integer number, consisting of optional sign followed by a sequence of digits. + */ + protected final boolean _isIntNumber(String text) + { + final int len = text.length(); + if (len > 0) { + char c = text.charAt(0); + // skip leading sign (plus not allowed for strict JSON numbers but...) + int i = (c == '-' || c == '+') ? 1 : 0; + for (; i < len; ++i) { + int ch = text.charAt(i); + if (ch > '9' || ch < '0') { + return false; + } + } + return true; + } + return false; + } + + /* + /********************************************************** + /* Helper methods for sub-classes, deserializer construction + /********************************************************** + */ + + /** + * Helper method that can be used to see if specified property has annotation + * indicating that a converter is to be used for contained values (contents + * of structured types; array/List/Map values) + * + * @param existingDeserializer (optional) configured content + * serializer if one already exists. + * + * @since 2.2 + */ + protected JsonDeserializer findConvertingContentDeserializer(DeserializationContext ctxt, + BeanProperty prop, JsonDeserializer existingDeserializer) + throws JsonMappingException + { + final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); + if (intr != null && prop != null) { + AnnotatedMember member = prop.getMember(); + if (member != null) { + Object convDef = intr.findDeserializationContentConverter(member); + if (convDef != null) { + Converter conv = ctxt.converterInstance(prop.getMember(), convDef); + JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); + if (existingDeserializer == null) { + existingDeserializer = ctxt.findContextualValueDeserializer(delegateType, prop); + } + return new StdDelegatingDeserializer(conv, delegateType, existingDeserializer); + } + } + } + return existingDeserializer; + } + + /** + * Helper method that may be used to find if this deserializer has specific + * {@link JsonFormat} settings, either via property, or through type-specific + * defaulting. + * + * @param typeForDefaults Type (erased) used for finding default format settings, if any + * + * @since 2.7 + */ + protected JsonFormat.Value findFormatOverrides(DeserializationContext ctxt, + BeanProperty prop, Class typeForDefaults) + { + if (prop != null) { + return prop.findPropertyFormat(ctxt.getConfig(), typeForDefaults); + } + // even without property or AnnotationIntrospector, may have type-specific defaults + return ctxt.getDefaultPropertyFormat(typeForDefaults); + } + + /** + * Convenience method that uses {@link #findFormatOverrides} to find possible + * defaults and/of overrides, and then calls + * JsonFormat.Value.getFeature(feat) + * to find whether that feature has been specifically marked as enabled or disabled. + * + * @param typeForDefaults Type (erased) used for finding default format settings, if any + * + * @since 2.7 + */ + protected Boolean findFormatFeature(DeserializationContext ctxt, + BeanProperty prop, Class typeForDefaults, JsonFormat.Feature feat) + { + JsonFormat.Value format = findFormatOverrides(ctxt, prop, typeForDefaults); + if (format != null) { + return format.getFeature(feat); + } + return null; + } + + /* + /********************************************************** + /* Helper methods for sub-classes, problem reporting + /********************************************************** + */ + + /** + * Method called to deal with a property that did not map to a known + * Bean property. Method can deal with the problem as it sees fit (ignore, + * throw exception); but if it does return, it has to skip the matching + * Json content parser has. + *

+ * NOTE: method signature was changed in version 1.5; explicit JsonParser + * must be passed since it may be something other than what + * context has. Prior versions did not include the first parameter. + * + * @param jp Parser that points to value of the unknown property + * @param ctxt Context for deserialization; allows access to the parser, + * error reporting functionality + * @param instanceOrClass Instance that is being populated by this + * deserializer, or if not known, Class that would be instantiated. + * If null, will assume type is what {@link #getValueClass} returns. + * @param propName Name of the property that can not be mapped + */ + protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object instanceOrClass, String propName) + throws IOException + { + if (instanceOrClass == null) { + instanceOrClass = handledType(); + } + // Maybe we have configured handler(s) to take care of it? + if (ctxt.handleUnknownProperty(jp, this, instanceOrClass, propName)) { + return; + } + // Nope, not handled. Potentially that's a problem... + ctxt.reportUnknownProperty(instanceOrClass, propName, this); + + /* But if we do get this far, need to skip whatever value we + * are pointing to now. + */ + jp.skipChildren(); + } + + protected void _failDoubleToIntCoercion(JsonParser jp, DeserializationContext ctxt, + String type) throws IOException + { + throw ctxt.mappingException("Can not coerce a floating-point value ('%s') into %s; enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow", + jp.getValueAsString(), type); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,415 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URL; +import java.util.Calendar; +import java.util.Currency; +import java.util.Date; +import java.util.Locale; +import java.util.UUID; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.io.NumberInput; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.EnumResolver; + +/** + * Default {@link KeyDeserializer} implementation used for most {@link java.util.Map} + * types Jackson supports. + * Implemented as "chameleon" (or swiss pocket knife) class; not particularly elegant, + * but helps reduce number of classes and jar size (class metadata adds significant + * per-class overhead; much more than bytecode). + */ +@JacksonStdImpl +public class StdKeyDeserializer extends KeyDeserializer + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + public final static int TYPE_BOOLEAN = 1; + public final static int TYPE_BYTE = 2; + public final static int TYPE_SHORT = 3; + public final static int TYPE_CHAR = 4; + public final static int TYPE_INT = 5; + public final static int TYPE_LONG = 6; + public final static int TYPE_FLOAT = 7; + public final static int TYPE_DOUBLE = 8; + public final static int TYPE_LOCALE = 9; + public final static int TYPE_DATE = 10; + public final static int TYPE_CALENDAR = 11; + public final static int TYPE_UUID = 12; + public final static int TYPE_URI = 13; + public final static int TYPE_URL = 14; + public final static int TYPE_CLASS = 15; + public final static int TYPE_CURRENCY = 16; + + final protected int _kind; + final protected Class _keyClass; + + /** + * Some types that are deserialized using a helper deserializer. + */ + protected final FromStringDeserializer _deser; + + protected StdKeyDeserializer(int kind, Class cls) { + this(kind, cls, null); + } + + protected StdKeyDeserializer(int kind, Class cls, FromStringDeserializer deser) { + _kind = kind; + _keyClass = cls; + _deser = deser; + } + + public static StdKeyDeserializer forType(Class raw) + { + int kind; + + // first common types: + if (raw == String.class || raw == Object.class) { + return StringKD.forType(raw); + } else if (raw == UUID.class) { + kind = TYPE_UUID; + } else if (raw == Integer.class) { + kind = TYPE_INT; + } else if (raw == Long.class) { + kind = TYPE_LONG; + } else if (raw == Date.class) { + kind = TYPE_DATE; + } else if (raw == Calendar.class) { + kind = TYPE_CALENDAR; + // then less common ones... + } else if (raw == Boolean.class) { + kind = TYPE_BOOLEAN; + } else if (raw == Byte.class) { + kind = TYPE_BYTE; + } else if (raw == Character.class) { + kind = TYPE_CHAR; + } else if (raw == Short.class) { + kind = TYPE_SHORT; + } else if (raw == Float.class) { + kind = TYPE_FLOAT; + } else if (raw == Double.class) { + kind = TYPE_DOUBLE; + } else if (raw == URI.class) { + kind = TYPE_URI; + } else if (raw == URL.class) { + kind = TYPE_URL; + } else if (raw == Class.class) { + kind = TYPE_CLASS; + } else if (raw == Locale.class) { + FromStringDeserializer deser = FromStringDeserializer.findDeserializer(Locale.class); + return new StdKeyDeserializer(TYPE_LOCALE, raw, deser); + } else if (raw == Currency.class) { + FromStringDeserializer deser = FromStringDeserializer.findDeserializer(Currency.class); + return new StdKeyDeserializer(TYPE_CURRENCY, raw, deser); + } else { + return null; + } + return new StdKeyDeserializer(kind, raw); + } + + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) + throws IOException + { + if (key == null) { // is this even legal call? + return null; + } + try { + Object result = _parse(key, ctxt); + if (result != null) { + return result; + } + } catch (Exception re) { + throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation: "+re.getMessage()); + } + if (_keyClass.isEnum() && ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { + return null; + } + throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation"); + } + + public Class getKeyClass() { return _keyClass; } + + protected Object _parse(String key, DeserializationContext ctxt) throws Exception + { + switch (_kind) { + case TYPE_BOOLEAN: + if ("true".equals(key)) { + return Boolean.TRUE; + } + if ("false".equals(key)) { + return Boolean.FALSE; + } + throw ctxt.weirdKeyException(_keyClass, key, "value not 'true' or 'false'"); + case TYPE_BYTE: + { + int value = _parseInt(key); + // as per [JACKSON-804], allow range up to 255, inclusive + if (value < Byte.MIN_VALUE || value > 255) { + throw ctxt.weirdKeyException(_keyClass, key, "overflow, value can not be represented as 8-bit value"); + } + return Byte.valueOf((byte) value); + } + case TYPE_SHORT: + { + int value = _parseInt(key); + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw ctxt.weirdKeyException(_keyClass, key, "overflow, value can not be represented as 16-bit value"); + } + return Short.valueOf((short) value); + } + case TYPE_CHAR: + if (key.length() == 1) { + return Character.valueOf(key.charAt(0)); + } + throw ctxt.weirdKeyException(_keyClass, key, "can only convert 1-character Strings"); + case TYPE_INT: + return _parseInt(key); + + case TYPE_LONG: + return _parseLong(key); + + case TYPE_FLOAT: + // Bounds/range checks would be tricky here, so let's not bother even trying... + return Float.valueOf((float) _parseDouble(key)); + case TYPE_DOUBLE: + return _parseDouble(key); + case TYPE_LOCALE: + try { + return _deser._deserialize(key, ctxt); + } catch (IOException e) { + throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as locale"); + } + case TYPE_CURRENCY: + try { + return _deser._deserialize(key, ctxt); + } catch (IOException e) { + throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as currency"); + } + case TYPE_DATE: + return ctxt.parseDate(key); + case TYPE_CALENDAR: + java.util.Date date = ctxt.parseDate(key); + return (date == null) ? null : ctxt.constructCalendar(date); + case TYPE_UUID: + return UUID.fromString(key); + case TYPE_URI: + return URI.create(key); + case TYPE_URL: + return new URL(key); + case TYPE_CLASS: + try { + return ctxt.findClass(key); + } catch (Exception e) { + throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as Class"); + } + } + return null; + } + + /* + /********************************************************** + /* Helper methods for sub-classes + /********************************************************** + */ + + protected int _parseInt(String key) throws IllegalArgumentException { + return Integer.parseInt(key); + } + + protected long _parseLong(String key) throws IllegalArgumentException { + return Long.parseLong(key); + } + + protected double _parseDouble(String key) throws IllegalArgumentException { + return NumberInput.parseDouble(key); + } + + /* + /********************************************************** + /* First: the standard "String as String" deserializer + /********************************************************** + */ + + @JacksonStdImpl + final static class StringKD extends StdKeyDeserializer + { + private static final long serialVersionUID = 1L; + private final static StringKD sString = new StringKD(String.class); + private final static StringKD sObject = new StringKD(Object.class); + + private StringKD(Class nominalType) { super(-1, nominalType); } + + public static StringKD forType(Class nominalType) + { + if (nominalType == String.class) { + return sString; + } + if (nominalType == Object.class) { + return sObject; + } + return new StringKD(nominalType); + } + + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return key; + } + } + + /* + /********************************************************** + /* Key deserializer implementations; other + /********************************************************** + */ + + /** + * Key deserializer that wraps a "regular" deserializer (but one + * that must recognize FIELD_NAMEs as text!) to reuse existing + * handlers as key handlers. + */ + final static class DelegatingKD + extends KeyDeserializer // note: NOT the std one + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + final protected Class _keyClass; + + protected final JsonDeserializer _delegate; + + protected DelegatingKD(Class cls, JsonDeserializer deser) { + _keyClass = cls; + _delegate = deser; + } + + @Override + public final Object deserializeKey(String key, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (key == null) { // is this even legal call? + return null; + } + try { + // Ugh... should not have to give parser which may or may not be correct one... + Object result = _delegate.deserialize(ctxt.getParser(), ctxt); + if (result != null) { + return result; + } + } catch (Exception re) { + throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation: "+re.getMessage()); + } + throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation"); + } + + public Class getKeyClass() { return _keyClass; } + } + + @JacksonStdImpl + final static class EnumKD extends StdKeyDeserializer + { + private static final long serialVersionUID = 1L; + + protected final EnumResolver _byNameResolver; + + protected final AnnotatedMethod _factory; + + /** + * Lazily constructed alternative in case there is need to + * use 'toString()' method as the source. + * + * @since 2.7.3 + */ + protected EnumResolver _byToStringResolver; + + protected EnumKD(EnumResolver er, AnnotatedMethod factory) { + super(-1, er.getEnumClass()); + _byNameResolver = er; + _factory = factory; + } + + @Override + public Object _parse(String key, DeserializationContext ctxt) throws JsonMappingException + { + if (_factory != null) { + try { + return _factory.call1(key); + } catch (Exception e) { + ClassUtil.unwrapAndThrowAsIAE(e); + } + } + EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING) + ? _getToStringResolver() : _byNameResolver; + Enum e = res.findEnum(key); + if ((e == null) && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { + throw ctxt.weirdKeyException(_keyClass, key, "not one of values excepted for Enum class: " + +res.getEnumIds()); + } + return e; + } + + private EnumResolver _getToStringResolver() + { + EnumResolver res = _byToStringResolver; + if (res == null) { + synchronized (this) { + res = EnumResolver.constructUnsafeUsingToString(_byNameResolver.getEnumClass()); + } + } + return res; + } + } + + /** + * Key deserializer that calls a single-string-arg constructor + * to instantiate desired key type. + */ + final static class StringCtorKeyDeserializer extends StdKeyDeserializer + { + private static final long serialVersionUID = 1L; + + protected final Constructor _ctor; + + public StringCtorKeyDeserializer(Constructor ctor) { + super(-1, ctor.getDeclaringClass()); + _ctor = ctor; + } + + @Override + public Object _parse(String key, DeserializationContext ctxt) throws Exception + { + return _ctor.newInstance(key); + } + } + + /** + * Key deserializer that calls a static no-args factory method + * to instantiate desired key type. + */ + final static class StringFactoryKeyDeserializer extends StdKeyDeserializer + { + private static final long serialVersionUID = 1L; + + final Method _factoryMethod; + + public StringFactoryKeyDeserializer(Method fm) { + super(-1, fm.getDeclaringClass()); + _factoryMethod = fm; + } + + @Override + public Object _parse(String key, DeserializationContext ctxt) throws Exception + { + return _factoryMethod.invoke(null, key); + } + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,94 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.KeyDeserializers; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.EnumResolver; + +/** + * Helper class used to contain simple/well-known key deserializers. + * Following kinds of Objects can be handled currently: + *

    + *
  • Primitive wrappers (Boolean, Byte, Char, Short, Integer, Float, Long, Double)
  • + *
  • Enums (usually not needed, since EnumMap doesn't call us)
  • + *
  • {@link java.util.Date}
  • + *
  • {@link java.util.Calendar}
  • + *
  • {@link java.util.UUID}
  • + *
  • {@link java.util.Locale}
  • + *
  • Anything with constructor that takes a single String arg + * (if not explicitly @JsonIgnore'd)
  • + *
  • Anything with {@code static T valueOf(String)} factory method + * (if not explicitly @JsonIgnore'd)
  • + *
+ */ +public class StdKeyDeserializers + implements KeyDeserializers, java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + public static KeyDeserializer constructEnumKeyDeserializer(EnumResolver enumResolver) { + return new StdKeyDeserializer.EnumKD(enumResolver, null); + } + + public static KeyDeserializer constructEnumKeyDeserializer(EnumResolver enumResolver, + AnnotatedMethod factory) { + return new StdKeyDeserializer.EnumKD(enumResolver, factory); + } + + public static KeyDeserializer constructDelegatingKeyDeserializer(DeserializationConfig config, + JavaType type, JsonDeserializer deser) + { + return new StdKeyDeserializer.DelegatingKD(type.getRawClass(), deser); + } + + public static KeyDeserializer findStringBasedKeyDeserializer(DeserializationConfig config, + JavaType type) + { + /* We don't need full deserialization information, just need to + * know creators. + */ + BeanDescription beanDesc = config.introspect(type); + // Ok, so: can we find T(String) constructor? + Constructor ctor = beanDesc.findSingleArgConstructor(String.class); + if (ctor != null) { + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(ctor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return new StdKeyDeserializer.StringCtorKeyDeserializer(ctor); + } + /* or if not, "static T valueOf(String)" (or equivalent marked + * with @JsonCreator annotation?) + */ + Method m = beanDesc.findFactoryMethod(String.class); + if (m != null){ + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(m, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return new StdKeyDeserializer.StringFactoryKeyDeserializer(m); + } + // nope, no such luck... + return null; + } + + /* + /********************************************************** + /* KeyDeserializers implementation + /********************************************************** + */ + + @Override + public KeyDeserializer findKeyDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException + { + Class raw = type.getRawClass(); + // 23-Apr-2013, tatu: Map primitive types, just in case one was given + if (raw.isPrimitive()) { + raw = ClassUtil.wrapperType(raw); + } + return StdKeyDeserializer.forType(raw); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdNodeBasedDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,87 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Convenience deserializer that may be used to deserialize values given an + * intermediate tree representation ({@link JsonNode}). + * Note that this is a slightly simplified alternative to {@link StdDelegatingDeserializer}). + * + * @param Target type of this deserializer; that is, type of values that + * input data is deserialized into. + * + * @since 2.5 + */ +public abstract class StdNodeBasedDeserializer + extends StdDeserializer + implements ResolvableDeserializer +{ + private static final long serialVersionUID = 1L; + + protected JsonDeserializer _treeDeserializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected StdNodeBasedDeserializer(JavaType targetType) { + super(targetType); + } + + protected StdNodeBasedDeserializer(Class targetType) { + super(targetType); + } + + /** + * "Copy-constructor" used when creating a modified copies, most often + * if sub-class implements {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}. + */ + protected StdNodeBasedDeserializer(StdNodeBasedDeserializer src) { + super(src); + _treeDeserializer = src._treeDeserializer; + } + + @Override + public void resolve(DeserializationContext ctxt) throws JsonMappingException { + _treeDeserializer = ctxt.findRootValueDeserializer(ctxt.constructType(JsonNode.class)); + } + + /* + /********************************************************** + /* Abstract methods for sub-classes + /********************************************************** + */ + + public abstract T convert(JsonNode root, DeserializationContext ctxt) throws IOException; + + /* + /********************************************************** + /* JsonDeserializer impl + /********************************************************** + */ + + @Override + public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + JsonNode n = (JsonNode) _treeDeserializer.deserialize(jp, ctxt); + return convert(n, ctxt); + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, + TypeDeserializer td) + throws IOException, JsonProcessingException + { + /* 19-Nov-2014, tatu: Quite likely we'd have some issues but... let's + * try, just in case. + */ + JsonNode n = (JsonNode) _treeDeserializer.deserializeWithType(jp, ctxt, td); + return convert(n, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Base class for deserializers that handle types that are serialized + * as JSON scalars (non-structured, i.e. non-Object, non-Array, values). + */ +public abstract class StdScalarDeserializer extends StdDeserializer +{ + private static final long serialVersionUID = 1L; + + protected StdScalarDeserializer(Class vc) { super(vc); } + protected StdScalarDeserializer(JavaType valueType) { super(valueType); } + + // since 2.5 + protected StdScalarDeserializer(StdScalarDeserializer src) { super(src); } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + return typeDeserializer.deserializeTypedFromScalar(jp, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,505 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; +import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; + +/** + * Default {@link ValueInstantiator} implementation, which supports + * Creator methods that can be indicated by standard Jackson + * annotations. + */ +@JacksonStdImpl +public class StdValueInstantiator + extends ValueInstantiator + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Type of values that are instantiated; used + * for error reporting purposes. + */ + protected final String _valueTypeDesc; + + // // // Default (no-args) construction + + /** + * Default (no-argument) constructor to use for instantiation + * (with {@link #createUsingDefault}) + */ + protected AnnotatedWithParams _defaultCreator; + + // // // With-args (property-based) construction + + protected AnnotatedWithParams _withArgsCreator; + protected SettableBeanProperty[] _constructorArguments; + + // // // Delegate construction + + protected JavaType _delegateType; + protected AnnotatedWithParams _delegateCreator; + protected SettableBeanProperty[] _delegateArguments; + + // // // Array delegate construction + + protected JavaType _arrayDelegateType; + protected AnnotatedWithParams _arrayDelegateCreator; + protected SettableBeanProperty[] _arrayDelegateArguments; + + // // // Scalar construction + + protected AnnotatedWithParams _fromStringCreator; + protected AnnotatedWithParams _fromIntCreator; + protected AnnotatedWithParams _fromLongCreator; + protected AnnotatedWithParams _fromDoubleCreator; + protected AnnotatedWithParams _fromBooleanCreator; + + // // // Incomplete creator + protected AnnotatedParameter _incompleteParameter; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public StdValueInstantiator(DeserializationConfig config, Class valueType) { + _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.getName(); + } + + public StdValueInstantiator(DeserializationConfig config, JavaType valueType) { + _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.toString(); + } + + /** + * Copy-constructor that sub-classes can use when creating new instances + * by fluent-style construction + */ + protected StdValueInstantiator(StdValueInstantiator src) + { + _valueTypeDesc = src._valueTypeDesc; + + _defaultCreator = src._defaultCreator; + + _constructorArguments = src._constructorArguments; + _withArgsCreator = src._withArgsCreator; + + _delegateType = src._delegateType; + _delegateCreator = src._delegateCreator; + _delegateArguments = src._delegateArguments; + + _arrayDelegateType = src._arrayDelegateType; + _arrayDelegateCreator = src._arrayDelegateCreator; + _arrayDelegateArguments = src._arrayDelegateArguments; + + _fromStringCreator = src._fromStringCreator; + _fromIntCreator = src._fromIntCreator; + _fromLongCreator = src._fromLongCreator; + _fromDoubleCreator = src._fromDoubleCreator; + _fromBooleanCreator = src._fromBooleanCreator; + } + + /** + * Method for setting properties related to instantiating values + * from JSON Object. We will choose basically only one approach (out of possible + * three), and clear other properties + */ + public void configureFromObjectSettings(AnnotatedWithParams defaultCreator, + AnnotatedWithParams delegateCreator, JavaType delegateType, SettableBeanProperty[] delegateArgs, + AnnotatedWithParams withArgsCreator, SettableBeanProperty[] constructorArgs) + { + _defaultCreator = defaultCreator; + _delegateCreator = delegateCreator; + _delegateType = delegateType; + _delegateArguments = delegateArgs; + _withArgsCreator = withArgsCreator; + _constructorArguments = constructorArgs; + } + + public void configureFromArraySettings( + AnnotatedWithParams arrayDelegateCreator, + JavaType arrayDelegateType, + SettableBeanProperty[] arrayDelegateArgs) + { + _arrayDelegateCreator = arrayDelegateCreator; + _arrayDelegateType = arrayDelegateType; + _arrayDelegateArguments = arrayDelegateArgs; + } + + public void configureFromStringCreator(AnnotatedWithParams creator) { + _fromStringCreator = creator; + } + + public void configureFromIntCreator(AnnotatedWithParams creator) { + _fromIntCreator = creator; + } + + public void configureFromLongCreator(AnnotatedWithParams creator) { + _fromLongCreator = creator; + } + + public void configureFromDoubleCreator(AnnotatedWithParams creator) { + _fromDoubleCreator = creator; + } + + public void configureFromBooleanCreator(AnnotatedWithParams creator) { + _fromBooleanCreator = creator; + } + + public void configureIncompleteParameter(AnnotatedParameter parameter) { + _incompleteParameter = parameter; + } + + /* + /********************************************************** + /* Public API implementation; metadata + /********************************************************** + */ + + @Override + public String getValueTypeDesc() { + return _valueTypeDesc; + } + + @Override + public boolean canCreateFromString() { + return (_fromStringCreator != null); + } + + @Override + public boolean canCreateFromInt() { + return (_fromIntCreator != null); + } + + @Override + public boolean canCreateFromLong() { + return (_fromLongCreator != null); + } + + @Override + public boolean canCreateFromDouble() { + return (_fromDoubleCreator != null); + } + + @Override + public boolean canCreateFromBoolean() { + return (_fromBooleanCreator != null); + } + + @Override + public boolean canCreateUsingDefault() { + return (_defaultCreator != null); + } + + @Override + public boolean canCreateUsingDelegate() { + return _delegateType != null; + } + + @Override + public boolean canCreateUsingArrayDelegate() { + return _arrayDelegateType != null; + } + + @Override + public boolean canCreateFromObjectWith() { + return (_withArgsCreator != null); + } + + @Override + public JavaType getDelegateType(DeserializationConfig config) { + return _delegateType; + } + + @Override + public JavaType getArrayDelegateType(DeserializationConfig config) { + return _arrayDelegateType; + } + + @Override + public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { + return _constructorArguments; + } + + /* + /********************************************************** + /* Public API implementation; instantiation from JSON Object + /********************************************************** + */ + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException + { + if (_defaultCreator == null) { // sanity-check; caller should check + throw new IllegalStateException("No default constructor for "+getValueTypeDesc()); + } + try { + return _defaultCreator.call(); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } + + @Override + public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException + { + if (_withArgsCreator == null) { // sanity-check; caller should check + throw new IllegalStateException("No with-args constructor for "+getValueTypeDesc()); + } + try { + return _withArgsCreator.call(args); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } + + @Override + public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException + { + return _createUsingDelegate(_delegateCreator, _delegateArguments, ctxt, delegate); + } + + @Override + public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException + { + if (_arrayDelegateCreator == null) { // sanity-check; caller should check + // fallback to the classic delegate creator + return createUsingDelegate(ctxt, delegate); + } + return _createUsingDelegate(_arrayDelegateCreator, _arrayDelegateArguments, ctxt, delegate); + } + + /* + /********************************************************** + /* Public API implementation; instantiation from JSON scalars + /********************************************************** + */ + + @Override + public Object createFromString(DeserializationContext ctxt, String value) throws IOException + { + if (_fromStringCreator == null) { + return _createFromStringFallbacks(ctxt, value); + } + try { + return _fromStringCreator.call1(value); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } + + @Override + public Object createFromInt(DeserializationContext ctxt, int value) throws IOException + { + try { + // First: "native" int methods work best: + if (_fromIntCreator != null) { + return _fromIntCreator.call1(Integer.valueOf(value)); + } + // but if not, can do widening conversion + if (_fromLongCreator != null) { + return _fromLongCreator.call1(Long.valueOf(value)); + } + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + throw ctxt.mappingException("Can not instantiate value of type %s from Integral number (%s); no single-int-arg constructor/factory method", + getValueTypeDesc(), value); + } + + @Override + public Object createFromLong(DeserializationContext ctxt, long value) throws IOException + { + if (_fromLongCreator == null) { + throw ctxt.mappingException("Can not instantiate value of type %s" + +" from Long integral number (%s); no single-long-arg constructor/factory method", + getValueTypeDesc(), value); + } + try { + return _fromLongCreator.call1(Long.valueOf(value)); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } + + @Override + public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException + { + if (_fromDoubleCreator == null) { + throw ctxt.mappingException("Can not instantiate value of type %s" + +" from Floating-point number (%s); no one-double/Double-arg constructor/factory method", + getValueTypeDesc(), value); + } + try { + return _fromDoubleCreator.call1(Double.valueOf(value)); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } + + @Override + public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException + { + if (_fromBooleanCreator == null) { + throw ctxt.mappingException("Can not instantiate value of type %s" + +" from Boolean value (%s); no single-boolean/Boolean-arg constructor/factory method", + getValueTypeDesc(), value); + } + try { + return _fromBooleanCreator.call1(Boolean.valueOf(value)); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } + + /* + /********************************************************** + /* Extended API: configuration mutators, accessors + /********************************************************** + */ + + @Override + public AnnotatedWithParams getDelegateCreator() { + return _delegateCreator; + } + + @Override + public AnnotatedWithParams getArrayDelegateCreator() { + return _arrayDelegateCreator; + } + + @Override + public AnnotatedWithParams getDefaultCreator() { + return _defaultCreator; + } + + @Override + public AnnotatedWithParams getWithArgsCreator() { + return _withArgsCreator; + } + + @Override + public AnnotatedParameter getIncompleteParameter() { + return _incompleteParameter; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * @deprecated Since 2.7 call either {@link #unwrapAndWrapException} or + * {@link #wrapAsJsonMappingException} + */ + @Deprecated // since 2.7 + protected JsonMappingException wrapException(Throwable t) + { + // 05-Nov-2015, tatu: This used to always unwrap the whole exception, but now only + // does so if and until `JsonMappingException` is found. + for (Throwable curr = t; curr != null; curr = curr.getCause()) { + if (curr instanceof JsonMappingException) { + return (JsonMappingException) curr; + } + } + return new JsonMappingException(null, + "Instantiation of "+getValueTypeDesc()+" value failed: "+t.getMessage(), t); + } + + /** + * @since 2.7 + */ + protected JsonMappingException unwrapAndWrapException(DeserializationContext ctxt, Throwable t) + { + // 05-Nov-2015, tatu: This used to always unwrap the whole exception, but now only + // does so if and until `JsonMappingException` is found. + for (Throwable curr = t; curr != null; curr = curr.getCause()) { + if (curr instanceof JsonMappingException) { + return (JsonMappingException) curr; + } + } + String msg = String.format("Instantiation of %s value failed (%s): %s", + getValueTypeDesc(), t.getClass().getName(), t.getMessage()); + return JsonMappingException.from(ctxt.getParser(), msg, t); + } + + /** + * @since 2.7 + */ + protected JsonMappingException wrapAsJsonMappingException(DeserializationContext ctxt, + Throwable t) + { + // 05-Nov-2015, tatu: Only avoid wrapping if already a JsonMappingException + if (t instanceof JsonMappingException) { + return (JsonMappingException) t; + } + String msg = String.format("Instantiation of %s value failed (%s): %s", + getValueTypeDesc(), t.getClass().getName(), t.getMessage()); + return JsonMappingException.from(ctxt.getParser(), msg, t); + } + + /** + * @since 2.7 + */ + protected JsonMappingException rewrapCtorProblem(DeserializationContext ctxt, + Throwable t) + { + // 05-Nov-2015, tatu: Seems like there are really only 2 useless wrapper errors/exceptions, + // so just peel those, and nothing else + if ((t instanceof ExceptionInInitializerError) // from static initialization block + || (t instanceof InvocationTargetException) // from constructor/method + ) { + Throwable cause = t.getCause(); + if (cause != null) { + t = cause; + } + } + return wrapAsJsonMappingException(ctxt, t); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + private Object _createUsingDelegate( + AnnotatedWithParams delegateCreator, + SettableBeanProperty[] delegateArguments, + DeserializationContext ctxt, + Object delegate) + throws IOException + { + if (delegateCreator == null) { // sanity-check; caller should check + throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc()); + } + try { + // First simple case: just delegate, no injectables + if (delegateArguments == null) { + return delegateCreator.call1(delegate); + } + // And then the case with at least one injectable... + final int len = delegateArguments.length; + Object[] args = new Object[len]; + for (int i = 0; i < len; ++i) { + SettableBeanProperty prop = delegateArguments[i]; + if (prop == null) { // delegate + args[i] = delegate; + } else { // nope, injectable: + args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null); + } + } + // and then try calling with full set of arguments + return delegateCreator.call(args); + } catch (Throwable t) { + throw rewrapCtorProblem(ctxt, t); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,189 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.util.ObjectBuffer; + +/** + * Separate implementation for serializing String arrays (instead of + * using {@link ObjectArrayDeserializer}. + * Used if (and only if) no custom value deserializers are used. + */ +@JacksonStdImpl +public final class StringArrayDeserializer + extends StdDeserializer + implements ContextualDeserializer +{ + private static final long serialVersionUID = 2L; + + public final static StringArrayDeserializer instance = new StringArrayDeserializer(); + + /** + * Value serializer to use, if not the standard one (which is inlined) + */ + protected JsonDeserializer _elementDeserializer; + + /** + * Specific override for this instance (from proper, or global per-type overrides) + * to indicate whether single value may be taken to mean an unwrapped one-element array + * or not. If null, left to global defaults. + * + * @since 2.7 + */ + protected final Boolean _unwrapSingle; + + public StringArrayDeserializer() { + this(null, null); + } + + @SuppressWarnings("unchecked") + protected StringArrayDeserializer(JsonDeserializer deser, Boolean unwrapSingle) { + super(String[].class); + _elementDeserializer = (JsonDeserializer) deser; + _unwrapSingle = unwrapSingle; + } + + /** + * Contextualization is needed to see whether we can "inline" deserialization + * of String values, or if we have to use separate value deserializer. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException + { + JsonDeserializer deser = _elementDeserializer; + // May have a content converter + deser = findConvertingContentDeserializer(ctxt, property, deser); + JavaType type = ctxt.constructType(String.class); + if (deser == null) { + deser = ctxt.findContextualValueDeserializer(type, property); + } else { // if directly assigned, probably not yet contextual, so: + deser = ctxt.handleSecondaryContextualization(deser, property, type); + } + // One more thing: allow unwrapping? + Boolean unwrapSingle = findFormatFeature(ctxt, property, String[].class, + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + // Ok ok: if all we got is the default String deserializer, can just forget about it + if ((deser != null) && isDefaultDeserializer(deser)) { + deser = null; + } + if ((_elementDeserializer == deser) && (_unwrapSingle == unwrapSingle)) { + return this; + } + return new StringArrayDeserializer(deser, unwrapSingle); + } + + @Override + public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // Ok: must point to START_ARRAY (or equivalent) + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt); + } + if (_elementDeserializer != null) { + return _deserializeCustom(p, ctxt); + } + + final ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] chunk = buffer.resetAndStart(); + + int ix = 0; + + try { + while (true) { + String value = p.nextTextValue(); + if (value == null) { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_ARRAY) { + break; + } + if (t != JsonToken.VALUE_NULL) { + value = _parseString(p, ctxt); + } + } + if (ix >= chunk.length) { + chunk = buffer.appendCompletedChunk(chunk); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix); + } + String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class); + ctxt.returnObjectBuffer(buffer); + return result; + } + + /** + * Offlined version used when we do not use the default deserialization method. + */ + protected final String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt) throws IOException + { + final ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] chunk = buffer.resetAndStart(); + final JsonDeserializer deser = _elementDeserializer; + + int ix = 0; + + try { + while (true) { + /* 30-Dec-2014, tatu: This may look odd, but let's actually call method + * that suggest we are expecting a String; this helps with some formats, + * notably XML. Note, however, that while we can get String, we can't + * assume that's what we use due to custom deserializer + */ + String value; + if (p.nextTextValue() == null) { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_ARRAY) { + break; + } + // Ok: no need to convert Strings, but must recognize nulls + value = (t == JsonToken.VALUE_NULL) ? deser.getNullValue(ctxt) : deser.deserialize(p, ctxt); + } else { + value = deser.deserialize(p, ctxt); + } + if (ix >= chunk.length) { + chunk = buffer.appendCompletedChunk(chunk); + ix = 0; + } + chunk[ix++] = value; + } + } catch (Exception e) { + // note: pass String.class, not String[].class, as we need element type for error info + throw JsonMappingException.wrapWithPath(e, String.class, ix); + } + String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class); + ctxt.returnObjectBuffer(buffer); + return result; + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + return typeDeserializer.deserializeTypedFromArray(p, ctxt); + } + + private final String[] handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException + { + // implicit arrays from single values? + boolean canWrap = (_unwrapSingle == Boolean.TRUE) || + ((_unwrapSingle == null) && + ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + if (canWrap) { + return new String[] { p.hasToken(JsonToken.VALUE_NULL) ? null : _parseString(p, ctxt) }; + } else if (p.hasToken(JsonToken.VALUE_STRING) + && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { + String str = p.getText(); + if (str.length() == 0) { + return null; + } + } + throw ctxt.mappingException(_valueClass); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,275 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +/** + * Specifically optimized version for {@link java.util.Collection}s + * that contain String values; reason is that this is a very common + * type and we can make use of the fact that Strings are final. + */ +@JacksonStdImpl +public final class StringCollectionDeserializer + extends ContainerDeserializerBase> + implements ContextualDeserializer +{ + private static final long serialVersionUID = 1L; + + // // Configuration + + protected final JavaType _collectionType; + + /** + * Value deserializer to use, if NOT the standard one + * (if it is, will be null). + */ + protected final JsonDeserializer _valueDeserializer; + + // // Instance construction settings: + + /** + * Instantiator used in case custom handling is needed for creation. + */ + protected final ValueInstantiator _valueInstantiator; + + /** + * Deserializer that is used iff delegate-based creator is + * to be used for deserializing from JSON Object. + */ + protected final JsonDeserializer _delegateDeserializer; + + /** + * Specific override for this instance (from proper, or global per-type overrides) + * to indicate whether single value may be taken to mean an unwrapped one-element array + * or not. If null, left to global defaults. + * + * @since 2.7 + */ + protected final Boolean _unwrapSingle; + + // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public StringCollectionDeserializer(JavaType collectionType, + JsonDeserializer valueDeser, ValueInstantiator valueInstantiator) + { + this(collectionType, valueInstantiator, null, valueDeser, null); + } + + @SuppressWarnings("unchecked") + protected StringCollectionDeserializer(JavaType collectionType, + ValueInstantiator valueInstantiator, JsonDeserializer delegateDeser, + JsonDeserializer valueDeser, Boolean unwrapSingle) + { + super(collectionType); + _collectionType = collectionType; + _valueDeserializer = (JsonDeserializer) valueDeser; + _valueInstantiator = valueInstantiator; + _delegateDeserializer = (JsonDeserializer) delegateDeser; + _unwrapSingle = unwrapSingle; + } + + protected StringCollectionDeserializer withResolved(JsonDeserializer delegateDeser, + JsonDeserializer valueDeser, Boolean unwrapSingle) + { + if ((_unwrapSingle == unwrapSingle) + && (_valueDeserializer == valueDeser) && (_delegateDeserializer == delegateDeser)) { + return this; + } + return new StringCollectionDeserializer(_collectionType, + _valueInstantiator, delegateDeser, valueDeser, unwrapSingle); + } + + @Override // since 2.5 + public boolean isCachable() { + // 26-Mar-2015, tatu: Important: prevent caching if custom deserializers are involved + return (_valueDeserializer == null) && (_delegateDeserializer == null); + } + + /* + /********************************************************** + /* Validation, post-processing + /********************************************************** + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + // May need to resolve types for delegate-based creators: + JsonDeserializer delegate = null; + if (_valueInstantiator != null) { + AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator(); + if (delegateCreator != null) { + JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig()); + delegate = findDeserializer(ctxt, delegateType, property); + } + } + JsonDeserializer valueDeser = _valueDeserializer; + final JavaType valueType = _collectionType.getContentType(); + if (valueDeser == null) { + // [databind#125]: May have a content converter + valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser); + if (valueDeser == null) { + // And we may also need to get deserializer for String + valueDeser = ctxt.findContextualValueDeserializer(valueType, property); + } + } else { // if directly assigned, probably not yet contextual, so: + valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, valueType); + } + // 11-Dec-2015, tatu: Should we pass basic `Collection.class`, or more refined? Mostly + // comes down to "List vs Collection" I suppose... for now, pass Collection + Boolean unwrapSingle = findFormatFeature(ctxt, property, Collection.class, + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + if (isDefaultDeserializer(valueDeser)) { + valueDeser = null; + } + return withResolved(delegate, valueDeser, unwrapSingle); + } + + /* + /********************************************************** + /* ContainerDeserializerBase API + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _collectionType.getContentType(); + } + + @SuppressWarnings("unchecked") + @Override + public JsonDeserializer getContentDeserializer() { + JsonDeserializer deser = _valueDeserializer; + return (JsonDeserializer) deser; + } + + /* + /********************************************************** + /* JsonDeserializer API + /********************************************************** + */ + + @SuppressWarnings("unchecked") + @Override + public Collection deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException + { + if (_delegateDeserializer != null) { + return (Collection) _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + final Collection result = (Collection) _valueInstantiator.createUsingDefault(ctxt); + return deserialize(p, ctxt, result); + } + + @Override + public Collection deserialize(JsonParser p, DeserializationContext ctxt, + Collection result) + throws IOException + { + // Ok: must point to START_ARRAY + if (!p.isExpectedStartArrayToken()) { + return handleNonArray(p, ctxt, result); + } + + if (_valueDeserializer != null) { + return deserializeUsingCustom(p, ctxt, result, _valueDeserializer); + } + try { + while (true) { + // First the common case: + String value = p.nextTextValue(); + if (value != null) { + result.add(value); + continue; + } + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_ARRAY) { + break; + } + if (t != JsonToken.VALUE_NULL) { + value = _parseString(p, ctxt); + } + result.add(value); + } + } catch (Exception e) { + throw JsonMappingException.wrapWithPath(e, result, result.size()); + } + return result; + } + + private Collection deserializeUsingCustom(JsonParser p, DeserializationContext ctxt, + Collection result, final JsonDeserializer deser) throws IOException + { + while (true) { + /* 30-Dec-2014, tatu: This may look odd, but let's actually call method + * that suggest we are expecting a String; this helps with some formats, + * notably XML. Note, however, that while we can get String, we can't + * assume that's what we use due to custom deserializer + */ + String value; + if (p.nextTextValue() == null) { + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.END_ARRAY) { + break; + } + // Ok: no need to convert Strings, but must recognize nulls + value = (t == JsonToken.VALUE_NULL) ? deser.getNullValue(ctxt) : deser.deserialize(p, ctxt); + } else { + value = deser.deserialize(p, ctxt); + } + result.add(value); + } + return result; + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + // In future could check current token... for now this should be enough: + return typeDeserializer.deserializeTypedFromArray(p, ctxt); + } + + /** + * Helper method called when current token is not START_ARRAY. Will either + * throw an exception, or try to handle value as if member of implicit + * array, depending on configuration. + */ + private final Collection handleNonArray(JsonParser p, DeserializationContext ctxt, Collection result) throws IOException + { + // implicit arrays from single values? + boolean canWrap = (_unwrapSingle == Boolean.TRUE) || + ((_unwrapSingle == null) && + ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)); + if (!canWrap) { + throw ctxt.mappingException(_collectionType.getRawClass()); + } + // Strings are one of "native" (intrinsic) types, so there's never type deserializer involved + JsonDeserializer valueDes = _valueDeserializer; + JsonToken t = p.getCurrentToken(); + + String value; + + if (t == JsonToken.VALUE_NULL) { + value = (valueDes == null) ? null : valueDes.getNullValue(ctxt); + } else { + value = (valueDes == null) ? _parseString(p, ctxt) : valueDes.deserialize(p, ctxt); + } + result.add(value); + return result; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,70 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; + +@JacksonStdImpl +public final class StringDeserializer extends StdScalarDeserializer +{ + private static final long serialVersionUID = 1L; + + /** + * @since 2.2 + */ + public final static StringDeserializer instance = new StringDeserializer(); + + public StringDeserializer() { super(String.class); } + + // since 2.6, slightly faster lookups for this very common type + @Override + public boolean isCachable() { return true; } + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (p.hasToken(JsonToken.VALUE_STRING)) { + return p.getText(); + } + JsonToken t = p.getCurrentToken(); + // [databind#381] + if ((t == JsonToken.START_ARRAY) && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + p.nextToken(); + final String parsed = _parseString(p, ctxt); + if (p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "Attempted to unwrap single value array for single 'String' value but there was more than a single value in the array"); + } + return parsed; + } + // need to gracefully handle byte[] data, as base64 + if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { + Object ob = p.getEmbeddedObject(); + if (ob == null) { + return null; + } + if (ob instanceof byte[]) { + return ctxt.getBase64Variant().encode((byte[]) ob, false); + } + // otherwise, try conversion using toString()... + return ob.toString(); + } + // allow coercions for other scalar types + String text = p.getValueAsString(); + if (text != null) { + return text; + } + throw ctxt.mappingException(_valueClass, p.getCurrentToken()); + } + + // Since we can never have type info ("natural type"; String, Boolean, Integer, Double): + // (is it an error to even call this version?) + @Override + public String deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { + return deserialize(p, ctxt); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,160 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.SettableBeanProperty; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Deserializer that builds on basic {@link BeanDeserializer} but + * override some aspects like instance construction. + */ +public class ThrowableDeserializer + extends BeanDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final static String PROP_NAME_MESSAGE = "message"; + + /* + /************************************************************ + /* Construction + /************************************************************ + */ + + public ThrowableDeserializer(BeanDeserializer baseDeserializer) { + super(baseDeserializer); + // need to disable this, since we do post-processing + _vanillaProcessing = false; + } + + /** + * Alternative constructor used when creating "unwrapping" deserializers + */ + protected ThrowableDeserializer(BeanDeserializer src, NameTransformer unwrapper) { + super(src, unwrapper); + } + + @Override + public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) { + if (getClass() != ThrowableDeserializer.class) { + return this; + } + /* main thing really is to just enforce ignoring of unknown + * properties; since there may be multiple unwrapped values + * and properties for all may be interleaved... + */ + return new ThrowableDeserializer(this, unwrapper); + } + + + /* + /************************************************************ + /* Overridden methods + /************************************************************ + */ + + @Override + public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException + { + // 30-Sep-2010, tatu: Need to allow use of @JsonCreator, so: + if (_propertyBasedCreator != null) { // proper @JsonCreator + return _deserializeUsingPropertyBased(p, ctxt); + } + if (_delegateDeserializer != null) { + return _valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt)); + } + if (_beanType.isAbstract()) { // for good measure, check this too + throw JsonMappingException.from(p, "Can not instantiate abstract type "+_beanType + +" (need to add/enable type information?)"); + } + boolean hasStringCreator = _valueInstantiator.canCreateFromString(); + boolean hasDefaultCtor = _valueInstantiator.canCreateUsingDefault(); + // and finally, verify we do have single-String arg constructor (if no @JsonCreator) + if (!hasStringCreator && !hasDefaultCtor) { + throw JsonMappingException.from(p,"Can not deserialize Throwable of type "+_beanType + +" without having a default contructor, a single-String-arg constructor; or explicit @JsonCreator"); + } + + Object throwable = null; + Object[] pending = null; + int pendingIx = 0; + + for (; p.getCurrentToken() != JsonToken.END_OBJECT; p.nextToken()) { + String propName = p.getCurrentName(); + SettableBeanProperty prop = _beanProperties.find(propName); + p.nextToken(); // to point to field value + + if (prop != null) { // normal case + if (throwable != null) { + prop.deserializeAndSet(p, ctxt, throwable); + continue; + } + // nope; need to defer + if (pending == null) { + int len = _beanProperties.size(); + pending = new Object[len + len]; + } + pending[pendingIx++] = prop; + pending[pendingIx++] = prop.deserialize(p, ctxt); + continue; + } + + // Maybe it's "message"? + if (PROP_NAME_MESSAGE.equals(propName)) { + if (hasStringCreator) { + throwable = _valueInstantiator.createFromString(ctxt, p.getText()); + // any pending values? + if (pending != null) { + for (int i = 0, len = pendingIx; i < len; i += 2) { + prop = (SettableBeanProperty)pending[i]; + prop.set(throwable, pending[i+1]); + } + pending = null; + } + continue; + } + } + /* As per [JACKSON-313], things marked as ignorable should not be + * passed to any setter + */ + if (_ignorableProps != null && _ignorableProps.contains(propName)) { + p.skipChildren(); + continue; + } + if (_anySetter != null) { + _anySetter.deserializeAndSet(p, ctxt, throwable, propName); + continue; + } + // Unknown: let's call handler method + handleUnknownProperty(p, ctxt, throwable, propName); + } + // Sanity check: did we find "message"? + if (throwable == null) { + /* 15-Oct-2010, tatu: Can't assume missing message is an error, since it may be + * suppressed during serialization, as per [JACKSON-388]. + * + * Should probably allow use of default constructor, too... + */ + //throw new JsonMappingException("No 'message' property found: could not deserialize "+_beanType); + if (hasStringCreator) { + throwable = _valueInstantiator.createFromString(ctxt, null); + } else { + throwable = _valueInstantiator.createUsingDefault(ctxt); + } + // any pending values? + if (pending != null) { + for (int i = 0, len = pendingIx; i < len; i += 2) { + SettableBeanProperty prop = (SettableBeanProperty)pending[i]; + prop.set(throwable, pending[i+1]); + } + } + } + return throwable; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/TokenBufferDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,37 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * We also want to directly support deserialization of {@link TokenBuffer}. + *

+ * Note that we use scalar deserializer base just because we claim + * to be of scalar for type information inclusion purposes; actual + * underlying content can be of any (Object, Array, scalar) type. + *

+ * Since 2.3, another important thing is that possible native ids + * (type id, object id) should be properly copied even when converting + * with {@link TokenBuffer}. Such ids are supported if (and only if!) + * source {@link JsonParser} supports them. + */ +@JacksonStdImpl +public class TokenBufferDeserializer extends StdScalarDeserializer { + private static final long serialVersionUID = 1L; + + public TokenBufferDeserializer() { super(TokenBuffer.class); } + + @Override + public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return createBufferInstance(p).deserialize(p, ctxt); + } + + protected TokenBuffer createBufferInstance(JsonParser p) { + return new TokenBuffer(p); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/UUIDDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,140 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.Arrays; +import java.util.UUID; + +import com.fasterxml.jackson.core.Base64Variants; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; + +public class UUIDDeserializer extends FromStringDeserializer +{ + private static final long serialVersionUID = 1L; + + final static int[] HEX_DIGITS = new int[127]; + static { + Arrays.fill(HEX_DIGITS, -1); + for (int i = 0; i < 10; ++i) { HEX_DIGITS['0' + i] = i; } + for (int i = 0; i < 6; ++i) { + HEX_DIGITS['a' + i] = 10 + i; + HEX_DIGITS['A' + i] = 10 + i; + } + } + + public UUIDDeserializer() { super(UUID.class); } + + @Override + protected UUID _deserialize(String id, DeserializationContext ctxt) throws IOException + { + // Adapted from java-uuid-generator (https://github.com/cowtowncoder/java-uuid-generator) + // which is 5x faster than UUID.fromString(value), as oper "ManualReadPerfWithUUID" + if (id.length() != 36) { + /* 14-Sep-2013, tatu: One trick we do allow, Base64-encoding, since we know + * length it must have... + */ + if (id.length() == 24) { + byte[] stuff = Base64Variants.getDefaultVariant().decode(id); + return _fromBytes(stuff, ctxt); + } + _badFormat(id, ctxt); + } + + // verify hyphens first: + if ((id.charAt(8) != '-') || (id.charAt(13) != '-') + || (id.charAt(18) != '-') || (id.charAt(23) != '-')) { + _badFormat(id, ctxt); + } + long l1 = intFromChars(id, 0, ctxt); + l1 <<= 32; + long l2 = ((long) shortFromChars(id, 9, ctxt)) << 16; + l2 |= shortFromChars(id, 14, ctxt); + long hi = l1 + l2; + + int i1 = (shortFromChars(id, 19, ctxt) << 16) | shortFromChars(id, 24, ctxt); + l1 = i1; + l1 <<= 32; + l2 = intFromChars(id, 28, ctxt); + l2 = (l2 << 32) >>> 32; // sign removal, Java-style. Ugh. + long lo = l1 | l2; + + return new UUID(hi, lo); + } + + @Override + protected UUID _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws IOException + { + if (ob instanceof byte[]) { + return _fromBytes((byte[]) ob, ctxt); + } + super._deserializeEmbedded(ob, ctxt); + return null; // never gets here + } + + private void _badFormat(String uuidStr, DeserializationContext ctxt) + throws JsonMappingException + { + throw InvalidFormatException.from(ctxt.getParser(), + String.format("UUID has to be represented by standard 36-char representation: input String '%s'", + uuidStr), + uuidStr, handledType()); + } + + static int intFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException { + return (byteFromChars(str, index, ctxt) << 24) + + (byteFromChars(str, index+2, ctxt) << 16) + + (byteFromChars(str, index+4, ctxt) << 8) + + byteFromChars(str, index+6, ctxt); + } + + static int shortFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException { + return (byteFromChars(str, index, ctxt) << 8) + byteFromChars(str, index+2, ctxt); + } + + static int byteFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException + { + final char c1 = str.charAt(index); + final char c2 = str.charAt(index+1); + + if (c1 <= 127 && c2 <= 127) { + int hex = (HEX_DIGITS[c1] << 4) | HEX_DIGITS[c2]; + if (hex >= 0) { + return hex; + } + } + if (c1 > 127 || HEX_DIGITS[c1] < 0) { + return _badChar(str, index, ctxt, c1); + } + return _badChar(str, index+1, ctxt, c2); + } + + static int _badChar(String uuidStr, int index, DeserializationContext ctxt, char c) throws JsonMappingException { + String msg = String.format( +"Non-hex character '%c' (value 0x%s), not valid for UUID String: input String '%s'", + c, Integer.toHexString(c), uuidStr); + throw InvalidFormatException.from(ctxt.getParser(), msg, uuidStr, UUID.class); + } + + private UUID _fromBytes(byte[] bytes, DeserializationContext ctxt) throws JsonMappingException { + if (bytes.length != 16) { + throw InvalidFormatException.from(ctxt.getParser(), + "Can only construct UUIDs from byte[16]; got "+bytes.length+" bytes", + bytes, handledType()); + } + return new UUID(_long(bytes, 0), _long(bytes, 8)); + } + + private static long _long(byte[] b, int offset) { + long l1 = ((long) _int(b, offset)) << 32; + long l2 = _int(b, offset+4); + // faster to just do it than check if it has sign + l2 = (l2 << 32) >>> 32; // to get rid of sign + return l1 | l2; + } + + private static int _int(byte[] b, int offset) { + return (b[offset] << 24) | ((b[offset+1] & 0xFF) << 16) | ((b[offset+2] & 0xFF) << 8) | (b[offset+3] & 0xFF); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,676 @@ +package com.fasterxml.jackson.databind.deser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.ObjectBuffer; + +/** + * Deserializer implementation that is used if it is necessary to bind content of + * "unknown" type; something declared as basic {@link java.lang.Object} + * (either explicitly, or due to type erasure). + * If so, "natural" mapping is used to convert JSON values to their natural + * Java object matches: JSON arrays to Java {@link java.util.List}s (or, if configured, + * Object[]), JSON objects to {@link java.util.Map}s, numbers to + * {@link java.lang.Number}s, booleans to {@link java.lang.Boolean}s and + * strings to {@link java.lang.String} (and nulls to nulls). + */ +@JacksonStdImpl +public class UntypedObjectDeserializer + extends StdDeserializer + implements ResolvableDeserializer, ContextualDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final static Object[] NO_OBJECTS = new Object[0]; + + /** + * @deprecated Since 2.3, construct a new instance, needs to be resolved + */ + @Deprecated + public final static UntypedObjectDeserializer instance = new UntypedObjectDeserializer(null, null); + + /* + /********************************************************** + /* Possible custom deserializer overrides we need to use + /********************************************************** + */ + + protected JsonDeserializer _mapDeserializer; + + protected JsonDeserializer _listDeserializer; + + protected JsonDeserializer _stringDeserializer; + + protected JsonDeserializer _numberDeserializer; + + /** + * If {@link java.util.List} has been mapped to non-default implementation, + * we'll store type here + * + * @since 2.6 + */ + protected JavaType _listType; + + /** + * If {@link java.util.Map} has been mapped to non-default implementation, + * we'll store type here + * + * @since 2.6 + */ + protected JavaType _mapType; + + /** + * @deprecated Since 2.6 use variant takes type arguments + */ + @Deprecated + public UntypedObjectDeserializer() { + this(null, null); + } + + public UntypedObjectDeserializer(JavaType listType, JavaType mapType) { + super(Object.class); + _listType = listType; + _mapType = mapType; + } + + @SuppressWarnings("unchecked") + public UntypedObjectDeserializer(UntypedObjectDeserializer base, + JsonDeserializer mapDeser, JsonDeserializer listDeser, + JsonDeserializer stringDeser, JsonDeserializer numberDeser) + { + super(Object.class); + _mapDeserializer = (JsonDeserializer) mapDeser; + _listDeserializer = (JsonDeserializer) listDeser; + _stringDeserializer = (JsonDeserializer) stringDeser; + _numberDeserializer = (JsonDeserializer) numberDeser; + _listType = base._listType; + _mapType = base._mapType; + } + + /* + /********************************************************** + /* Initialization + /********************************************************** + */ + + /** + * We need to implement this method to properly find things to delegate + * to: it can not be done earlier since delegated deserializers almost + * certainly require access to this instance (at least "List" and "Map" ones) + */ + @SuppressWarnings("unchecked") + @Override + public void resolve(DeserializationContext ctxt) throws JsonMappingException + { + JavaType obType = ctxt.constructType(Object.class); + JavaType stringType = ctxt.constructType(String.class); + TypeFactory tf = ctxt.getTypeFactory(); + + /* 26-Nov-2014, tatu: This is highly unusual, as in general contextualization + * should always be called separately, from within "createContextual()". + * But this is a very singular deserializer since it operates on `Object` + * (and often for `?` type parameter), and as a result, easily and commonly + * results in cycles, being value deserializer for various Maps and Collections. + * Because of this, we must somehow break the cycles. This is done here by + * forcing pseudo-contextualization with null property. + */ + + // So: first find possible custom instances + if (_listType == null) { + _listDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructCollectionType(List.class, obType))); + } else { + // NOTE: if non-default List type, always consider to be non-standard deser + _listDeserializer = _findCustomDeser(ctxt, _listType); + } + if (_mapType == null) { + _mapDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructMapType(Map.class, stringType, obType))); + } else { + // NOTE: if non-default Map type, always consider to be non-standard deser + _mapDeserializer = _findCustomDeser(ctxt, _mapType); + } + _stringDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, stringType)); + _numberDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructType(Number.class))); + + // and then do bogus contextualization, in case custom ones need to resolve dependencies of + // their own + JavaType unknown = TypeFactory.unknownType(); + _mapDeserializer = (JsonDeserializer) ctxt.handleSecondaryContextualization(_mapDeserializer, null, unknown); + _listDeserializer = (JsonDeserializer) ctxt.handleSecondaryContextualization(_listDeserializer, null, unknown); + _stringDeserializer = (JsonDeserializer) ctxt.handleSecondaryContextualization(_stringDeserializer, null, unknown); + _numberDeserializer = (JsonDeserializer) ctxt.handleSecondaryContextualization(_numberDeserializer, null, unknown); + } + + protected JsonDeserializer _findCustomDeser(DeserializationContext ctxt, JavaType type) + throws JsonMappingException + { + // Since we are calling from `resolve`, we should NOT try to contextualize yet; + // contextualization will only occur at a later point + return ctxt.findNonContextualValueDeserializer(type); + } + + protected JsonDeserializer _clearIfStdImpl(JsonDeserializer deser) { + return ClassUtil.isJacksonStdImpl(deser) ? null : deser; + } + + /** + * We only use contextualization for optimizing the case where no customization + * occurred; if so, can slip in a more streamlined version. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) throws JsonMappingException + { + // 20-Apr-2014, tatu: If nothing custom, let's use "vanilla" instance, + // simpler and can avoid some of delegation + if ((_stringDeserializer == null) && (_numberDeserializer == null) + && (_mapDeserializer == null) && (_listDeserializer == null) + && getClass() == UntypedObjectDeserializer.class) { + return Vanilla.std; + } + return this; + } + + protected JsonDeserializer _withResolved(JsonDeserializer mapDeser, + JsonDeserializer listDeser, + JsonDeserializer stringDeser, JsonDeserializer numberDeser) { + return new UntypedObjectDeserializer(this, + mapDeser, listDeser, stringDeser, numberDeser); + } + + /* + /********************************************************** + /* Deserializer API + /********************************************************** + */ + + /* 07-Nov-2014, tatu: When investigating [databind#604], realized that it makes + * sense to also mark this is cachable, since lookup not exactly free, and + * since it's not uncommon to "read anything" + */ + @Override + public boolean isCachable() { + /* 26-Mar-2015, tatu: With respect to [databind#735], there are concerns over + * cachability. It seems like we SHOULD be safe here; but just in case there + * are problems with false sharing, this may need to be revisited. + */ + return true; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_START_OBJECT: + case JsonTokenId.ID_FIELD_NAME: + // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME), + // if caller has advanced to the first token of Object, but for empty Object + case JsonTokenId.ID_END_OBJECT: + if (_mapDeserializer != null) { + return _mapDeserializer.deserialize(p, ctxt); + } + return mapObject(p, ctxt); + case JsonTokenId.ID_START_ARRAY: + if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { + return mapArrayToArray(p, ctxt); + } + if (_listDeserializer != null) { + return _listDeserializer.deserialize(p, ctxt); + } + return mapArray(p, ctxt); + case JsonTokenId.ID_EMBEDDED_OBJECT: + return p.getEmbeddedObject(); + case JsonTokenId.ID_STRING: + if (_stringDeserializer != null) { + return _stringDeserializer.deserialize(p, ctxt); + } + return p.getText(); + + case JsonTokenId.ID_NUMBER_INT: + if (_numberDeserializer != null) { + return _numberDeserializer.deserialize(p, ctxt); + } + /* Caller may want to get all integral values returned as {@link java.math.BigInteger}, + * or {@link java.lang.Long} for consistency + */ + if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { + return _coerceIntegral(p, ctxt); + } + return p.getNumberValue(); // should be optimal, whatever it is + + case JsonTokenId.ID_NUMBER_FLOAT: + if (_numberDeserializer != null) { + return _numberDeserializer.deserialize(p, ctxt); + } + /* [JACKSON-72]: need to allow overriding the behavior regarding + * which type to use + */ + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return p.getDecimalValue(); + } + return p.getDoubleValue(); + + case JsonTokenId.ID_TRUE: + return Boolean.TRUE; + case JsonTokenId.ID_FALSE: + return Boolean.FALSE; + + case JsonTokenId.ID_NULL: // should not get this but... + return null; + +// case JsonTokenId.ID_END_ARRAY: // invalid + default: + } + throw ctxt.mappingException(Object.class); + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException + { + switch (p.getCurrentTokenId()) { + // First: does it look like we had type id wrapping of some kind? + case JsonTokenId.ID_START_ARRAY: + case JsonTokenId.ID_START_OBJECT: + case JsonTokenId.ID_FIELD_NAME: + /* Output can be as JSON Object, Array or scalar: no way to know + * a this point: + */ + return typeDeserializer.deserializeTypedFromAny(p, ctxt); + + case JsonTokenId.ID_EMBEDDED_OBJECT: + return p.getEmbeddedObject(); + + /* Otherwise we probably got a "native" type (ones that map + * naturally and thus do not need or use type ids) + */ + case JsonTokenId.ID_STRING: + if (_stringDeserializer != null) { + return _stringDeserializer.deserialize(p, ctxt); + } + return p.getText(); + + case JsonTokenId.ID_NUMBER_INT: + if (_numberDeserializer != null) { + return _numberDeserializer.deserialize(p, ctxt); + } + // May need coercion to "bigger" types: + if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { + return _coerceIntegral(p, ctxt); + } + return p.getNumberValue(); // should be optimal, whatever it is + + case JsonTokenId.ID_NUMBER_FLOAT: + if (_numberDeserializer != null) { + return _numberDeserializer.deserialize(p, ctxt); + } + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return p.getDecimalValue(); + } + return Double.valueOf(p.getDoubleValue()); + + case JsonTokenId.ID_TRUE: + return Boolean.TRUE; + case JsonTokenId.ID_FALSE: + return Boolean.FALSE; + + case JsonTokenId.ID_NULL: // should not get this far really but... + return null; + default: + } + throw ctxt.mappingException(Object.class); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * Method called to map a JSON Array into a Java value. + */ + protected Object mapArray(JsonParser jp, DeserializationContext ctxt) throws IOException + { + // Minor optimization to handle small lists (default size for ArrayList is 10) + if (jp.nextToken() == JsonToken.END_ARRAY) { + return new ArrayList(2); + } + Object value = deserialize(jp, ctxt); + if (jp.nextToken() == JsonToken.END_ARRAY) { + ArrayList l = new ArrayList(2); + l.add(value); + return l; + } + Object value2 = deserialize(jp, ctxt); + if (jp.nextToken() == JsonToken.END_ARRAY) { + ArrayList l = new ArrayList(2); + l.add(value); + l.add(value2); + return l; + } + ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] values = buffer.resetAndStart(); + int ptr = 0; + values[ptr++] = value; + values[ptr++] = value2; + int totalSize = ptr; + do { + value = deserialize(jp, ctxt); + ++totalSize; + if (ptr >= values.length) { + values = buffer.appendCompletedChunk(values); + ptr = 0; + } + values[ptr++] = value; + } while (jp.nextToken() != JsonToken.END_ARRAY); + // let's create full array then + ArrayList result = new ArrayList(totalSize); + buffer.completeAndClearBuffer(values, ptr, result); + return result; + } + + /** + * Method called to map a JSON Object into a Java value. + */ + protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOException + { + String key1; + + JsonToken t = p.getCurrentToken(); + + if (t == JsonToken.START_OBJECT) { + key1 = p.nextFieldName(); + } else if (t == JsonToken.FIELD_NAME) { + key1 = p.getCurrentName(); + } else { + if (t != JsonToken.END_OBJECT) { + throw ctxt.mappingException(handledType(), p.getCurrentToken()); + } + key1 = null; + } + if (key1 == null) { + // empty map might work; but caller may want to modify... so better just give small modifiable + return new LinkedHashMap(2); + } + // minor optimization; let's handle 1 and 2 entry cases separately + // 24-Mar-2015, tatu: Ideally, could use one of 'nextXxx()' methods, but for + // that we'd need new method(s) in JsonDeserializer. So not quite yet. + p.nextToken(); + Object value1 = deserialize(p, ctxt); + + String key2 = p.nextFieldName(); + if (key2 == null) { // has to be END_OBJECT, then + // single entry; but we want modifiable + LinkedHashMap result = new LinkedHashMap(2); + result.put(key1, value1); + return result; + } + p.nextToken(); + Object value2 = deserialize(p, ctxt); + + String key = p.nextFieldName(); + + if (key == null) { + LinkedHashMap result = new LinkedHashMap(4); + result.put(key1, value1); + result.put(key2, value2); + return result; + } + // And then the general case; default map size is 16 + LinkedHashMap result = new LinkedHashMap(); + result.put(key1, value1); + result.put(key2, value2); + + do { + p.nextToken(); + result.put(key, deserialize(p, ctxt)); + } while ((key = p.nextFieldName()) != null); + return result; + } + + /** + * Method called to map a JSON Array into a Java Object array (Object[]). + */ + protected Object[] mapArrayToArray(JsonParser jp, DeserializationContext ctxt) throws IOException + { + // Minor optimization to handle small lists (default size for ArrayList is 10) + if (jp.nextToken() == JsonToken.END_ARRAY) { + return NO_OBJECTS; + } + ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] values = buffer.resetAndStart(); + int ptr = 0; + do { + Object value = deserialize(jp, ctxt); + if (ptr >= values.length) { + values = buffer.appendCompletedChunk(values); + ptr = 0; + } + values[ptr++] = value; + } while (jp.nextToken() != JsonToken.END_ARRAY); + return buffer.completeAndClearBuffer(values, ptr); + } + + /* + /********************************************************** + /* Separate "vanilla" implementation for common case of + /* no custom deserializer overrides + /********************************************************** + */ + + @JacksonStdImpl + public static class Vanilla + extends StdDeserializer + { + private static final long serialVersionUID = 1L; + + public final static Vanilla std = new Vanilla(); + + public Vanilla() { super(Object.class); } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_START_OBJECT: + { + JsonToken t = p.nextToken(); + if (t == JsonToken.END_OBJECT) { + return new LinkedHashMap(2); + } + } + case JsonTokenId.ID_FIELD_NAME: + return mapObject(p, ctxt); + case JsonTokenId.ID_START_ARRAY: + { + JsonToken t = p.nextToken(); + if (t == JsonToken.END_ARRAY) { // and empty one too + if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { + return NO_OBJECTS; + } + return new ArrayList(2); + } + } + if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { + return mapArrayToArray(p, ctxt); + } + return mapArray(p, ctxt); + case JsonTokenId.ID_EMBEDDED_OBJECT: + return p.getEmbeddedObject(); + case JsonTokenId.ID_STRING: + return p.getText(); + + case JsonTokenId.ID_NUMBER_INT: + if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { + return _coerceIntegral(p, ctxt); + } + return p.getNumberValue(); // should be optimal, whatever it is + + case JsonTokenId.ID_NUMBER_FLOAT: + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return p.getDecimalValue(); + } + return Double.valueOf(p.getDoubleValue()); + + case JsonTokenId.ID_TRUE: + return Boolean.TRUE; + case JsonTokenId.ID_FALSE: + return Boolean.FALSE; + + case JsonTokenId.ID_NULL: // should not get this but... + return null; + + case JsonTokenId.ID_END_OBJECT: + // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME), + // if caller has advanced to the first token of Object, but for empty Object + return new LinkedHashMap(2); + + //case JsonTokenId.ID_END_ARRAY: // invalid + default: + throw ctxt.mappingException(Object.class); + } + } + + @Override + public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException + { + switch (jp.getCurrentTokenId()) { + case JsonTokenId.ID_START_ARRAY: + case JsonTokenId.ID_START_OBJECT: + case JsonTokenId.ID_FIELD_NAME: + return typeDeserializer.deserializeTypedFromAny(jp, ctxt); + + case JsonTokenId.ID_STRING: + return jp.getText(); + + case JsonTokenId.ID_NUMBER_INT: + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) { + return jp.getBigIntegerValue(); + } + return jp.getNumberValue(); + + case JsonTokenId.ID_NUMBER_FLOAT: + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return jp.getDecimalValue(); + } + return Double.valueOf(jp.getDoubleValue()); + + case JsonTokenId.ID_TRUE: + return Boolean.TRUE; + case JsonTokenId.ID_FALSE: + return Boolean.FALSE; + case JsonTokenId.ID_EMBEDDED_OBJECT: + return jp.getEmbeddedObject(); + + case JsonTokenId.ID_NULL: // should not get this far really but... + return null; + default: + throw ctxt.mappingException(Object.class); + } + } + + protected Object mapArray(JsonParser jp, DeserializationContext ctxt) throws IOException + { + Object value = deserialize(jp, ctxt); + if (jp.nextToken() == JsonToken.END_ARRAY) { + ArrayList l = new ArrayList(2); + l.add(value); + return l; + } + Object value2 = deserialize(jp, ctxt); + if (jp.nextToken() == JsonToken.END_ARRAY) { + ArrayList l = new ArrayList(2); + l.add(value); + l.add(value2); + return l; + } + ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] values = buffer.resetAndStart(); + int ptr = 0; + values[ptr++] = value; + values[ptr++] = value2; + int totalSize = ptr; + do { + value = deserialize(jp, ctxt); + ++totalSize; + if (ptr >= values.length) { + values = buffer.appendCompletedChunk(values); + ptr = 0; + } + values[ptr++] = value; + } while (jp.nextToken() != JsonToken.END_ARRAY); + // let's create full array then + ArrayList result = new ArrayList(totalSize); + buffer.completeAndClearBuffer(values, ptr, result); + return result; + } + + /** + * Method called to map a JSON Object into a Java value. + */ + protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOException + { + // will point to FIELD_NAME at this point, guaranteed + String key1 = p.getText(); + p.nextToken(); + Object value1 = deserialize(p, ctxt); + + String key2 = p.nextFieldName(); + if (key2 == null) { // single entry; but we want modifiable + LinkedHashMap result = new LinkedHashMap(2); + result.put(key1, value1); + return result; + } + p.nextToken(); + Object value2 = deserialize(p, ctxt); + + String key = p.nextFieldName(); + if (key == null) { + LinkedHashMap result = new LinkedHashMap(4); + result.put(key1, value1); + result.put(key2, value2); + return result; + } + // And then the general case; default map size is 16 + LinkedHashMap result = new LinkedHashMap(); + result.put(key1, value1); + result.put(key2, value2); + do { + p.nextToken(); + result.put(key, deserialize(p, ctxt)); + } while ((key = p.nextFieldName()) != null); + return result; + } + + /** + * Method called to map a JSON Array into a Java Object array (Object[]). + */ + protected Object[] mapArrayToArray(JsonParser jp, DeserializationContext ctxt) throws IOException { + ObjectBuffer buffer = ctxt.leaseObjectBuffer(); + Object[] values = buffer.resetAndStart(); + int ptr = 0; + do { + Object value = deserialize(jp, ctxt); + if (ptr >= values.length) { + values = buffer.appendCompletedChunk(values); + ptr = 0; + } + values[ptr++] = value; + } while (jp.nextToken() != JsonToken.END_ARRAY); + return buffer.completeAndClearBuffer(values, ptr); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/deser/std/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,13 @@ +/** + * Contains public standard implementations of abstraction that + * Jackson uses. This means that they are not merely implementation + * details, but part of semi-public interface where project + * tries to maintain backwards compatibility at higher level + * than for 'impl' types (although less so than with fully + * public interfaces). + *

+ * Note that since this package was only added relatively late + * in development cycle, not all classes that belong here are + * included. Plan is to move more classes over time. + */ +package com.fasterxml.jackson.databind.deser.std; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/IgnoredPropertyException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,74 @@ +package com.fasterxml.jackson.databind.exc; + +import java.util.*; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * Specialized {@link JsonMappingException} sub-class used to indicate + * case where an explicitly ignored property is encountered, and mapper + * is configured to consider this an error. + * + * @since 2.3 + */ +public class IgnoredPropertyException + extends PropertyBindingException +{ + private static final long serialVersionUID = 1L; + + /** + * @since 2.7 + */ + public IgnoredPropertyException(JsonParser p, String msg, JsonLocation loc, + Class referringClass, String propName, + Collection propertyIds) + { + super(p, msg, loc, referringClass, propName, propertyIds); + } + + /** + * @deprecated Since 2.7 + */ + @Deprecated + public IgnoredPropertyException(String msg, JsonLocation loc, + Class referringClass, String propName, + Collection propertyIds) + { + super(msg, loc, referringClass, propName, propertyIds); + } + + + /** + * Factory method used for constructing instances of this exception type. + * + * @param p Underlying parser used for reading input being used for data-binding + * @param fromObjectOrClass Reference to either instance of problematic type ( + * if available), or if not, type itself + * @param propertyName Name of unrecognized property + * @param propertyIds (optional, null if not available) Set of properties that + * type would recognize, if completely known: null if set can not be determined. + */ + public static IgnoredPropertyException from(JsonParser p, + Object fromObjectOrClass, String propertyName, + Collection propertyIds) + { + if (fromObjectOrClass == null) { + throw new IllegalArgumentException(); + } + Class ref; + if (fromObjectOrClass instanceof Class) { + ref = (Class) fromObjectOrClass; + } else { + ref = fromObjectOrClass.getClass(); + } + String msg = "Ignored field \""+propertyName+"\" (class "+ref.getName() + +") encountered; mapper configured not to allow this"; + IgnoredPropertyException e = new IgnoredPropertyException(p, msg, + p.getCurrentLocation(), ref, propertyName, propertyIds); + // but let's also ensure path includes this last (missing) segment + e.prependPath(fromObjectOrClass, propertyName); + return e; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/InvalidFormatException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/InvalidFormatException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/InvalidFormatException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,102 @@ +package com.fasterxml.jackson.databind.exc; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * Specialized sub-class of {@link JsonMappingException} + * that is used when the underlying problem appears to be that + * of bad formatting of a value to deserialize. + * + * @since 2.1 + */ +public class InvalidFormatException extends JsonMappingException +{ + private static final long serialVersionUID = 1L; // silly Eclipse, warnings + + /** + * Underlying value that could not be deserialized into + * target type, if available. + */ + protected final Object _value; + + /** + * Intended target type (type-erased class) that value could not + * be deserialized into, if known. + */ + protected final Class _targetType; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * @deprecated Since 2.7 Use variant that takes {@link JsonParser} + */ + @Deprecated // since 2.7 + public InvalidFormatException(String msg, + Object value, Class targetType) + { + super(null, msg); + _value = value; + _targetType = targetType; + } + + /** + * @deprecated Since 2.7 Use variant that takes {@link JsonParser} + */ + @Deprecated // since 2.7 + public InvalidFormatException(String msg, JsonLocation loc, + Object value, Class targetType) + { + super(null, msg, loc); + _value = value; + _targetType = targetType; + } + + /** + * @since 2.7 + */ + public InvalidFormatException(JsonParser p, + String msg, Object value, Class targetType) + { + super(p, msg); + _value = value; + _targetType = targetType; + } + + public static InvalidFormatException from(JsonParser p, String msg, + Object value, Class targetType) + { + return new InvalidFormatException(p, msg, value, targetType); + } + + /* + /********************************************************** + /* Additional accessors + /********************************************************** + */ + + /** + * Accessor for checking source value (String, Number usually) that could not + * be deserialized into target type ({@link #getTargetType}). + * Note that value may not be available, depending on who throws the exception + * and when. + */ + public Object getValue() { + return _value; + } + + /** + * Accessor for checking target type of value ({@link #getValue} that failed + * to deserialize. + * Note that type may not be available, depending on who throws the exception + * and when. + */ + public Class getTargetType() { + return _targetType; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/PropertyBindingException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/PropertyBindingException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/PropertyBindingException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,145 @@ +package com.fasterxml.jackson.databind.exc; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * Base class for {@link JsonMappingException}s that are specifically related + * to problems related to binding an individual property. + * + * @since 2.3 + */ +@SuppressWarnings("serial") +public abstract class PropertyBindingException + extends JsonMappingException +{ + /** + * Class that does not contain mapping for the unrecognized property. + */ + protected final Class _referringClass; + + /** + *

+ * Note: redundant information since it is also included in the + * reference path. + */ + protected final String _propertyName; + + /** + * Set of ids of properties that are known for the type, if this + * can be statically determined. + */ + protected final Collection _propertyIds; + + /** + * Lazily constructed description of known properties, used for + * constructing actual message if and as needed. + */ + protected transient String _propertiesAsString; + + /** + * @since 2.7 + */ + protected PropertyBindingException(JsonParser p, String msg, JsonLocation loc, + Class referringClass, String propName, + Collection propertyIds) + { + super(p, msg, loc); + _referringClass = referringClass; + _propertyName = propName; + _propertyIds = propertyIds; + } + + /** + * @deprecated Since 2.7 + */ + @Deprecated // since 2.7 + protected PropertyBindingException(String msg, JsonLocation loc, + Class referringClass, String propName, + Collection propertyIds) + { + this(null, msg, loc, referringClass, propName, propertyIds); + } + + /* + /********************************************************** + /* Overrides + /********************************************************** + */ + + /** + * Somewhat arbitrary limit, but let's try not to create uselessly + * huge error messages + */ + private final static int MAX_DESC_LENGTH = 1000; + + @Override + public String getMessageSuffix() + { + String suffix = _propertiesAsString; + if (suffix == null && _propertyIds != null) { + StringBuilder sb = new StringBuilder(100); + int len = _propertyIds.size(); + if (len == 1) { + sb.append(" (one known property: \""); + sb.append(String.valueOf(_propertyIds.iterator().next())); + sb.append('"'); + } else { + sb.append(" (").append(len).append(" known properties: "); + Iterator it = _propertyIds.iterator(); + while (it.hasNext()) { + sb.append('"'); + sb.append(String.valueOf(it.next())); + sb.append('"'); + // one other thing: limit max length + if (sb.length() > MAX_DESC_LENGTH) { + sb.append(" [truncated]"); + break; + } + if (it.hasNext()) { + sb.append(", "); + } + } + } + sb.append("])"); + _propertiesAsString = suffix = sb.toString(); + } + return suffix; + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Method for accessing type (class) that is missing definition to allow + * binding of the unrecognized property. + */ + public Class getReferringClass() { + return _referringClass; + } + + /** + * Convenience method for accessing logical property name that could + * not be mapped. Note that it is the last path reference in the + * underlying path. + */ + public String getPropertyName() { + return _propertyName; + } + + public Collection getKnownPropertyIds() + { + if (_propertyIds == null) { + return null; + } + return Collections.unmodifiableCollection(_propertyIds); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,68 @@ +package com.fasterxml.jackson.databind.exc; + +import java.util.*; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * Specialized {@link JsonMappingException} sub-class specifically used + * to indicate problems due to encountering a JSON property that could + * not be mapped to an Object property (via getter, constructor argument + * or field). + */ +public class UnrecognizedPropertyException + extends PropertyBindingException +{ + private static final long serialVersionUID = 1L; + + public UnrecognizedPropertyException(JsonParser p, String msg, JsonLocation loc, + Class referringClass, String propName, + Collection propertyIds) + { + super(p, msg, loc, referringClass, propName, propertyIds); + } + + /** + * @deprecated Since 2.7 + */ + @Deprecated // since 2.7 + public UnrecognizedPropertyException(String msg, JsonLocation loc, + Class referringClass, String propName, + Collection propertyIds) + { + super(msg, loc, referringClass, propName, propertyIds); + } + + /** + * Factory method used for constructing instances of this exception type. + * + * @param p Underlying parser used for reading input being used for data-binding + * @param fromObjectOrClass Reference to either instance of problematic type ( + * if available), or if not, type itself + * @param propertyName Name of unrecognized property + * @param propertyIds (optional, null if not available) Set of properties that + * type would recognize, if completely known: null if set can not be determined. + */ + public static UnrecognizedPropertyException from(JsonParser p, + Object fromObjectOrClass, String propertyName, + Collection propertyIds) + { + if (fromObjectOrClass == null) { + throw new IllegalArgumentException(); + } + Class ref; + if (fromObjectOrClass instanceof Class) { + ref = (Class) fromObjectOrClass; + } else { + ref = fromObjectOrClass.getClass(); + } + String msg = "Unrecognized field \""+propertyName+"\" (class "+ref.getName()+"), not marked as ignorable"; + UnrecognizedPropertyException e = new UnrecognizedPropertyException(p, msg, + p.getCurrentLocation(), ref, propertyName, propertyIds); + // but let's also ensure path includes this last (missing) segment + e.prependPath(fromObjectOrClass, propertyName); + return e; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,115 @@ +package com.fasterxml.jackson.databind.ext; + +import java.io.IOException; +import java.util.*; + +import javax.xml.datatype.*; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.Deserializers; +import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer; + +/** + * Container deserializers that handle "core" XML types: ones included in standard + * JDK 1.5. Types are directly needed by JAXB, but may be unavailable on some + * limited platforms; hence separate out from basic deserializer factory. + */ +public class CoreXMLDeserializers extends Deserializers.Base +{ + /** + * Data type factories are thread-safe after instantiation (and + * configuration, if any); and since instantion (esp. implementation + * introspection) can be expensive we better reuse the instance. + */ + final static DatatypeFactory _dataTypeFactory; + static { + try { + _dataTypeFactory = DatatypeFactory.newInstance(); + } catch (DatatypeConfigurationException e) { + throw new RuntimeException(e); + } + } + + @Override + public JsonDeserializer findBeanDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) + { + Class raw = type.getRawClass(); + if (raw == QName.class) { + return new Std(raw, TYPE_QNAME); + } + if (raw == XMLGregorianCalendar.class) { + return new Std(raw, TYPE_G_CALENDAR); + } + if (raw == Duration.class) { + return new Std(raw, TYPE_DURATION); + } + return null; + } + + /* + /********************************************************** + /* Concrete deserializers + /********************************************************** + */ + + protected final static int TYPE_DURATION = 1; + protected final static int TYPE_G_CALENDAR = 2; + protected final static int TYPE_QNAME = 3; + + /** + * Combo-deserializer that supports deserialization of somewhat optional + * javax.xml types {@link QName}, {@link Duration} and {@link XMLGregorianCalendar}. + * Combined into a single class to eliminate bunch of one-off implementation + * classes, to reduce resulting jar size (mostly). + * + * @since 2.4 + */ + public static class Std extends FromStringDeserializer + { + private static final long serialVersionUID = 1L; + + protected final int _kind; + + public Std(Class raw, int kind) { + super(raw); + _kind = kind; + } + + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + // For most types, use super impl; but not for GregorianCalendar + if (_kind == TYPE_G_CALENDAR) { + Date d = _parseDate(jp, ctxt); + if (d == null) { + return null; + } + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(d); + TimeZone tz = ctxt.getTimeZone(); + if (tz != null) { + calendar.setTimeZone(tz); + } + return _dataTypeFactory.newXMLGregorianCalendar(calendar); + } + return super.deserialize(jp, ctxt); + } + + @Override + protected Object _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException + { + switch (_kind) { + case TYPE_DURATION: + return _dataTypeFactory.newDuration(value); + case TYPE_QNAME: + return QName.valueOf(value); + } + throw new IllegalStateException(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,106 @@ +package com.fasterxml.jackson.databind.ext; + +import java.io.IOException; +import java.util.Calendar; + +import javax.xml.datatype.Duration; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.ser.std.CalendarSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; + +/** + * Provider for serializers of XML types that are part of full JDK 1.5, but + * that some alleged 1.5 platforms are missing (Android, GAE). + * And for this reason these are added using more dynamic mechanism. + *

+ * Note: since many of classes defined are abstract, caller must take + * care not to just use straight equivalency check but rather consider + * subclassing as well. + */ +public class CoreXMLSerializers extends Serializers.Base +{ + @Override + public JsonSerializer findSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc) + { + Class raw = type.getRawClass(); + if (Duration.class.isAssignableFrom(raw) || QName.class.isAssignableFrom(raw)) { + return ToStringSerializer.instance; + } + if (XMLGregorianCalendar.class.isAssignableFrom(raw)) { + return XMLGregorianCalendarSerializer.instance; + } + return null; + } + + @SuppressWarnings("serial") + public static class XMLGregorianCalendarSerializer + extends StdSerializer + implements ContextualSerializer + { + final static XMLGregorianCalendarSerializer instance = new XMLGregorianCalendarSerializer(); + + final JsonSerializer _delegate; + + public XMLGregorianCalendarSerializer() { + this(CalendarSerializer.instance); + } + + @SuppressWarnings("unchecked") + protected XMLGregorianCalendarSerializer(JsonSerializer del) { + super(XMLGregorianCalendar.class); + _delegate = (JsonSerializer) del; + } + + @Override + public JsonSerializer getDelegatee() { + return _delegate; + } + + @Override + public boolean isEmpty(SerializerProvider provider, XMLGregorianCalendar value) { + return _delegate.isEmpty(provider, _convert(value)); + } + + @Override + public void serialize(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + _delegate.serialize(_convert(value), gen, provider); + } + + @Override + public void serializeWithType(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + _delegate.serializeWithType(_convert(value), gen, provider, typeSer); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + _delegate.acceptJsonFormatVisitor(visitor, null); + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) + throws JsonMappingException { + JsonSerializer ser = prov.handlePrimaryContextualization(_delegate, property); + if (ser != _delegate) { + return new XMLGregorianCalendarSerializer(ser); + } + return this; + } + + protected Calendar _convert(XMLGregorianCalendar input) { + return (input == null) ? null : input.toGregorianCalendar(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/DOMDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/DOMDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/DOMDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.ext; + +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer; + +/** + * Base for serializers that allows parsing DOM Documents from JSON Strings. + * Nominal type can be either {@link org.w3c.dom.Node} or + * {@link org.w3c.dom.Document}. + */ +public abstract class DOMDeserializer extends FromStringDeserializer +{ + private static final long serialVersionUID = 1L; + + private final static DocumentBuilderFactory _parserFactory; + static { + _parserFactory = DocumentBuilderFactory.newInstance(); + // yup, only cave men do XML without recognizing namespaces... + _parserFactory.setNamespaceAware(true); + } + + protected DOMDeserializer(Class cls) { super(cls); } + + @Override + public abstract T _deserialize(String value, DeserializationContext ctxt); + + protected final Document parse(String value) throws IllegalArgumentException { + try { + return _parserFactory.newDocumentBuilder().parse(new InputSource(new StringReader(value))); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to parse JSON String as XML: "+e.getMessage(), e); + } + } + + /* + /********************************************************** + /* Concrete deserializers + /********************************************************** + */ + + public static class NodeDeserializer extends DOMDeserializer { + private static final long serialVersionUID = 1L; + public NodeDeserializer() { super(Node.class); } + @Override + public Node _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException { + return parse(value); + } + } + + public static class DocumentDeserializer extends DOMDeserializer { + private static final long serialVersionUID = 1L; + public DocumentDeserializer() { super(Document.class); } + @Override + public Document _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException { + return parse(value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/DOMSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/DOMSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/DOMSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,53 @@ +package com.fasterxml.jackson.databind.ext; + +import java.io.IOException; + +import org.w3c.dom.Node; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +@SuppressWarnings("serial") +public class DOMSerializer extends StdSerializer +{ + protected final DOMImplementationLS _domImpl; + + public DOMSerializer() { + super(Node.class); + DOMImplementationRegistry registry; + try { + registry = DOMImplementationRegistry.newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Could not instantiate DOMImplementationRegistry: "+e.getMessage(), e); + } + _domImpl = (DOMImplementationLS)registry.getDOMImplementation("LS"); + } + + @Override + public void serialize(Node value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + if (_domImpl == null) throw new IllegalStateException("Could not find DOM LS"); + LSSerializer writer = _domImpl.createLSSerializer(); + jgen.writeString(writer.writeToString(value)); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) { + // Well... it is serialized as String + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + if (visitor != null) visitor.expectAnyFormat(typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,179 @@ +package com.fasterxml.jackson.databind.ext; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.Deserializers; +import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; + +/** + * Helper class used for isolating details of handling optional+external types + * (javax.xml classes) from standard factories that offer them. + *

+ * Note that 2.7 changed handling to slightly less dynamic, to avoid having to + * traverse class hierarchy, which turned to be a performance issue in + * certain cases. Since DOM classes are assumed to exist on all Java 1.6 + * environments (yes, even on Android/GAE), this part could be simplified by + * slightly less dynamic lookups. + *

+ * Also with 2.7 we are supporting JDK 1.7/Java 7 type(s). + */ +public class OptionalHandlerFactory implements java.io.Serializable +{ + private static final long serialVersionUID = 1; + + /* To make 2 main "optional" handler groups (javax.xml.stream) + * more dynamic, we better only figure out handlers completely dynamically, if and + * when they are needed. To do this we need to assume package prefixes. + */ + private final static String PACKAGE_PREFIX_JAVAX_XML = "javax.xml."; + + private final static String SERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLSerializers"; + private final static String DESERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLDeserializers"; + + // Plus we also have a single serializer for DOM Node: +// private final static String CLASS_NAME_DOM_NODE = "org.w3c.dom.Node"; +// private final static String CLASS_NAME_DOM_DOCUMENT = "org.w3c.dom.Document"; + private final static String SERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMSerializer"; + private final static String DESERIALIZER_FOR_DOM_DOCUMENT = "com.fasterxml.jackson.databind.ext.DOMDeserializer$DocumentDeserializer"; + private final static String DESERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMDeserializer$NodeDeserializer"; + + private final static String DESERIALIZER_FOR_PATH = "com.fasterxml.jackson.databind.ext.PathDeserializer"; + + // // Since 2.7, we will assume DOM classes are always found, both due to JDK 1.6 minimum + // // and because Android (and presumably GAE) have these classes + + private final static Class CLASS_DOM_NODE; + private final static Class CLASS_DOM_DOCUMENT; + + static { + Class doc = null, node = null; + try { + node = org.w3c.dom.Node.class; + doc = org.w3c.dom.Document.class; + } catch (Exception e) { + // not optimal but will do + System.err.println("WARNING: could not load DOM Node and/or Document classes"); + } + CLASS_DOM_NODE = node; + CLASS_DOM_DOCUMENT = doc; + } + + // // But Java7 type(s) may or may not be; dynamic lookup should be fine, still + // // (note: also assume it comes from JDK so that ClassLoader issues with OSGi + // // can, I hope, be avoided?) + + private final static Class CLASS_JAVA7_PATH; + static { + Class cls = null; + try { + cls = Class.forName("java.nio.file.Path"); + } catch (Exception e) { + // not optimal but will do + System.err.println("WARNING: could not load Java7 Path class"); + } + CLASS_JAVA7_PATH = cls; + } + + public final static OptionalHandlerFactory instance = new OptionalHandlerFactory(); + + protected OptionalHandlerFactory() { } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + public JsonSerializer findSerializer(SerializationConfig config, JavaType type, + BeanDescription beanDesc) + { + final Class rawType = type.getRawClass(); + + if ((CLASS_JAVA7_PATH != null) && CLASS_JAVA7_PATH.isAssignableFrom(rawType)) { + return ToStringSerializer.instance; + } + if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) { + return (JsonSerializer) instantiate(SERIALIZER_FOR_DOM_NODE); + } + String className = rawType.getName(); + String factoryName; + if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) { + factoryName = SERIALIZERS_FOR_JAVAX_XML; + } else { + return null; + } + + Object ob = instantiate(factoryName); + if (ob == null) { // could warn, if we had logging system (j.u.l?) + return null; + } + return ((Serializers) ob).findSerializer(config, type, beanDesc); + } + + public JsonDeserializer findDeserializer(JavaType type, DeserializationConfig config, + BeanDescription beanDesc) + throws JsonMappingException + { + final Class rawType = type.getRawClass(); + + if ((CLASS_JAVA7_PATH != null) && CLASS_JAVA7_PATH.isAssignableFrom(rawType)) { + return (JsonDeserializer) instantiate(DESERIALIZER_FOR_PATH); + } + if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) { + return (JsonDeserializer) instantiate(DESERIALIZER_FOR_DOM_NODE); + } + if ((CLASS_DOM_DOCUMENT != null) && CLASS_DOM_DOCUMENT.isAssignableFrom(rawType)) { + return (JsonDeserializer) instantiate(DESERIALIZER_FOR_DOM_DOCUMENT); + } + String className = rawType.getName(); + String factoryName; + if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) + || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) { + factoryName = DESERIALIZERS_FOR_JAVAX_XML; + } else { + return null; + } + Object ob = instantiate(factoryName); + if (ob == null) { // could warn, if we had logging system (j.u.l?) + return null; + } + return ((Deserializers) ob).findBeanDeserializer(type, config, beanDesc); + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + private Object instantiate(String className) + { + try { + return Class.forName(className).newInstance(); + } catch (LinkageError e) { } + // too many different kinds to enumerate here: + catch (Exception e) { } + return null; + } + + /** + * Since 2.7 we only need to check for class extension, as all implemented + * types are classes, not interfaces. This has performance implications for + * some cases, as we do not need to go over interfaces implemented, just + * superclasses + * + * @since 2.7 + */ + private boolean hasSuperClassStartingWith(Class rawType, String prefix) + { + for (Class supertype = rawType.getSuperclass(); supertype != null; supertype = supertype.getSuperclass()) { + if (supertype == Object.class) { + return false; + } + if (supertype.getName().startsWith(prefix)) { + return true; + } + } + return false; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/PathDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/PathDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/PathDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,29 @@ +package com.fasterxml.jackson.databind.ext; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; + +public class PathDeserializer extends StdScalarDeserializer +{ + private static final long serialVersionUID = 1; + + public PathDeserializer() { super(Path.class); } + + @Override + public Path deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonToken t= p.getCurrentToken(); + if (t != null) { + if (t.isScalarValue()) { + return Paths.get(p.getValueAsString()); + } + // 16-Oct-2015: should we perhaps allow JSON Arrays (of Strings) as well? + } + throw ctxt.mappingException(Path.class, t); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ext/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,23 @@ +/** +Contains extended support for "external" packages: things that +may or may not be present in runtime environment, but that are +commonly enough used so that explicit support can be added. +

+Currently supported extensions include: +

    +
  • Support for Java 1.5 core XML datatypes: the reason these are +considered "external" is that some platforms that claim to be 1.5 conformant +are only partially so (Google Android, GAE) and do not included these + types. +
  • +
  • Joda time. This package has superior date/time handling functionality, +and is thus supported. However, to minimize forced dependencies this +support is added as extension so that Joda is not needed by Jackson +itself: but if it is present, its core types are supported to some +degree +
  • +
+ +*/ + +package com.fasterxml.jackson.databind.ext; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/Annotated.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/Annotated.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/Annotated.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,114 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeBindings; + +/** + * Shared base class used for anything on which annotations (included + * within a {@link AnnotationMap}). + */ +public abstract class Annotated +{ + protected Annotated() { } + + public abstract A getAnnotation(Class acls); + + public abstract boolean hasAnnotation(Class acls); + + /** + * @since 2.7 + */ + public abstract boolean hasOneOf(Class[] annoClasses); + + /** + * Fluent factory method that will construct a new instance that uses specified + * instance annotations instead of currently configured ones. + */ + public abstract Annotated withAnnotations(AnnotationMap fallback); + + /** + * Fluent factory method that will construct a new instance that uses + * annotations from specified {@link Annotated} as fallback annotations + */ + public final Annotated withFallBackAnnotationsFrom(Annotated annotated) { + return withAnnotations(AnnotationMap.merge(getAllAnnotations(), annotated.getAllAnnotations())); + } + + /** + * Method that can be used to find actual JDK element that this instance + * represents. It is non-null, except for method/constructor parameters + * which do not have a JDK counterpart. + */ + public abstract AnnotatedElement getAnnotated(); + + protected abstract int getModifiers(); + + public final boolean isPublic() { + return Modifier.isPublic(getModifiers()); + } + + public abstract String getName(); + + /** + * Full generic type of the annotated element; definition + * of what exactly this means depends on sub-class. + * + * @since 2.7 + */ + public abstract JavaType getType(); + + /** + * @deprecated Since 2.7 Use {@link #getType()} instead. To be removed from 2.8. + */ + @Deprecated + public final JavaType getType(TypeBindings bogus) { + return getType(); + } + + /** + * Full generic type of the annotated element; definition + * of what exactly this means depends on sub-class. + * + * @deprecated Since 2.7 should instead use {@link #getType()}. To be removed from 2.8 + */ + @Deprecated + public Type getGenericType() { + return getRawType(); + } + + /** + * "Raw" type (type-erased class) of the annotated element; definition + * of what exactly this means depends on sub-class. + */ + public abstract Class getRawType(); + + /** + * Accessor that can be used to iterate over all the annotations + * associated with annotated component. + * + * @since 2.3 + */ + public abstract Iterable annotations(); + + /** + * Internal helper method used to access annotation information; + * not exposed to developers since instances are mutable. + */ + protected abstract AnnotationMap getAllAnnotations(); + + // Also: ensure we can use #equals, #hashCode + + @Override + public abstract boolean equals(Object o); + + @Override + public abstract int hashCode(); + + @Override + public abstract String toString(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1209 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.*; +import java.util.*; + +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; +import com.fasterxml.jackson.databind.type.TypeBindings; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.Annotations; +import com.fasterxml.jackson.databind.util.ClassUtil; + +public final class AnnotatedClass + extends Annotated + implements TypeResolutionContext +{ + private final static AnnotationMap[] NO_ANNOTATION_MAPS = new AnnotationMap[0]; + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * @since 2.7 + */ + final protected JavaType _type; + + /** + * Class for which annotations apply, and that owns other + * components (constructors, methods) + */ + final protected Class _class; + + /** + * Type bindings to use for members of {@link #_class}. + * + * @since 2.7 + */ + final protected TypeBindings _bindings; + + /** + * Ordered set of super classes and interfaces of the + * class itself: included in order of precedence + *

+ * NOTE: changed in 2.7 from List of Classes to List of {@link JavaType}s. + */ + final protected List _superTypes; + + /** + * Filter used to determine which annotations to gather; used + * to optimize things so that unnecessary annotations are + * ignored. + */ + final protected AnnotationIntrospector _annotationIntrospector; + + /** + * @since 2.7 + */ + final protected TypeFactory _typeFactory; + + /** + * Object that knows mapping of mix-in classes (ones that contain + * annotations to add) with their target classes (ones that + * get these additional annotations "mixed in"). + */ + final protected MixInResolver _mixInResolver; + + /** + * Primary mix-in class; one to use for the annotated class + * itself. Can be null. + */ + final protected Class _primaryMixIn; + + /* + /********************************************************** + /* Gathered information + /********************************************************** + */ + + /** + * Combined list of Jackson annotations that the class has, + * including inheritable ones from super classes and interfaces + */ + protected AnnotationMap _classAnnotations; + + /** + * Flag to indicate whether creator information has been resolved + * or not. + */ + protected boolean _creatorsResolved = false; + + /** + * Default constructor of the annotated class, if it has one. + */ + protected AnnotatedConstructor _defaultConstructor; + + /** + * Single argument constructors the class has, if any. + */ + protected List _constructors; + + /** + * Single argument static methods that might be usable + * as factory methods + */ + protected List _creatorMethods; + + /** + * Member methods of interest; for now ones with 0 or 1 arguments + * (just optimization, since others won't be used now) + */ + protected AnnotatedMethodMap _memberMethods; + + /** + * Member fields of interest: ones that are either public, + * or have at least one annotation. + */ + protected List _fields; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Constructor will not do any initializations, to allow for + * configuring instances differently depending on use cases + */ + private AnnotatedClass(JavaType type, Class rawType, TypeBindings bindings, + List superTypes, + AnnotationIntrospector aintr, MixInResolver mir, TypeFactory tf, + AnnotationMap classAnnotations) + { + _type = type; + _class = rawType; + _bindings = bindings; + _superTypes = superTypes; + _annotationIntrospector = aintr; + _typeFactory = tf; + _mixInResolver = mir; + _primaryMixIn = (_mixInResolver == null) ? null + : _mixInResolver.findMixInClassFor(_class); + _classAnnotations = classAnnotations; + } + + @Override + public AnnotatedClass withAnnotations(AnnotationMap ann) { + return new AnnotatedClass(_type, _class, _bindings, _superTypes, + _annotationIntrospector, _mixInResolver, _typeFactory, ann); + } + + /** + * Factory method that instantiates an instance. Returned instance + * will only be initialized with class annotations, but not with + * any method information. + * + * @since 2.7 + */ + public static AnnotatedClass construct(JavaType type, MapperConfig config) { + AnnotationIntrospector intr = config.isAnnotationProcessingEnabled() + ? config.getAnnotationIntrospector() : null; + Class raw = type.getRawClass(); + return new AnnotatedClass(type, raw, type.getBindings(), + ClassUtil.findSuperTypes(type, null, false), intr, + (MixInResolver) config, config.getTypeFactory(), null); + } + + /** + * @since 2.7 + */ + public static AnnotatedClass construct(JavaType type, MapperConfig config, + MixInResolver mir) + { + AnnotationIntrospector intr = config.isAnnotationProcessingEnabled() + ? config.getAnnotationIntrospector() : null; + Class raw = type.getRawClass(); + return new AnnotatedClass(type, raw, type.getBindings(), + ClassUtil.findSuperTypes(type, null, false), + intr, mir, config.getTypeFactory(), null); + } + + /** + * Method similar to {@link #construct}, but that will NOT include + * information from supertypes; only class itself and any direct + * mix-ins it may have. + */ + public static AnnotatedClass constructWithoutSuperTypes(Class cls, MapperConfig config) + { + if (config == null) { + return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(), + Collections.emptyList(), null, null, null, null); + } + AnnotationIntrospector intr = config.isAnnotationProcessingEnabled() + ? config.getAnnotationIntrospector() : null; + return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(), + Collections.emptyList(), intr, (MixInResolver) config, config.getTypeFactory(), null); + } + + public static AnnotatedClass constructWithoutSuperTypes(Class cls, MapperConfig config, + MixInResolver mir) + { + if (config == null) { + return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(), + Collections.emptyList(), null, null, null, null); + } + AnnotationIntrospector intr = config.isAnnotationProcessingEnabled() + ? config.getAnnotationIntrospector() : null; + return new AnnotatedClass(null, cls, TypeBindings.emptyBindings(), + Collections.emptyList(), intr, mir, config.getTypeFactory(), null); + } + + /* + /********************************************************** + /* TypeResolutionContext implementation + /********************************************************** + */ + + @Override + public JavaType resolveType(Type type) { + return _typeFactory.constructType(type, _bindings); + } + + /* + /********************************************************** + /* Annotated impl + /********************************************************** + */ + + @Override + public Class getAnnotated() { return _class; } + + @Override + public int getModifiers() { return _class.getModifiers(); } + + @Override + public String getName() { return _class.getName(); } + + @Override + public A getAnnotation(Class acls) { + return _classAnnotations().get(acls); + } + + @Override + public boolean hasAnnotation(Class acls) { + return _classAnnotations().has(acls); + } + + @Override + public boolean hasOneOf(Class[] annoClasses) { + return _classAnnotations().hasOneOf(annoClasses); + } + + @Override + public Class getRawType() { + return _class; + } + + @Override + public Iterable annotations() { + return _classAnnotations().annotations(); + } + + @Override + protected AnnotationMap getAllAnnotations() { + return _classAnnotations(); + } + + @Override + public JavaType getType() { + return _type; + } + + /* + /********************************************************** + /* Public API, generic accessors + /********************************************************** + */ + + public Annotations getAnnotations() { + return _classAnnotations(); + } + + public boolean hasAnnotations() { + return _classAnnotations().size() > 0; + } + + public AnnotatedConstructor getDefaultConstructor() + { + if (!_creatorsResolved) { + resolveCreators(); + } + return _defaultConstructor; + } + + public List getConstructors() + { + if (!_creatorsResolved) { + resolveCreators(); + } + return _constructors; + } + + public List getStaticMethods() + { + if (!_creatorsResolved) { + resolveCreators(); + } + return _creatorMethods; + } + + public Iterable memberMethods() + { + if (_memberMethods == null) { + resolveMemberMethods(); + } + return _memberMethods; + } + + public int getMemberMethodCount() + { + if (_memberMethods == null) { + resolveMemberMethods(); + } + return _memberMethods.size(); + } + + public AnnotatedMethod findMethod(String name, Class[] paramTypes) + { + if (_memberMethods == null) { + resolveMemberMethods(); + } + return _memberMethods.find(name, paramTypes); + } + + public int getFieldCount() { + if (_fields == null) { + resolveFields(); + } + return _fields.size(); + } + + public Iterable fields() + { + if (_fields == null) { + resolveFields(); + } + return _fields; + } + + /* + /********************************************************** + /* Public API, main-level resolution methods + /********************************************************** + */ + + private AnnotationMap _classAnnotations() { + AnnotationMap anns = _classAnnotations; + if (anns == null) { + // 06-Dec-2015, tatu: yes, double-locking, typically not a good choice. + // But for typical usage pattern here (and with JVM 7 and above) is + // a reasonable choice to avoid non-common but existing race condition + // from root name lookup style usage + // Also note that race condition stems from caching only used for loading + // where just class annotations are needed + synchronized (this) { + anns = _classAnnotations; + if (anns == null) { + anns = _resolveClassAnnotations(); + _classAnnotations = anns; + } + } + } + return anns; + } + + /** + * Initialization method that will recursively collect Jackson + * annotations for this class and all super classes and + * interfaces. + */ + private AnnotationMap _resolveClassAnnotations() + { + AnnotationMap ca = new AnnotationMap(); + // Should skip processing if annotation processing disabled + if (_annotationIntrospector != null) { + // add mix-in annotations first (overrides) + if (_primaryMixIn != null) { + _addClassMixIns(ca, _class, _primaryMixIn); + } + // first, annotations from the class itself: + _addAnnotationsIfNotPresent(ca, + ClassUtil.findClassAnnotations(_class)); + + // and then from super types + for (JavaType type : _superTypes) { + // and mix mix-in annotations in-between + _addClassMixIns(ca, type); + _addAnnotationsIfNotPresent(ca, + ClassUtil.findClassAnnotations(type.getRawClass())); + } + /* and finally... any annotations there might be for plain + * old Object.class: separate because for all other purposes + * it is just ignored (not included in super types) + */ + /* 12-Jul-2009, tatu: Should this be done for interfaces too? + * For now, yes, seems useful for some cases, and not harmful for any? + */ + _addClassMixIns(ca, Object.class); + } + return ca; + } + + /** + * Initialization method that will find out all constructors + * and potential static factory methods the class has. + */ + private void resolveCreators() + { + // Then see which constructors we have + List constructors = null; + ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(_class); + // Constructor also always members of this class, so + TypeResolutionContext typeContext = this; + for (ClassUtil.Ctor ctor : declaredCtors) { + if (_isIncludableConstructor(ctor.getConstructor())) { + if (ctor.getParamCount() == 0) { + _defaultConstructor = _constructDefaultConstructor(ctor, typeContext); + } else { + if (constructors == null) { + constructors = new ArrayList(Math.max(10, declaredCtors.length)); + } + constructors.add(_constructNonDefaultConstructor(ctor, typeContext)); + } + } + } + if (constructors == null) { + _constructors = Collections.emptyList(); + } else { + _constructors = constructors; + } + // and if need be, augment with mix-ins + if (_primaryMixIn != null) { + if (_defaultConstructor != null || !_constructors.isEmpty()) { + _addConstructorMixIns(_primaryMixIn); + } + } + + + /* And then... let's remove all constructors that are deemed + * ignorable after all annotations have been properly collapsed. + */ + // AnnotationIntrospector is null if annotations not enabled; if so, can skip: + if (_annotationIntrospector != null) { + if (_defaultConstructor != null) { + if (_annotationIntrospector.hasIgnoreMarker(_defaultConstructor)) { + _defaultConstructor = null; + } + } + if (_constructors != null) { + // count down to allow safe removal + for (int i = _constructors.size(); --i >= 0; ) { + if (_annotationIntrospector.hasIgnoreMarker(_constructors.get(i))) { + _constructors.remove(i); + } + } + } + } + List creatorMethods = null; + + // Then static methods which are potential factory methods + for (Method m : _findClassMethods(_class)) { + if (!Modifier.isStatic(m.getModifiers())) { + continue; + } + // all factory methods are fine: + //int argCount = m.getParameterTypes().length; + if (creatorMethods == null) { + creatorMethods = new ArrayList(8); + } + creatorMethods.add(_constructCreatorMethod(m, typeContext)); + } + if (creatorMethods == null) { + _creatorMethods = Collections.emptyList(); + } else { + _creatorMethods = creatorMethods; + // mix-ins to mix in? + if (_primaryMixIn != null) { + _addFactoryMixIns(_primaryMixIn); + } + // anything to ignore at this point? + if (_annotationIntrospector != null) { + // count down to allow safe removal + for (int i = _creatorMethods.size(); --i >= 0; ) { + if (_annotationIntrospector.hasIgnoreMarker(_creatorMethods.get(i))) { + _creatorMethods.remove(i); + } + } + } + } + _creatorsResolved = true; + } + + /** + * Method for resolving member method information: aggregating all non-static methods + * and combining annotations (to implement method-annotation inheritance) + * + * @param methodFilter Filter used to determine which methods to include + */ + private void resolveMemberMethods() + { + _memberMethods = new AnnotatedMethodMap(); + AnnotatedMethodMap mixins = new AnnotatedMethodMap(); + // first: methods from the class itself + _addMemberMethods(_class, this, _memberMethods, _primaryMixIn, mixins); + + // and then augment these with annotations from super-types: + for (JavaType type : _superTypes) { + Class mixin = (_mixInResolver == null) ? null : _mixInResolver.findMixInClassFor(type.getRawClass()); + TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, + type.getBindings()); + _addMemberMethods(type.getRawClass(), typeContext, _memberMethods, mixin, mixins); + } + // Special case: mix-ins for Object.class? (to apply to ALL classes) + if (_mixInResolver != null) { + Class mixin = _mixInResolver.findMixInClassFor(Object.class); + if (mixin != null) { + _addMethodMixIns(_class, _memberMethods, mixin, mixins); + } + } + + /* Any unmatched mix-ins? Most likely error cases (not matching + * any method); but there is one possible real use case: + * exposing Object#hashCode (alas, Object#getClass can NOT be + * exposed, see [JACKSON-140]) + */ + // 14-Feb-2011, tatu: AnnotationIntrospector is null if annotations not enabled; if so, can skip: + if (_annotationIntrospector != null) { + if (!mixins.isEmpty()) { + Iterator it = mixins.iterator(); + while (it.hasNext()) { + AnnotatedMethod mixIn = it.next(); + try { + Method m = Object.class.getDeclaredMethod(mixIn.getName(), mixIn.getRawParameterTypes()); + if (m != null) { + // Since it's from java.lang.Object, no generics, no need for real type context: + AnnotatedMethod am = _constructMethod(m, this); + _addMixOvers(mixIn.getAnnotated(), am, false); + _memberMethods.add(am); + } + } catch (Exception e) { } + } + } + } + } + + /** + * Method that will collect all member (non-static) fields + * that are either public, or have at least a single annotation + * associated with them. + */ + private void resolveFields() + { + Map foundFields = _findFields(_type, this, null); + if (foundFields == null || foundFields.size() == 0) { + _fields = Collections.emptyList(); + } else { + _fields = new ArrayList(foundFields.size()); + _fields.addAll(foundFields.values()); + } + } + + /* + /********************************************************** + /* Helper methods for resolving class annotations + /* (resolution consisting of inheritance, overrides, + /* and injection of mix-ins as necessary) + /********************************************************** + */ + + /** + * Helper method for adding any mix-in annotations specified + * class might have. + */ + protected void _addClassMixIns(AnnotationMap annotations, JavaType target) + { + if (_mixInResolver != null) { + final Class toMask = target.getRawClass(); + _addClassMixIns(annotations, toMask, _mixInResolver.findMixInClassFor(toMask)); + } + } + + protected void _addClassMixIns(AnnotationMap annotations, Class target) + { + if (_mixInResolver != null) { + _addClassMixIns(annotations, target, _mixInResolver.findMixInClassFor(target)); + } + } + + protected void _addClassMixIns(AnnotationMap annotations, Class toMask, + Class mixin) + { + if (mixin == null) { + return; + } + // Ok, first: annotations from mix-in class itself: + _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(mixin)); + + /* And then from its supertypes, if any. But note that we will + * only consider super-types up until reaching the masked + * class (if found); this because often mix-in class + * is a sub-class (for convenience reasons). And if so, we + * absolutely must NOT include super types of masked class, + * as that would inverse precedence of annotations. + */ + for (Class parent : ClassUtil.findSuperClasses(mixin, toMask, false)) { + _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(parent)); + } + } + + /* + /********************************************************** + /* Helper methods for populating creator (ctor, factory) information + /********************************************************** + */ + + protected void _addConstructorMixIns(Class mixin) + { + MemberKey[] ctorKeys = null; + int ctorCount = (_constructors == null) ? 0 : _constructors.size(); + for (ClassUtil.Ctor ctor0 : ClassUtil.getConstructors(mixin)) { + Constructor ctor = ctor0.getConstructor(); + if (ctor.getParameterTypes().length == 0) { + if (_defaultConstructor != null) { + _addMixOvers(ctor, _defaultConstructor, false); + } + } else { + if (ctorKeys == null) { + ctorKeys = new MemberKey[ctorCount]; + for (int i = 0; i < ctorCount; ++i) { + ctorKeys[i] = new MemberKey(_constructors.get(i).getAnnotated()); + } + } + MemberKey key = new MemberKey(ctor); + + for (int i = 0; i < ctorCount; ++i) { + if (!key.equals(ctorKeys[i])) { + continue; + } + _addMixOvers(ctor, _constructors.get(i), true); + break; + } + } + } + } + + protected void _addFactoryMixIns(Class mixin) + { + MemberKey[] methodKeys = null; + int methodCount = _creatorMethods.size(); + + for (Method m : ClassUtil.getDeclaredMethods(mixin)) { + if (!Modifier.isStatic(m.getModifiers())) { + continue; + } + if (m.getParameterTypes().length == 0) { + continue; + } + if (methodKeys == null) { + methodKeys = new MemberKey[methodCount]; + for (int i = 0; i < methodCount; ++i) { + methodKeys[i] = new MemberKey(_creatorMethods.get(i).getAnnotated()); + } + } + MemberKey key = new MemberKey(m); + for (int i = 0; i < methodCount; ++i) { + if (!key.equals(methodKeys[i])) { + continue; + } + _addMixOvers(m, _creatorMethods.get(i), true); + break; + } + } + } + + /* + /********************************************************** + /* Helper methods for populating method information + /********************************************************** + */ + + protected void _addMemberMethods(Class cls, TypeResolutionContext typeContext, + AnnotatedMethodMap methods, + Class mixInCls, AnnotatedMethodMap mixIns) + { + // first, mixIns, since they have higher priority then class methods + if (mixInCls != null) { + _addMethodMixIns(cls, methods, mixInCls, mixIns); + } + if (cls == null) { // just so caller need not check when passing super-class + return; + } + // then methods from the class itself + for (Method m : _findClassMethods(cls)) { + if (!_isIncludableMemberMethod(m)) { + continue; + } + AnnotatedMethod old = methods.find(m); + if (old == null) { + AnnotatedMethod newM = _constructMethod(m, typeContext); + methods.add(newM); + // Ok, but is there a mix-in to connect now? + old = mixIns.remove(m); + if (old != null) { + _addMixOvers(old.getAnnotated(), newM, false); + } + } else { + /* If sub-class already has the method, we only want to augment + * annotations with entries that are not masked by sub-class. + */ + _addMixUnders(m, old); + + /* 06-Jan-2010, tatu: [JACKSON-450] Except that if method we saw first is + * from an interface, and we now find a non-interface definition, we should + * use this method, but with combination of annotations. + * This helps (or rather, is essential) with JAXB annotations and + * may also result in faster method calls (interface calls are slightly + * costlier than regular method calls) + */ + if (old.getDeclaringClass().isInterface() && !m.getDeclaringClass().isInterface()) { + methods.add(old.withMethod(m)); + } + } + } + } + + protected void _addMethodMixIns(Class targetClass, AnnotatedMethodMap methods, + Class mixInCls, AnnotatedMethodMap mixIns) + { +// List> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true); + + List> parents = ClassUtil.findRawSuperTypes(mixInCls, targetClass, true); + for (Class mixin : parents) { + for (Method m : ClassUtil.getDeclaredMethods(mixin)) { + if (!_isIncludableMemberMethod(m)) { + continue; + } + AnnotatedMethod am = methods.find(m); + /* Do we already have a method to augment (from sub-class + * that will mask this mixIn)? If so, add if visible + * without masking (no such annotation) + */ + if (am != null) { + _addMixUnders(m, am); + /* Otherwise will have precedence, but must wait + * until we find the real method (mixIn methods are + * just placeholder, can't be called) + */ + } else { + // Well, or, as per [databind#515], multi-level merge within mixins... + am = mixIns.find(m); + if (am != null) { + _addMixUnders(m, am); + } else { + // 03-Nov-2015, tatu: Mix-in method never called, should not need + // to resolve generic types, so this class is fine as context + mixIns.add(_constructMethod(m, this)); + } + } + } + } + } + + /* + /********************************************************** + /* Helper methods for populating field information + /********************************************************** + */ + + protected Map _findFields(JavaType type, + TypeResolutionContext typeContext, Map fields) + { + /* First, a quick test: we only care for regular classes (not + * interfaces, primitive types etc), except for Object.class. + * A simple check to rule out other cases is to see if there + * is a super class or not. + */ + JavaType parent = type.getSuperClass(); + if (parent != null) { + final Class cls = type.getRawClass(); + // Let's add super-class' fields first, then ours. + /* 21-Feb-2010, tatu: Need to handle masking: as per [JACKSON-226] + * we otherwise get into trouble... + */ + fields = _findFields(parent, + new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()), + fields); + for (Field f : ClassUtil.getDeclaredFields(cls)) { + // static fields not included (transients are at this point, filtered out later) + if (!_isIncludableField(f)) { + continue; + } + /* Ok now: we can (and need) not filter out ignorable fields + * at this point; partly because mix-ins haven't been + * added, and partly because logic can be done when + * determining get/settability of the field. + */ + if (fields == null) { + fields = new LinkedHashMap(); + } + fields.put(f.getName(), _constructField(f, typeContext)); + } + // And then... any mix-in overrides? + if (_mixInResolver != null) { + Class mixin = _mixInResolver.findMixInClassFor(cls); + if (mixin != null) { + _addFieldMixIns(mixin, cls, fields); + } + } + } + return fields; + } + + /** + * Method called to add field mix-ins from given mix-in class (and its fields) + * into already collected actual fields (from introspected classes and their + * super-classes) + */ + protected void _addFieldMixIns(Class mixInCls, Class targetClass, + Map fields) + { + List> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true); + for (Class mixin : parents) { + for (Field mixinField : ClassUtil.getDeclaredFields(mixin)) { + // there are some dummy things (static, synthetic); better ignore + if (!_isIncludableField(mixinField)) { + continue; + } + String name = mixinField.getName(); + // anything to mask? (if not, quietly ignore) + AnnotatedField maskedField = fields.get(name); + if (maskedField != null) { + _addOrOverrideAnnotations(maskedField, mixinField.getDeclaredAnnotations()); + } + } + } + } + + /* + /********************************************************** + /* Helper methods, constructing value types + /********************************************************** + */ + + protected AnnotatedMethod _constructMethod(Method m, TypeResolutionContext typeContext) + { + /* note: parameter annotations not used for regular (getter, setter) + * methods; only for creator methods (static factory methods) + * -- at least not yet! + */ + if (_annotationIntrospector == null) { // when annotation processing is disabled + return new AnnotatedMethod(typeContext, m, _emptyAnnotationMap(), null); + } + return new AnnotatedMethod(typeContext, m, _collectRelevantAnnotations(m.getDeclaredAnnotations()), null); + } + + protected AnnotatedConstructor _constructDefaultConstructor(ClassUtil.Ctor ctor, + TypeResolutionContext typeContext) + { + if (_annotationIntrospector == null) { // when annotation processing is disabled + return new AnnotatedConstructor(typeContext, ctor.getConstructor(), _emptyAnnotationMap(), NO_ANNOTATION_MAPS); + } + return new AnnotatedConstructor(typeContext, ctor.getConstructor(), + _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), NO_ANNOTATION_MAPS); + } + + protected AnnotatedConstructor _constructNonDefaultConstructor(ClassUtil.Ctor ctor, + TypeResolutionContext typeContext) + { + final int paramCount = ctor.getParamCount(); + if (_annotationIntrospector == null) { // when annotation processing is disabled + return new AnnotatedConstructor(typeContext, ctor.getConstructor(), + _emptyAnnotationMap(), _emptyAnnotationMaps(paramCount)); + } + + /* [JACKSON-701]: Looks like JDK has discrepancy, whereas annotations for implicit 'this' + * (for non-static inner classes) are NOT included, but type is? Strange, sounds like + * a bug. Alas, we can't really fix that... + */ + if (paramCount == 0) { // no-arg default constructors, can simplify slightly + return new AnnotatedConstructor(typeContext, ctor.getConstructor(), + _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), NO_ANNOTATION_MAPS); + } + // Also: [JACKSON-757] (enum value constructors) + AnnotationMap[] resolvedAnnotations; + Annotation[][] paramAnns = ctor.getParameterAnnotations(); + if (paramCount != paramAnns.length) { + // Limits of the work-around (to avoid hiding real errors): + // first, only applicable for member classes and then either: + + resolvedAnnotations = null; + Class dc = ctor.getDeclaringClass(); + // (a) is enum, which have two extra hidden params (name, index) + if (dc.isEnum() && (paramCount == paramAnns.length + 2)) { + Annotation[][] old = paramAnns; + paramAnns = new Annotation[old.length+2][]; + System.arraycopy(old, 0, paramAnns, 2, old.length); + resolvedAnnotations = _collectRelevantAnnotations(paramAnns); + } else if (dc.isMemberClass()) { + // (b) non-static inner classes, get implicit 'this' for parameter, not annotation + if (paramCount == (paramAnns.length + 1)) { + // hack attack: prepend a null entry to make things match + Annotation[][] old = paramAnns; + paramAnns = new Annotation[old.length+1][]; + System.arraycopy(old, 0, paramAnns, 1, old.length); + resolvedAnnotations = _collectRelevantAnnotations(paramAnns); + } + } + if (resolvedAnnotations == null) { + throw new IllegalStateException("Internal error: constructor for "+ctor.getDeclaringClass().getName() + +" has mismatch: "+paramCount+" parameters; "+paramAnns.length+" sets of annotations"); + } + } else { + resolvedAnnotations = _collectRelevantAnnotations(paramAnns); + } + return new AnnotatedConstructor(typeContext, ctor.getConstructor(), + _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), resolvedAnnotations); + } + + protected AnnotatedMethod _constructCreatorMethod(Method m, TypeResolutionContext typeContext) + { + final int paramCount = m.getParameterTypes().length; + if (_annotationIntrospector == null) { // when annotation processing is disabled + return new AnnotatedMethod(typeContext, m, _emptyAnnotationMap(), _emptyAnnotationMaps(paramCount)); + } + if (paramCount == 0) { // common enough we can slightly optimize + return new AnnotatedMethod(typeContext, m, _collectRelevantAnnotations(m.getDeclaredAnnotations()), + NO_ANNOTATION_MAPS); + } + return new AnnotatedMethod(typeContext, m, _collectRelevantAnnotations(m.getDeclaredAnnotations()), + _collectRelevantAnnotations(m.getParameterAnnotations())); + } + + protected AnnotatedField _constructField(Field f, TypeResolutionContext typeContext) + { + if (_annotationIntrospector == null) { // when annotation processing is disabled + return new AnnotatedField(typeContext, f, _emptyAnnotationMap()); + } + return new AnnotatedField(typeContext, f, _collectRelevantAnnotations(f.getDeclaredAnnotations())); + } + + private AnnotationMap _emptyAnnotationMap() { + return new AnnotationMap(); + } + + private AnnotationMap[] _emptyAnnotationMaps(int count) { + if (count == 0) { + return NO_ANNOTATION_MAPS; + } + AnnotationMap[] maps = new AnnotationMap[count]; + for (int i = 0; i < count; ++i) { + maps[i] = _emptyAnnotationMap(); + } + return maps; + } + + /* + /********************************************************** + /* Helper methods, inclusion filtering + /********************************************************** + */ + + protected boolean _isIncludableMemberMethod(Method m) + { + if (Modifier.isStatic(m.getModifiers())) { + return false; + } + /* 07-Apr-2009, tatu: Looks like generics can introduce hidden + * bridge and/or synthetic methods. I don't think we want to + * consider those... + */ + if (m.isSynthetic() || m.isBridge()) { + return false; + } + // also, for now we have no use for methods with 2 or more arguments: + int pcount = m.getParameterTypes().length; + return (pcount <= 2); + } + + private boolean _isIncludableField(Field f) + { + // Most likely synthetic fields, if any, are to be skipped similar to methods + if (f.isSynthetic()) { + return false; + } + // Static fields are never included. Transient are (since 2.6), for + // purpose of propagating removal + int mods = f.getModifiers(); + if (Modifier.isStatic(mods)) { + return false; + } + return true; + } + + // for [databind#1005]: do not use or expose synthetic constructors + private boolean _isIncludableConstructor(Constructor c) + { + return !c.isSynthetic(); + } + + /* + /********************************************************** + /* Helper methods, attaching annotations + /********************************************************** + */ + + protected AnnotationMap[] _collectRelevantAnnotations(Annotation[][] anns) + { + int len = anns.length; + AnnotationMap[] result = new AnnotationMap[len]; + for (int i = 0; i < len; ++i) { + result[i] = _collectRelevantAnnotations(anns[i]); + } + return result; + } + + protected AnnotationMap _collectRelevantAnnotations(Annotation[] anns) + { + return _addAnnotationsIfNotPresent(new AnnotationMap(), anns); + } + + /* Helper method used to add all applicable annotations from given set. + * Takes into account possible "annotation bundles" (meta-annotations to + * include instead of main-level annotation) + */ + private AnnotationMap _addAnnotationsIfNotPresent(AnnotationMap result, Annotation[] anns) + { + if (anns != null) { + List fromBundles = null; + for (Annotation ann : anns) { // first: direct annotations + // note: we will NOT filter out non-Jackson anns any more + boolean wasNotPresent = result.addIfNotPresent(ann); + if (wasNotPresent && _isAnnotationBundle(ann)) { + fromBundles = _addFromBundle(ann, fromBundles); + } + } + if (fromBundles != null) { // and secondarily handle bundles, if any found: precedence important + _addAnnotationsIfNotPresent(result, fromBundles.toArray(new Annotation[fromBundles.size()])); + } + } + return result; + } + + private List _addFromBundle(Annotation bundle, List result) + { + for (Annotation a : ClassUtil.findClassAnnotations(bundle.annotationType())) { + // minor optimization: by-pass 2 common JDK meta-annotations + if ((a instanceof Target) || (a instanceof Retention)) { + continue; + } + if (result == null) { + result = new ArrayList(); + } + result.add(a); + } + return result; + } + + private void _addAnnotationsIfNotPresent(AnnotatedMember target, Annotation[] anns) + { + if (anns != null) { + List fromBundles = null; + for (Annotation ann : anns) { // first: direct annotations + boolean wasNotPresent = target.addIfNotPresent(ann); + if (wasNotPresent && _isAnnotationBundle(ann)) { + fromBundles = _addFromBundle(ann, fromBundles); + } + } + if (fromBundles != null) { // and secondarily handle bundles, if any found: precedence important + _addAnnotationsIfNotPresent(target, fromBundles.toArray(new Annotation[fromBundles.size()])); + } + } + } + + private void _addOrOverrideAnnotations(AnnotatedMember target, Annotation[] anns) + { + if (anns != null) { + List fromBundles = null; + for (Annotation ann : anns) { // first: direct annotations + boolean wasModified = target.addOrOverride(ann); + if (wasModified && _isAnnotationBundle(ann)) { + fromBundles = _addFromBundle(ann, fromBundles); + } + } + if (fromBundles != null) { // and then bundles, if any: important for precedence + _addOrOverrideAnnotations(target, fromBundles.toArray(new Annotation[fromBundles.size()])); + } + } + } + + /** + * @param addParamAnnotations Whether parameter annotations are to be + * added as well + */ + protected void _addMixOvers(Constructor mixin, AnnotatedConstructor target, + boolean addParamAnnotations) + { + _addOrOverrideAnnotations(target, mixin.getDeclaredAnnotations()); + if (addParamAnnotations) { + Annotation[][] pa = mixin.getParameterAnnotations(); + for (int i = 0, len = pa.length; i < len; ++i) { + for (Annotation a : pa[i]) { + target.addOrOverrideParam(i, a); + } + } + } + } + + /** + * @param addParamAnnotations Whether parameter annotations are to be + * added as well + */ + protected void _addMixOvers(Method mixin, AnnotatedMethod target, + boolean addParamAnnotations) + { + _addOrOverrideAnnotations(target, mixin.getDeclaredAnnotations()); + if (addParamAnnotations) { + Annotation[][] pa = mixin.getParameterAnnotations(); + for (int i = 0, len = pa.length; i < len; ++i) { + for (Annotation a : pa[i]) { + target.addOrOverrideParam(i, a); + } + } + } + } + + /** + * Method that will add annotations from specified source method to target method, + * but only if target does not yet have them. + */ + protected void _addMixUnders(Method src, AnnotatedMethod target) { + _addAnnotationsIfNotPresent(target, src.getDeclaredAnnotations()); + } + + private final boolean _isAnnotationBundle(Annotation ann) { + return (_annotationIntrospector != null) && _annotationIntrospector.isAnnotationBundle(ann); + } + + /** + * Helper method that gets methods declared in given class; usually a simple thing, + * but sometimes (as per [databind#785]) more complicated, depending on classloader + * setup. + * + * @since 2.4.7 + */ + protected Method[] _findClassMethods(Class cls) + { + try { + return ClassUtil.getDeclaredMethods(cls); + } catch (final NoClassDefFoundError ex) { + // One of the methods had a class that was not found in the cls.getClassLoader. + // Maybe the developer was nice and has a different class loader for this context. + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader == null){ + // Nope... this is going to end poorly + throw ex; + } + final Class contextClass; + try { + contextClass = loader.loadClass(cls.getName()); + } catch (ClassNotFoundException e) { + // !!! TODO: 08-May-2015, tatu: Chain appropriately once we have JDK 1.7/Java7 as baseline + //ex.addSuppressed(e); Not until Jackson 2.7 + throw ex; + } + return contextClass.getDeclaredMethods(); // Cross fingers + } + } + + /* + /********************************************************** + /* Other methods + /********************************************************** + */ + + @Override + public String toString() { + return "[AnnotedClass "+_class.getName()+"]"; + } + + @Override + public int hashCode() { + return _class.getName().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + return ((AnnotatedClass) o)._class == _class; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,215 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.util.ClassUtil; + +public final class AnnotatedConstructor + extends AnnotatedWithParams +{ + private static final long serialVersionUID = 1L; + + protected final Constructor _constructor; + + /** + * Field that is used to make JDK serialization work with this + * object. + * + * @since 2.1 + */ + protected Serialization _serialization; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public AnnotatedConstructor(TypeResolutionContext ctxt, Constructor constructor, + AnnotationMap classAnn, AnnotationMap[] paramAnn) + { + super(ctxt, classAnn, paramAnn); + if (constructor == null) { + throw new IllegalArgumentException("Null constructor not allowed"); + } + _constructor = constructor; + } + + /** + * Method used for JDK serialization support + * @since 2.1 + */ + protected AnnotatedConstructor(Serialization ser) + { + super(null, null, null); + _constructor = null; + _serialization = ser; + } + + @Override + public AnnotatedConstructor withAnnotations(AnnotationMap ann) { + return new AnnotatedConstructor(_typeContext, _constructor, ann, _paramAnnotations); + } + + /* + /********************************************************** + /* Annotated impl + /********************************************************** + */ + + @Override + public Constructor getAnnotated() { return _constructor; } + + @Override + public int getModifiers() { return _constructor.getModifiers(); } + + @Override + public String getName() { return _constructor.getName(); } + + @Override + public JavaType getType() { + return _typeContext.resolveType(getRawType()); + } + + @Override + public Class getRawType() { + return _constructor.getDeclaringClass(); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + @Override + public int getParameterCount() { + return _constructor.getParameterTypes().length; + } + + @Override + public Class getRawParameterType(int index) + { + Class[] types = _constructor.getParameterTypes(); + return (index >= types.length) ? null : types[index]; + } + + @Override + public JavaType getParameterType(int index) { + Type[] types = _constructor.getGenericParameterTypes(); + if (index >= types.length) { + return null; + } + return _typeContext.resolveType(types[index]); + } + + @Override + public final Object call() throws Exception { + return _constructor.newInstance(); + } + + @Override + public final Object call(Object[] args) throws Exception { + return _constructor.newInstance(args); + } + + @Override + public final Object call1(Object arg) throws Exception { + return _constructor.newInstance(arg); + } + + /* + /********************************************************** + /* AnnotatedMember impl + /********************************************************** + */ + + @Override + public Class getDeclaringClass() { return _constructor.getDeclaringClass(); } + + @Override + public Member getMember() { return _constructor; } + + @Override + public void setValue(Object pojo, Object value) + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Cannot call setValue() on constructor of " + +getDeclaringClass().getName()); + } + + @Override + public Object getValue(Object pojo) + throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Cannot call getValue() on constructor of " + +getDeclaringClass().getName()); + } + + /* + /********************************************************** + /* Extended API, specific annotations + /********************************************************** + */ + + @Override + public String toString() { + return "[constructor for "+getName()+", annotations: "+_annotations+"]"; + } + + @Override + public int hashCode() { + return _constructor.getName().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + return ((AnnotatedConstructor) o)._constructor == _constructor; + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + Object writeReplace() { + return new AnnotatedConstructor(new Serialization(_constructor)); + } + + Object readResolve() { + Class clazz = _serialization.clazz; + try { + Constructor ctor = clazz.getDeclaredConstructor(_serialization.args); + // 06-Oct-2012, tatu: Has "lost" its security override, must force back + if (!ctor.isAccessible()) { + ClassUtil.checkAndFixAccess(ctor, false); + } + return new AnnotatedConstructor(null, ctor, null, null); + } catch (Exception e) { + throw new IllegalArgumentException("Could not find constructor with " + +_serialization.args.length+" args from Class '"+clazz.getName()); + } + } + + /** + * Helper class that is used as the workaround to persist + * Field references. It basically just stores declaring class + * and field name. + */ + private final static class Serialization + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + protected Class clazz; + protected Class[] args; + + public Serialization(Constructor ctor) { + clazz = ctor.getDeclaringClass(); + args = ctor.getParameterTypes(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedField.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedField.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedField.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,195 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Object that represents non-static (and usually non-transient/volatile) + * fields of a class. + */ +public final class AnnotatedField + extends AnnotatedMember + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Actual {@link Field} used for access. + *

+ * Transient since it can not be persisted directly using + * JDK serialization + */ + protected final transient Field _field; + + /** + * Temporary field required for JDK serialization support + */ + protected Serialization _serialization; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public AnnotatedField(TypeResolutionContext contextClass, Field field, AnnotationMap annMap) + { + super(contextClass, annMap); + _field = field; + } + + @Override + public AnnotatedField withAnnotations(AnnotationMap ann) { + return new AnnotatedField(_typeContext, _field, ann); + } + + /** + * Method used for JDK serialization support + */ + protected AnnotatedField(Serialization ser) + { + super(null, null); + _field = null; + _serialization = ser; + } + + /* + /********************************************************** + /* Annotated impl + /********************************************************** + */ + + @Override + public Field getAnnotated() { return _field; } + + @Override + public int getModifiers() { return _field.getModifiers(); } + + @Override + public String getName() { return _field.getName(); } + + @Override + public Class getRawType() { + return _field.getType(); + } + + @Override + public JavaType getType() { + return _typeContext.resolveType(_field.getGenericType()); + } + + /* + /********************************************************** + /* AnnotatedMember impl + /********************************************************** + */ + + @Override + public Class getDeclaringClass() { return _field.getDeclaringClass(); } + + @Override + public Member getMember() { return _field; } + + @Override + public void setValue(Object pojo, Object value) throws IllegalArgumentException + { + try { + _field.set(pojo, value); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Failed to setValue() for field " + +getFullName()+": "+e.getMessage(), e); + } + } + + @Override + public Object getValue(Object pojo) throws IllegalArgumentException + { + try { + return _field.get(pojo); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Failed to getValue() for field " + +getFullName()+": "+e.getMessage(), e); + } + } + + /* + /********************************************************** + /* Extended API, generic + /********************************************************** + */ + + public String getFullName() { + return getDeclaringClass().getName() + "#" + getName(); + } + + public int getAnnotationCount() { return _annotations.size(); } + + /** + * @since 2.6 + */ + public boolean isTransient() { return Modifier.isTransient(getModifiers()); } + + @Override + public int hashCode() { + return _field.getName().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + return ((AnnotatedField) o)._field == _field; + } + + @Override + public String toString() { + return "[field "+getFullName()+"]"; + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + Object writeReplace() { + return new AnnotatedField(new Serialization(_field)); + } + + Object readResolve() { + Class clazz = _serialization.clazz; + try { + Field f = clazz.getDeclaredField(_serialization.name); + // 06-Oct-2012, tatu: Has "lost" its security override, may need to force back + if (!f.isAccessible()) { + ClassUtil.checkAndFixAccess(f, false); + } + return new AnnotatedField(null, f, null); + } catch (Exception e) { + throw new IllegalArgumentException("Could not find method '"+_serialization.name + +"' from Class '"+clazz.getName()); + } + } + + /** + * Helper class that is used as the workaround to persist + * Field references. It basically just stores declaring class + * and field name. + */ + private final static class Serialization + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + protected Class clazz; + protected String name; + + public Serialization(Field f) { + clazz = f.getDeclaringClass(); + name = f.getName(); + + } + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,171 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Member; +import java.util.Collections; + +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Intermediate base class for annotated entities that are members of + * a class; fields, methods and constructors. This is a superset + * of things that can represent logical properties as it contains + * constructors in addition to fields and methods. + */ +public abstract class AnnotatedMember + extends Annotated + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; // since 2.5 + + // 19-Dec-2014, tatu: Similarly, assumed NOT to be needed in cases where + // owning object (ObjectMapper or relatives) is being JDK-serialized + /** + * Context object needed for resolving generic type associated with this + * member (method parameter or return value, or field type). + * + * @since 2.7 + */ + protected final transient TypeResolutionContext _typeContext; + + // Transient since information not needed after construction, so + // no need to persist + protected final transient AnnotationMap _annotations; + + protected AnnotatedMember(TypeResolutionContext ctxt, AnnotationMap annotations) { + super(); + _typeContext = ctxt; + _annotations = annotations; + } + + /** + * Copy-constructor. + * + * @since 2.5 + */ + protected AnnotatedMember(AnnotatedMember base) { + _typeContext = base._typeContext; + _annotations = base._annotations; + } + + /** + * Actual physical class in which this memmber was declared. + */ + public abstract Class getDeclaringClass(); + + public abstract Member getMember(); + + /** + * Accessor for {@link TypeResolutionContext} that is used for resolving + * full generic type of this member. + * + * @since 2.7 + */ + public TypeResolutionContext getTypeContext() { + return _typeContext; + } + + @Override + public final A getAnnotation(Class acls) { + if (_annotations == null) { + return null; + } + return _annotations.get(acls); + } + + @Override + public final boolean hasAnnotation(Class acls) { + if (_annotations == null) { + return false; + } + return _annotations.has(acls); + } + + @Override + public boolean hasOneOf(Class[] annoClasses) { + if (_annotations == null) { + return false; + } + return _annotations.hasOneOf(annoClasses); + } + + @Override + public Iterable annotations() { + if (_annotations == null) { + return Collections.emptyList(); + } + return _annotations.annotations(); + } + + @Override + protected AnnotationMap getAllAnnotations() { + return _annotations; + } + + /** + * Method called to override an annotation, usually due to a mix-in + * annotation masking or overriding an annotation 'real' constructor + * has. + */ + public final boolean addOrOverride(Annotation a) { + return _annotations.add(a); + } + + /** + * Method called to augment annotations, by adding specified + * annotation if and only if it is not yet present in the + * annotation map we have. + */ + public final boolean addIfNotPresent(Annotation a) { + return _annotations.addIfNotPresent(a); + } + + /** + * Method that can be called to modify access rights, by calling + * {@link java.lang.reflect.AccessibleObject#setAccessible} on + * the underlying annotated element. + *

+ * Note that caller should verify that + * {@link com.fasterxml.jackson.databind.MapperFeature#CAN_OVERRIDE_ACCESS_MODIFIERS} + * is enabled before calling this method; as well as pass + * force flag appropriately. + * + * @since 2.7 + */ + public final void fixAccess(boolean force) { + ClassUtil.checkAndFixAccess(getMember(), force); + } + + /** + * @deprecated Since 2.7 use {@link #fixAccess(boolean)} instead + */ + @Deprecated + public final void fixAccess() { +// fixAccess(false); + fixAccess(true); + } + + /** + * Optional method that can be used to assign value of + * this member on given object, if this is a supported + * operation for member type. + *

+ * This is implemented for fields and single-argument + * member methods; but not for constructor parameters or + * other types of methods (like static methods) + */ + public abstract void setValue(Object pojo, Object value) + throws UnsupportedOperationException, IllegalArgumentException; + + /** + * Optional method that can be used to access the value of + * this member on given object, if this is a supported + * operation for member type. + *

+ * This is implemented for fields and no-argument + * member methods; but not for constructor parameters or + * other types of methods (like static methods) + */ + public abstract Object getValue(Object pojo) + throws UnsupportedOperationException, IllegalArgumentException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,286 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.util.ClassUtil; + +public final class AnnotatedMethod + extends AnnotatedWithParams + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + final protected transient Method _method; + + // // Simple lazy-caching: + + protected Class[] _paramClasses; + + /** + * Field that is used to make JDK serialization work with this + * object. + * + * @since 2.1 + */ + protected Serialization _serialization; + + /* + /***************************************************** + /* Life-cycle + /***************************************************** + */ + + public AnnotatedMethod(TypeResolutionContext ctxt, Method method, + AnnotationMap classAnn, AnnotationMap[] paramAnnotations) + { + super(ctxt, classAnn, paramAnnotations); + if (method == null) { + throw new IllegalArgumentException("Can not construct AnnotatedMethod with null Method"); + } + _method = method; + } + + /** + * Method used for JDK serialization support + * @since 2.1 + */ + protected AnnotatedMethod(Serialization ser) + { + super(null, null, null); + _method = null; + _serialization = ser; + } + + /** + * Method that constructs a new instance with settings (annotations, parameter annotations) + * of this instance, but with different physical {@link Method}. + */ + public AnnotatedMethod withMethod(Method m) { + return new AnnotatedMethod(_typeContext, m, _annotations, _paramAnnotations); + } + + @Override + public AnnotatedMethod withAnnotations(AnnotationMap ann) { + return new AnnotatedMethod(_typeContext, _method, ann, _paramAnnotations); + } + + /* + /***************************************************** + /* Annotated impl + /***************************************************** + */ + + @Override + public Method getAnnotated() { return _method; } + + @Override + public int getModifiers() { return _method.getModifiers(); } + + @Override + public String getName() { return _method.getName(); } + + /** + * For methods, this returns declared return type, which is only + * useful with getters (setters do not return anything; hence "void" + * type is returned here) + */ + @Override + public JavaType getType() { + return _typeContext.resolveType(_method.getGenericReturnType()); + } + + /** + * For methods, this returns declared return type, which is only + * useful with getters (setters do not usually return anything; + * hence "void" type is returned here) + */ + @Override + public Class getRawType() { + return _method.getReturnType(); + } + + @Override + public final Object call() throws Exception { + return _method.invoke(null); + } + + @Override + public final Object call(Object[] args) throws Exception { + return _method.invoke(null, args); + } + + @Override + public final Object call1(Object arg) throws Exception { + return _method.invoke(null, arg); + } + + /* + /******************************************************** + /* AnnotatedMember impl + /******************************************************** + */ + + @Override + public Class getDeclaringClass() { return _method.getDeclaringClass(); } + + @Override + public Method getMember() { return _method; } + + @Override + public void setValue(Object pojo, Object value) throws IllegalArgumentException + { + try { + _method.invoke(pojo, value); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Failed to setValue() with method " + +getFullName()+": "+e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException("Failed to setValue() with method " + +getFullName()+": "+e.getMessage(), e); + } + } + + @Override + public Object getValue(Object pojo) throws IllegalArgumentException + { + try { + return _method.invoke(pojo); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Failed to getValue() with method " + +getFullName()+": "+e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException("Failed to getValue() with method " + +getFullName()+": "+e.getMessage(), e); + } + } + + /* + /***************************************************** + /* Extended API, generic + /***************************************************** + */ + + @Override + public int getParameterCount() { + return getRawParameterTypes().length; + } + + public String getFullName() { + return getDeclaringClass().getName() + "#" + getName() + "(" + +getParameterCount()+" params)"; + } + + public Class[] getRawParameterTypes() + { + if (_paramClasses == null) { + _paramClasses = _method.getParameterTypes(); + } + return _paramClasses; + } + + public Type[] getGenericParameterTypes() { + return _method.getGenericParameterTypes(); + } + + @Override + public Class getRawParameterType(int index) + { + Class[] types = getRawParameterTypes(); + return (index >= types.length) ? null : types[index]; + } + + @Override + public JavaType getParameterType(int index) { + Type[] types = _method.getGenericParameterTypes(); + if (index >= types.length) { + return null; + } + return _typeContext.resolveType(types[index]); + } + + public Class getRawReturnType() { + return _method.getReturnType(); + } + + /** + * Helper method that can be used to check whether method returns + * a value or not; if return type declared as void, returns + * false, otherwise true + * + * @since 2.4 + */ + public boolean hasReturnType() { + Class rt = getRawReturnType(); + return (rt != Void.TYPE && rt != Void.class); + } + + /* + /******************************************************** + /* Other + /******************************************************** + */ + + @Override + public String toString() { + return "[method "+getFullName()+"]"; + } + + @Override + public int hashCode() { + return _method.getName().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + return ((AnnotatedMethod) o)._method == _method; + } + + /* + /********************************************************** + /* JDK serialization handling + /********************************************************** + */ + + Object writeReplace() { + return new AnnotatedMethod(new Serialization(_method)); + } + + Object readResolve() { + Class clazz = _serialization.clazz; + try { + Method m = clazz.getDeclaredMethod(_serialization.name, + _serialization.args); + // 06-Oct-2012, tatu: Has "lost" its security override, may need to force back + if (!m.isAccessible()) { + ClassUtil.checkAndFixAccess(m, false); + } + return new AnnotatedMethod(null, m, null, null); + } catch (Exception e) { + throw new IllegalArgumentException("Could not find method '"+_serialization.name + +"' from Class '"+clazz.getName()); + } + } + + /** + * Helper class that is used as the workaround to persist + * Field references. It basically just stores declaring class + * and field name. + */ + private final static class Serialization + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + protected Class clazz; + protected String name; + protected Class[] args; + + public Serialization(Method setter) { + clazz = setter.getDeclaringClass(); + name = setter.getName(); + args = setter.getParameterTypes(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,85 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.Method; +import java.util.*; + +/** + * Simple helper class used to keep track of collection of + * {@link AnnotatedMethod}s, accessible by lookup. Lookup + * is usually needed for augmenting and overriding annotations. + */ +public final class AnnotatedMethodMap + implements Iterable +{ + protected LinkedHashMap _methods; + + public AnnotatedMethodMap() { } + + /** + * Method called to add specified annotated method in the Map. + */ + public void add(AnnotatedMethod am) + { + if (_methods == null) { + _methods = new LinkedHashMap(); + } + _methods.put(new MemberKey(am.getAnnotated()), am); + } + + /** + * Method called to remove specified method, assuming + * it exists in the Map + */ + public AnnotatedMethod remove(AnnotatedMethod am) + { + return remove(am.getAnnotated()); + } + + public AnnotatedMethod remove(Method m) + { + if (_methods != null) { + return _methods.remove(new MemberKey(m)); + } + return null; + } + + public boolean isEmpty() { + return (_methods == null || _methods.size() == 0); + } + + public int size() { + return (_methods == null) ? 0 : _methods.size(); + } + + public AnnotatedMethod find(String name, Class[] paramTypes) + { + if (_methods == null) { + return null; + } + return _methods.get(new MemberKey(name, paramTypes)); + } + + public AnnotatedMethod find(Method m) + { + if (_methods == null) { + return null; + } + return _methods.get(new MemberKey(m)); + } + + /* + /********************************************************** + /* Iterable implementation (for iterating over values) + /********************************************************** + */ + + @Override + public Iterator iterator() + { + if (_methods != null) { + return _methods.values().iterator(); + } + List empty = Collections.emptyList(); + return empty.iterator(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,174 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.*; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Object that represents method parameters, mostly so that associated + * annotations can be processed conveniently. Note that many of accessors + * can not return meaningful values since parameters do not have stand-alone + * JDK objects associated; so access should mostly be limited to checking + * annotation values which are properly aggregated and included. + */ +public final class AnnotatedParameter + extends AnnotatedMember +{ + private static final long serialVersionUID = 1L; + + /** + * Member (method, constructor) that this parameter belongs to + */ + protected final AnnotatedWithParams _owner; + + /** + * JDK type of the parameter, possibly contains generic type information + */ + protected final JavaType _type; + + /** + * Index of the parameter within argument list + */ + protected final int _index; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public AnnotatedParameter(AnnotatedWithParams owner, JavaType type, AnnotationMap annotations, + int index) + { + super((owner == null) ? null : owner.getTypeContext(), annotations); + _owner = owner; + _type = type; + _index = index; + } + + @Override + public AnnotatedParameter withAnnotations(AnnotationMap ann) { + if (ann == _annotations) { + return this; + } + return _owner.replaceParameterAnnotations(_index, ann); + } + + /* + /********************************************************** + /* Annotated impl + /********************************************************** + */ + + /** + * Since there is no matching JDK element, this method will + * always return null + */ + @Override + public AnnotatedElement getAnnotated() { return null; } + + /** + * Returns modifiers of the constructor, as parameters do not + * have independent modifiers. + */ + @Override + public int getModifiers() { return _owner.getModifiers(); } + + /** + * Parameters have no names in bytecode (unlike in source code), + * will always return empty String (""). + */ + @Override + public String getName() { return ""; } + + @Override + public Class getRawType() { + return _type.getRawClass(); + } + + @Override + public JavaType getType() { + return _typeContext.resolveType(_type); + } + + /* + /********************************************************** + /* AnnotatedMember extras + /********************************************************** + */ + + @Override + public Class getDeclaringClass() { + return _owner.getDeclaringClass(); + } + + @Override + public Member getMember() { + /* This is bit tricky: since there is no JDK equivalent; can either + * return null or owner... let's do latter, for now. + */ + return _owner.getMember(); + } + + @Override + public void setValue(Object pojo, Object value) throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Cannot call setValue() on constructor parameter of " + +getDeclaringClass().getName()); + } + + @Override + public Object getValue(Object pojo) throws UnsupportedOperationException + { + throw new UnsupportedOperationException("Cannot call getValue() on constructor parameter of " + +getDeclaringClass().getName()); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + public Type getParameterType() { return _type; } + + /** + * Accessor for 'owner' of this parameter; method or constructor that + * has this parameter as member of its argument list. + * + * @return Owner (member or creator) object of this parameter + */ + public AnnotatedWithParams getOwner() { return _owner; } + + /** + * Accessor for index of this parameter within argument list + * + * @return Index of this parameter within argument list + */ + public int getIndex() { return _index; } + + /* + /******************************************************** + /* Other + /******************************************************** + */ + + @Override + public int hashCode() { + return _owner.hashCode() + _index; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + AnnotatedParameter other = (AnnotatedParameter) o; + return other._owner.equals(_owner) && (other._index == _index); + } + + @Override + public String toString() { + return "[parameter #"+getIndex()+", annotations: "+_annotations+"]"; + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,157 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Intermediate base class that encapsulates features that + * constructors and methods share. + */ +public abstract class AnnotatedWithParams + extends AnnotatedMember +{ + private static final long serialVersionUID = 1L; + + /** + * Annotations associated with parameters of the annotated + * entity (method or constructor parameters) + */ + protected final AnnotationMap[] _paramAnnotations; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected AnnotatedWithParams(TypeResolutionContext ctxt, AnnotationMap annotations, AnnotationMap[] paramAnnotations) + { + super(ctxt, annotations); + _paramAnnotations = paramAnnotations; + } + + /** + * Method called to override a method parameter annotation, + * usually due to a mix-in + * annotation masking or overriding an annotation 'real' method + * has. + */ + public final void addOrOverrideParam(int paramIndex, Annotation a) + { + AnnotationMap old = _paramAnnotations[paramIndex]; + if (old == null) { + old = new AnnotationMap(); + _paramAnnotations[paramIndex] = old; + } + old.add(a); + } + + /** + * Method called by parameter object when an augmented instance is created; + * needs to replace parameter with new instance + */ + protected AnnotatedParameter replaceParameterAnnotations(int index, AnnotationMap ann) + { + _paramAnnotations[index] = ann; + return getParameter(index); + } + + /* + /********************************************************** + /* Helper methods for subclasses + /********************************************************** + */ + + /* + protected JavaType getType(TypeBindings bindings, TypeVariable[] typeParams) + { + // [JACKSON-468] Need to consider local type binding declarations too... + if (typeParams != null && typeParams.length > 0) { + bindings = bindings.childInstance(); + for (TypeVariable var : typeParams) { + String name = var.getName(); + // to prevent infinite loops, need to first add placeholder (">" etc) + bindings._addPlaceholder(name); + // About only useful piece of information is the lower bound (which is at least Object.class) + Type lowerBound = var.getBounds()[0]; + JavaType type = (lowerBound == null) ? TypeFactory.unknownType() + : bindings.resolveType(lowerBound); + bindings.addBinding(var.getName(), type); + } + } + return bindings.resolveType(getGenericType()); + } + */ + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + public final AnnotationMap getParameterAnnotations(int index) + { + if (_paramAnnotations != null) { + if (index >= 0 && index < _paramAnnotations.length) { + return _paramAnnotations[index]; + } + } + return null; + } + + public final AnnotatedParameter getParameter(int index) { + return new AnnotatedParameter(this, getParameterType(index), + getParameterAnnotations(index), index); + } + + public abstract int getParameterCount(); + + public abstract Class getRawParameterType(int index); + + /** + * @since 2.7 + */ + public abstract JavaType getParameterType(int index); + + /** + * @deprecated Since 2.7, remove in 2.8 + */ + @Deprecated + public final Type getGenericParameterType(int index) { + return getRawParameterType(index); + } + + public final int getAnnotationCount() { return _annotations.size(); } + + /** + * Method that can be used to (try to) call this object without arguments. + * This may succeed or fail, depending on expected number + * of arguments: caller needs to take care to pass correct number. + * Exceptions are thrown directly from actual low-level call. + *

+ * Note: only works for constructors and static methods. + */ + public abstract Object call() throws Exception; + + /** + * Method that can be used to (try to) call this object with specified arguments. + * This may succeed or fail, depending on expected number + * of arguments: caller needs to take care to pass correct number. + * Exceptions are thrown directly from actual low-level call. + *

+ * Note: only works for constructors and static methods. + */ + public abstract Object call(Object[] args) throws Exception; + + /** + * Method that can be used to (try to) call this object with single arguments. + * This may succeed or fail, depending on expected number + * of arguments: caller needs to take care to pass correct number. + * Exceptions are thrown directly from actual low-level call. + *

+ * Note: only works for constructors and static methods. + */ + public abstract Object call1(Object arg) throws Exception; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,709 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Helper class that allows using 2 introspectors such that one + * introspector acts as the primary one to use; and second one + * as a fallback used if the primary does not provide conclusive + * or useful result for a method. + *

+ * An obvious consequence of priority is that it is easy to construct + * longer chains of introspectors by linking multiple pairs. + * Currently most likely combination is that of using the default + * Jackson provider, along with JAXB annotation introspector. + *

+ * Note: up until 2.0, this class was an inner class of + * {@link AnnotationIntrospector}; moved here for convenience. + * + * @since 2.1 + */ +public class AnnotationIntrospectorPair + extends AnnotationIntrospector + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final AnnotationIntrospector _primary, _secondary; + + public AnnotationIntrospectorPair(AnnotationIntrospector p, AnnotationIntrospector s) + { + _primary = p; + _secondary = s; + } + + @Override + public Version version() { + return _primary.version(); + } + + /** + * Helper method for constructing a Pair from two given introspectors (if + * neither is null); or returning non-null introspector if one is null + * (and return just null if both are null) + */ + public static AnnotationIntrospector create(AnnotationIntrospector primary, + AnnotationIntrospector secondary) + { + if (primary == null) { + return secondary; + } + if (secondary == null) { + return primary; + } + return new AnnotationIntrospectorPair(primary, secondary); + } + + @Override + public Collection allIntrospectors() { + return allIntrospectors(new ArrayList()); + } + + @Override + public Collection allIntrospectors(Collection result) + { + _primary.allIntrospectors(result); + _secondary.allIntrospectors(result); + return result; + } + + // // // Generic annotation properties, lookup + + @Override + public boolean isAnnotationBundle(Annotation ann) { + return _primary.isAnnotationBundle(ann) || _secondary.isAnnotationBundle(ann); + } + + /* + /****************************************************** + /* General class annotations + /****************************************************** + */ + + @Override + public PropertyName findRootName(AnnotatedClass ac) + { + PropertyName name1 = _primary.findRootName(ac); + if (name1 == null) { + return _secondary.findRootName(ac); + } + if (name1.hasSimpleName()) { + return name1; + } + // name1 is empty; how about secondary? + PropertyName name2 = _secondary.findRootName(ac); + return (name2 == null) ? name1 : name2; + } + + @Override + @Deprecated // since 2.6 + public String[] findPropertiesToIgnore(Annotated ac) { + String[] result = _primary.findPropertiesToIgnore(ac); + if (result == null) { + result = _secondary.findPropertiesToIgnore(ac); + } + return result; + } + + @Override + public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) { + String[] result = _primary.findPropertiesToIgnore(ac, forSerialization); + if (result == null) { + result = _secondary.findPropertiesToIgnore(ac, forSerialization); + } + return result; + } + + @Override + public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) + { + Boolean result = _primary.findIgnoreUnknownProperties(ac); + if (result == null) { + result = _secondary.findIgnoreUnknownProperties(ac); + } + return result; + } + + @Override + public Boolean isIgnorableType(AnnotatedClass ac) + { + Boolean result = _primary.isIgnorableType(ac); + if (result == null) { + result = _secondary.isIgnorableType(ac); + } + return result; + } + + @Override + public Object findFilterId(Annotated ann) + { + Object id = _primary.findFilterId(ann); + if (id == null) { + id = _secondary.findFilterId(ann); + } + return id; + } + + @Override + public Object findNamingStrategy(AnnotatedClass ac) + { + Object str = _primary.findNamingStrategy(ac); + if (str == null) { + str = _secondary.findNamingStrategy(ac); + } + return str; + } + + @Override + public String findClassDescription(AnnotatedClass ac) { + String str = _primary.findClassDescription(ac); + if ((str == null) || str.isEmpty()) { + str = _secondary.findClassDescription(ac); + } + return str; + } + + /* + /****************************************************** + /* Property auto-detection + /****************************************************** + */ + + @Override + public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, + VisibilityChecker checker) + { + /* Note: to have proper priorities, we must actually call delegatees + * in reverse order: + */ + checker = _secondary.findAutoDetectVisibility(ac, checker); + return _primary.findAutoDetectVisibility(ac, checker); + } + + /* + /****************************************************** + /* Type handling + /****************************************************** + */ + + @Override + public TypeResolverBuilder findTypeResolver(MapperConfig config, + AnnotatedClass ac, JavaType baseType) + { + TypeResolverBuilder b = _primary.findTypeResolver(config, ac, baseType); + if (b == null) { + b = _secondary.findTypeResolver(config, ac, baseType); + } + return b; + } + + @Override + public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, + AnnotatedMember am, JavaType baseType) + { + TypeResolverBuilder b = _primary.findPropertyTypeResolver(config, am, baseType); + if (b == null) { + b = _secondary.findPropertyTypeResolver(config, am, baseType); + } + return b; + } + + @Override + public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, + AnnotatedMember am, JavaType baseType) + { + TypeResolverBuilder b = _primary.findPropertyContentTypeResolver(config, am, baseType); + if (b == null) { + b = _secondary.findPropertyContentTypeResolver(config, am, baseType); + } + return b; + } + + @Override + public List findSubtypes(Annotated a) + { + List types1 = _primary.findSubtypes(a); + List types2 = _secondary.findSubtypes(a); + if (types1 == null || types1.isEmpty()) return types2; + if (types2 == null || types2.isEmpty()) return types1; + ArrayList result = new ArrayList(types1.size() + types2.size()); + result.addAll(types1); + result.addAll(types2); + return result; + } + + @Override + public String findTypeName(AnnotatedClass ac) + { + String name = _primary.findTypeName(ac); + if (name == null || name.length() == 0) { + name = _secondary.findTypeName(ac); + } + return name; + } + + // // // General member (field, method/constructor) annotations + + @Override + public ReferenceProperty findReferenceType(AnnotatedMember member) { + ReferenceProperty r = _primary.findReferenceType(member); + return (r == null) ? _secondary.findReferenceType(member) : r; + } + + @Override + public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) { + NameTransformer r = _primary.findUnwrappingNameTransformer(member); + return (r == null) ? _secondary.findUnwrappingNameTransformer(member) : r; + } + + @Override + public Object findInjectableValueId(AnnotatedMember m) { + Object r = _primary.findInjectableValueId(m); + return (r == null) ? _secondary.findInjectableValueId(m) : r; + } + + @Override + public boolean hasIgnoreMarker(AnnotatedMember m) { + return _primary.hasIgnoreMarker(m) || _secondary.hasIgnoreMarker(m); + } + + @Override + public Boolean hasRequiredMarker(AnnotatedMember m) { + Boolean r = _primary.hasRequiredMarker(m); + return (r == null) ? _secondary.hasRequiredMarker(m) : r; + } + + // // // Serialization: general annotations + + @Override + public Object findSerializer(Annotated am) { + Object r = _primary.findSerializer(am); + return _isExplicitClassOrOb(r, JsonSerializer.None.class) + ? r : _secondary.findSerializer(am); + } + + @Override + public Object findKeySerializer(Annotated a) { + Object r = _primary.findKeySerializer(a); + return _isExplicitClassOrOb(r, JsonSerializer.None.class) + ? r : _secondary.findKeySerializer(a); + } + + @Override + public Object findContentSerializer(Annotated a) { + Object r = _primary.findContentSerializer(a); + return _isExplicitClassOrOb(r, JsonSerializer.None.class) + ? r : _secondary.findContentSerializer(a); + } + + @Override + public Object findNullSerializer(Annotated a) { + Object r = _primary.findNullSerializer(a); + return _isExplicitClassOrOb(r, JsonSerializer.None.class) + ? r : _secondary.findNullSerializer(a); + } + + @Deprecated + @Override + public JsonInclude.Include findSerializationInclusion(Annotated a, + JsonInclude.Include defValue) + { + // note: call secondary first, to give lower priority + defValue = _secondary.findSerializationInclusion(a, defValue); + defValue = _primary.findSerializationInclusion(a, defValue); + return defValue; + } + + @Deprecated + @Override + public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue) + { + // note: call secondary first, to give lower priority + defValue = _secondary.findSerializationInclusionForContent(a, defValue); + defValue = _primary.findSerializationInclusionForContent(a, defValue); + return defValue; + } + + @Override + public JsonInclude.Value findPropertyInclusion(Annotated a) + { + JsonInclude.Value v2 = _secondary.findPropertyInclusion(a); + JsonInclude.Value v1 = _primary.findPropertyInclusion(a); + + if (v2 == null) { // shouldn't occur but + return v1; + } + return v2.withOverrides(v1); + } + + @Override + public JsonSerialize.Typing findSerializationTyping(Annotated a) { + JsonSerialize.Typing r = _primary.findSerializationTyping(a); + return (r == null) ? _secondary.findSerializationTyping(a) : r; + } + + @Override + public Object findSerializationConverter(Annotated a) { + Object r = _primary.findSerializationConverter(a); + return (r == null) ? _secondary.findSerializationConverter(a) : r; + } + + @Override + public Object findSerializationContentConverter(AnnotatedMember a) { + Object r = _primary.findSerializationContentConverter(a); + return (r == null) ? _secondary.findSerializationContentConverter(a) : r; + } + + @Override + public Class[] findViews(Annotated a) { + /* Theoretically this could be trickier, if multiple introspectors + * return non-null entries. For now, though, we'll just consider + * first one to return non-null to win. + */ + Class[] result = _primary.findViews(a); + if (result == null) { + result = _secondary.findViews(a); + } + return result; + } + + @Override + public Boolean isTypeId(AnnotatedMember member) { + Boolean b = _primary.isTypeId(member); + return (b == null) ? _secondary.isTypeId(member) : b; + } + + @Override + public ObjectIdInfo findObjectIdInfo(Annotated ann) { + ObjectIdInfo r = _primary.findObjectIdInfo(ann); + return (r == null) ? _secondary.findObjectIdInfo(ann) : r; + } + + @Override + public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { + // to give precedence for primary, must start with secondary: + objectIdInfo = _secondary.findObjectReferenceInfo(ann, objectIdInfo); + objectIdInfo = _primary.findObjectReferenceInfo(ann, objectIdInfo); + return objectIdInfo; + } + + @Override + public JsonFormat.Value findFormat(Annotated ann) { + JsonFormat.Value v1 = _primary.findFormat(ann); + JsonFormat.Value v2 = _secondary.findFormat(ann); + if (v2 == null) { // shouldn't occur but just in case + return v1; + } + return v2.withOverrides(v1); + } + + @Override + public PropertyName findWrapperName(Annotated ann) { + PropertyName name = _primary.findWrapperName(ann); + if (name == null) { + name = _secondary.findWrapperName(ann); + } else if (name == PropertyName.USE_DEFAULT) { + // does the other introspector have a better idea? + PropertyName name2 = _secondary.findWrapperName(ann); + if (name2 != null) { + name = name2; + } + } + return name; + } + + @Override + public String findPropertyDefaultValue(Annotated ann) { + String str = _primary.findPropertyDefaultValue(ann); + return (str == null || str.isEmpty()) ? _secondary.findPropertyDefaultValue(ann) : str; + } + + @Override + public String findPropertyDescription(Annotated ann) { + String r = _primary.findPropertyDescription(ann); + return (r == null) ? _secondary.findPropertyDescription(ann) : r; + } + + @Override + public Integer findPropertyIndex(Annotated ann) { + Integer r = _primary.findPropertyIndex(ann); + return (r == null) ? _secondary.findPropertyIndex(ann) : r; + } + + @Override + public String findImplicitPropertyName(AnnotatedMember param) { + String r = _primary.findImplicitPropertyName(param); + return (r == null) ? _secondary.findImplicitPropertyName(param) : r; + } + + @Override + public JsonProperty.Access findPropertyAccess(Annotated ann) { + JsonProperty.Access acc = _primary.findPropertyAccess(ann); + if ((acc != null) && (acc != JsonProperty.Access.AUTO)) { + return acc; + } + acc = _secondary.findPropertyAccess(ann); + if (acc != null) { + return acc; + } + return JsonProperty.Access.AUTO; + } + + @Override // since 2.7 + public AnnotatedMethod resolveSetterConflict(MapperConfig config, + AnnotatedMethod setter1, AnnotatedMethod setter2) + { + AnnotatedMethod res = _primary.resolveSetterConflict(config, setter1, setter2); + if (res == null) { + res = _secondary.resolveSetterConflict(config, setter1, setter2); + } + return res; + } + + // // // Serialization: type refinements + + @Override // since 2.7 + public JavaType refineSerializationType(MapperConfig config, + Annotated a, JavaType baseType) throws JsonMappingException + { + JavaType t = _secondary.refineSerializationType(config, a, baseType); + return _primary.refineSerializationType(config, a, t); + } + + @Override + @Deprecated + public Class findSerializationType(Annotated a) { + Class r = _primary.findSerializationType(a); + return (r == null) ? _secondary.findSerializationType(a) : r; + } + + @Override + @Deprecated + public Class findSerializationKeyType(Annotated am, JavaType baseType) { + Class r = _primary.findSerializationKeyType(am, baseType); + return (r == null) ? _secondary.findSerializationKeyType(am, baseType) : r; + } + + @Override + @Deprecated + public Class findSerializationContentType(Annotated am, JavaType baseType) { + Class r = _primary.findSerializationContentType(am, baseType); + return (r == null) ? _secondary.findSerializationContentType(am, baseType) : r; + } + + // // // Serialization: class annotations + + @Override + public String[] findSerializationPropertyOrder(AnnotatedClass ac) { + String[] r = _primary.findSerializationPropertyOrder(ac); + return (r == null) ? _secondary.findSerializationPropertyOrder(ac) : r; + } + + @Override + public Boolean findSerializationSortAlphabetically(Annotated ann) { + Boolean r = _primary.findSerializationSortAlphabetically(ann); + return (r == null) ? _secondary.findSerializationSortAlphabetically(ann) : r; + } + + @Override + public void findAndAddVirtualProperties(MapperConfig config, AnnotatedClass ac, + List properties) { + // first secondary, then primary, to give proper precedence + _primary.findAndAddVirtualProperties(config, ac, properties); + _secondary.findAndAddVirtualProperties(config, ac, properties); + } + + // // // Serialization: property annotations + + @Override + public PropertyName findNameForSerialization(Annotated a) { + PropertyName n = _primary.findNameForSerialization(a); + // note: "use default" should not block explicit answer, so: + if (n == null) { + n = _secondary.findNameForSerialization(a); + } else if (n == PropertyName.USE_DEFAULT) { + PropertyName n2 = _secondary.findNameForSerialization(a); + if (n2 != null) { + n = n2; + } + } + return n; + } + + @Override + public boolean hasAsValueAnnotation(AnnotatedMethod am) { + return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am); + } + + @Override + public String findEnumValue(Enum value) { + String r = _primary.findEnumValue(value); + return (r == null) ? _secondary.findEnumValue(value) : r; + } + + @Override + public String[] findEnumValues(Class enumType, Enum[] enumValues, String[] names) { + // reverse order to give _primary higher precedence + names = _secondary.findEnumValues(enumType, enumValues, names); + names = _primary.findEnumValues(enumType, enumValues, names); + return names; + } + + // // // Deserialization: general annotations + + @Override + public Object findDeserializer(Annotated am) { + Object r = _primary.findDeserializer(am); + return _isExplicitClassOrOb(r, JsonDeserializer.None.class) + ? r : _secondary.findDeserializer(am); + } + + @Override + public Object findKeyDeserializer(Annotated am) { + Object r = _primary.findKeyDeserializer(am); + return _isExplicitClassOrOb(r, KeyDeserializer.None.class) + ? r : _secondary.findKeyDeserializer(am); + } + + @Override + public Object findContentDeserializer(Annotated am) { + Object r = _primary.findContentDeserializer(am); + return _isExplicitClassOrOb(r, JsonDeserializer.None.class) + ? r : _secondary.findContentDeserializer(am); + } + + @Override + public Object findDeserializationConverter(Annotated a) { + Object ob = _primary.findDeserializationConverter(a); + return (ob == null) ? _secondary.findDeserializationConverter(a) : ob; + } + + @Override + public Object findDeserializationContentConverter(AnnotatedMember a) { + Object ob = _primary.findDeserializationContentConverter(a); + return (ob == null) ? _secondary.findDeserializationContentConverter(a) : ob; + } + + // // // Deserialization: type refinements + + // since 2.7 + @Override + public JavaType refineDeserializationType(MapperConfig config, + Annotated a, JavaType baseType) throws JsonMappingException + { + JavaType t = _secondary.refineDeserializationType(config, a, baseType); + return _primary.refineDeserializationType(config, a, t); + } + + @Override + @Deprecated + public Class findDeserializationType(Annotated am, JavaType baseType) { + Class r = _primary.findDeserializationType(am, baseType); + return (r != null) ? r : _secondary.findDeserializationType(am, baseType); + } + + @Override + @Deprecated + public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { + Class result = _primary.findDeserializationKeyType(am, baseKeyType); + return (result == null) ? _secondary.findDeserializationKeyType(am, baseKeyType) : result; + } + + @Override + @Deprecated + public Class findDeserializationContentType(Annotated am, JavaType baseContentType) { + Class result = _primary.findDeserializationContentType(am, baseContentType); + return (result == null) ? _secondary.findDeserializationContentType(am, baseContentType) : result; + } + + // // // Deserialization: class annotations + + @Override + public Object findValueInstantiator(AnnotatedClass ac) { + Object result = _primary.findValueInstantiator(ac); + return (result == null) ? _secondary.findValueInstantiator(ac) : result; + } + + @Override + public Class findPOJOBuilder(AnnotatedClass ac) { + Class result = _primary.findPOJOBuilder(ac); + return (result == null) ? _secondary.findPOJOBuilder(ac) : result; + } + + @Override + public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) { + JsonPOJOBuilder.Value result = _primary.findPOJOBuilderConfig(ac); + return (result == null) ? _secondary.findPOJOBuilderConfig(ac) : result; + } + + // // // Deserialization: method annotations + + @Override + public PropertyName findNameForDeserialization(Annotated a) + { + // note: "use default" should not block explicit answer, so: + PropertyName n = _primary.findNameForDeserialization(a); + if (n == null) { + n = _secondary.findNameForDeserialization(a); + } else if (n == PropertyName.USE_DEFAULT) { + PropertyName n2 = _secondary.findNameForDeserialization(a); + if (n2 != null) { + n = n2; + } + } + return n; + } + + @Override + public boolean hasAnySetterAnnotation(AnnotatedMethod am) { + return _primary.hasAnySetterAnnotation(am) || _secondary.hasAnySetterAnnotation(am); + } + + @Override + public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { + return _primary.hasAnyGetterAnnotation(am) || _secondary.hasAnyGetterAnnotation(am); + } + + @Override + public boolean hasCreatorAnnotation(Annotated a) { + return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a); + } + + @Override + public JsonCreator.Mode findCreatorBinding(Annotated a) { + JsonCreator.Mode mode = _primary.findCreatorBinding(a); + if (mode != null) { + return mode; + } + return _secondary.findCreatorBinding(a); + } + + protected boolean _isExplicitClassOrOb(Object maybeCls, Class implicit) { + if (maybeCls == null) { + return false; + } + if (!(maybeCls instanceof Class)) { + return true; + } + Class cls = (Class) maybeCls; + return (cls != implicit && !ClassUtil.isBogusClass(cls)); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotationMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotationMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/AnnotationMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,138 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.annotation.Annotation; +import java.util.*; + +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * Simple helper class used to keep track of collection of + * Jackson Annotations associated with annotatable things + * (methods, constructors, classes). + * Note that only Jackson-owned annotations are tracked (for now?). + */ +public final class AnnotationMap implements Annotations +{ + protected HashMap,Annotation> _annotations; + + public AnnotationMap() { } + + private AnnotationMap(HashMap,Annotation> a) { + _annotations = a; + } + + @SuppressWarnings("unchecked") + @Override + public A get(Class cls) + { + if (_annotations == null) { + return null; + } + return (A) _annotations.get(cls); + } + + public boolean has(Class cls) + { + if (_annotations == null) { + return false; + } + return _annotations.containsKey(cls); + } + + /** + * Helper method that can be used for a "bulk" check to see if at least + * one of given annotation types is included within this map. + * + * @since 2.7 + */ + public boolean hasOneOf(Class[] annoClasses) { + if (_annotations != null) { + for (int i = 0, end = annoClasses.length; i < end; ++i) { + if (_annotations.containsKey(annoClasses[i])) { + return true; + } + } + } + return false; + } + + /** + * @since 2.3 + */ + public Iterable annotations() { + if (_annotations == null || _annotations.size() == 0) { + return Collections.emptyList(); + } + return _annotations.values(); + } + + public static AnnotationMap merge(AnnotationMap primary, AnnotationMap secondary) + { + if (primary == null || primary._annotations == null || primary._annotations.isEmpty()) { + return secondary; + } + if (secondary == null || secondary._annotations == null || secondary._annotations.isEmpty()) { + return primary; + } + HashMap,Annotation> annotations = new HashMap,Annotation>(); + // add secondary ones first + for (Annotation ann : secondary._annotations.values()) { + annotations.put(ann.annotationType(), ann); + } + // to be overridden by primary ones + for (Annotation ann : primary._annotations.values()) { + annotations.put(ann.annotationType(), ann); + } + return new AnnotationMap(annotations); + } + + @Override + public int size() { + return (_annotations == null) ? 0 : _annotations.size(); + } + + /** + * Method called to add specified annotation in the Map, but + * only if it didn't yet exist. + */ + public boolean addIfNotPresent(Annotation ann) + { + if (_annotations == null || !_annotations.containsKey(ann.annotationType())) { + _add(ann); + return true; + } + return false; + } + + /** + * Method called to add specified annotation in the Map. + * + * @return True if the addition changed the contents, that is, this map did not + * already have specified annotation + */ + public boolean add(Annotation ann) { + return _add(ann); + } + + @Override + public String toString() { + if (_annotations == null) { + return "[null]"; + } + return _annotations.toString(); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected final boolean _add(Annotation ann) { + if (_annotations == null) { + _annotations = new HashMap,Annotation>(); + } + Annotation previous = _annotations.put(ann.annotationType(), ann); + return (previous == null) || !previous.equals(ann); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,703 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.type.TypeBindings; +import com.fasterxml.jackson.databind.util.Annotations; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Default {@link BeanDescription} implementation used by Jackson. + *

+ * Although sub-classing is a theoretical possibility there are no known + * use cases for that, nor is such usage tested or supported. + * Separation from API is mostly to isolate some implementation details + * here and keep API simple. + *

+ * Note that since 2.6 this class has been a thin shell around + * {@link POJOPropertiesCollector}, which handles most of actual work. + */ +public class BasicBeanDescription extends BeanDescription +{ + /* + /********************************************************** + /* General configuration + /********************************************************** + */ + + /** + * We will hold a reference to the collector in cases where + * information is lazily accessed and constructed; properties + * are only accessed when they are actually needed. + */ + final protected POJOPropertiesCollector _propCollector; + + final protected MapperConfig _config; + + final protected AnnotationIntrospector _annotationIntrospector; + + /** + * Information collected about the class introspected. + */ + final protected AnnotatedClass _classInfo; + + /** + * We may need type bindings for the bean type. If so, we'll + * construct it lazily + */ + protected TypeBindings _bindings; + + /* + /********************************************************** + /* Member information + /********************************************************** + */ + + /** + * Properties collected for the POJO; initialized as needed. + */ + protected List _properties; + + /** + * Details of Object Id to include, if any + */ + protected ObjectIdInfo _objectIdInfo; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected BasicBeanDescription(POJOPropertiesCollector coll, + JavaType type, AnnotatedClass classDef) + { + super(type); + _propCollector = coll; + _config = coll.getConfig(); + _annotationIntrospector = (_config == null) ? null : _config.getAnnotationIntrospector(); + _classInfo = classDef; + } + + /** + * Alternate constructor used in cases where property information is not needed, + * only class info. + */ + protected BasicBeanDescription(MapperConfig config, + JavaType type, AnnotatedClass classDef, List props) + { + super(type); + _propCollector = null; + _config = config; + _annotationIntrospector = (_config == null) ? null : _config.getAnnotationIntrospector(); + _classInfo = classDef; + _properties = props; + } + + protected BasicBeanDescription(POJOPropertiesCollector coll) + { + this(coll, coll.getType(), coll.getClassDef()); + _objectIdInfo = coll.getObjectIdInfo(); + } + + /** + * Factory method to use for constructing an instance to use for building + * deserializers. + */ + public static BasicBeanDescription forDeserialization(POJOPropertiesCollector coll) { + return new BasicBeanDescription(coll); + } + + /** + * Factory method to use for constructing an instance to use for building + * serializers. + */ + public static BasicBeanDescription forSerialization(POJOPropertiesCollector coll) { + return new BasicBeanDescription(coll); + } + + /** + * Factory method to use for constructing an instance to use for purposes + * other than building serializers or deserializers; will only have information + * on class, not on properties. + */ + public static BasicBeanDescription forOtherUse(MapperConfig config, + JavaType type, AnnotatedClass ac) + { + return new BasicBeanDescription(config, type, + ac, Collections.emptyList()); + } + + protected List _properties() { + if (_properties == null) { + _properties = _propCollector.getProperties(); + } + return _properties; + } + + /* + /********************************************************** + /* Limited modifications by core databind functionality + /********************************************************** + */ + + /** + * Method that can be used to prune unwanted properties, during + * construction of serializers and deserializers. + * Use with utmost care, if at all... + * + * @since 2.1 + */ + public boolean removeProperty(String propName) + { + Iterator it = _properties().iterator(); + while (it.hasNext()) { + BeanPropertyDefinition prop = it.next(); + if (prop.getName().equals(propName)) { + it.remove(); + return true; + } + } + return false; + } + + public boolean addProperty(BeanPropertyDefinition def) + { + // first: ensure we do not have such property + if (hasProperty(def.getFullName())) { + return false; + } + _properties().add(def); + return true; + } + + /** + * @since 2.6 + */ + public boolean hasProperty(PropertyName name) { + return findProperty(name) != null; + } + + /** + * @since 2.6 + */ + public BeanPropertyDefinition findProperty(PropertyName name) + { + for (BeanPropertyDefinition prop : _properties()) { + if (prop.hasName(name)) { + return prop; + } + } + return null; + } + + /* + /********************************************************** + /* Simple accessors from BeanDescription + /********************************************************** + */ + + @Override + public AnnotatedClass getClassInfo() { return _classInfo; } + + @Override + public ObjectIdInfo getObjectIdInfo() { return _objectIdInfo; } + + @Override + public List findProperties() { + return _properties(); + } + + @Override + public AnnotatedMethod findJsonValueMethod() { + return (_propCollector == null) ? null + : _propCollector.getJsonValueMethod(); + } + + @Override + public Set getIgnoredPropertyNames() { + Set ign = (_propCollector == null) ? null + : _propCollector.getIgnoredPropertyNames(); + if (ign == null) { + return Collections.emptySet(); + } + return ign; + } + + @Override + public boolean hasKnownClassAnnotations() { + return _classInfo.hasAnnotations(); + } + + @Override + public Annotations getClassAnnotations() { + return _classInfo.getAnnotations(); + } + + @Override + @Deprecated // since 2.7 + public TypeBindings bindingsForBeanType() { + return _type.getBindings(); + } + + @Override + public JavaType resolveType(java.lang.reflect.Type jdkType) { + if (jdkType == null) { + return null; + } + return _config.getTypeFactory().constructType(jdkType, _type.getBindings()); + } + + @Override + public AnnotatedConstructor findDefaultConstructor() { + return _classInfo.getDefaultConstructor(); + } + + @Override + public AnnotatedMethod findAnySetter() throws IllegalArgumentException + { + AnnotatedMethod anySetter = (_propCollector == null) ? null + : _propCollector.getAnySetterMethod(); + if (anySetter != null) { + /* Also, let's be somewhat strict on how field name is to be + * passed; String, Object make sense, others not + * so much. + */ + /* !!! 18-May-2009, tatu: how about enums? Can add support if + * requested; easy enough for devs to add support within + * method. + */ + Class type = anySetter.getRawParameterType(0); + if (type != String.class && type != Object.class) { + throw new IllegalArgumentException("Invalid 'any-setter' annotation on method "+anySetter.getName()+"(): first argument not of type String or Object, but "+type.getName()); + } + } + return anySetter; + } + + @Override + public Map findInjectables() { + if (_propCollector != null) { + return _propCollector.getInjectables(); + } + return Collections.emptyMap(); + } + + @Override + public List getConstructors() { + return _classInfo.getConstructors(); + } + + @Override + public Object instantiateBean(boolean fixAccess) { + AnnotatedConstructor ac = _classInfo.getDefaultConstructor(); + if (ac == null) { + return null; + } + if (fixAccess) { + ac.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + try { + return ac.getAnnotated().newInstance(); + } catch (Exception e) { + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + if (t instanceof Error) throw (Error) t; + if (t instanceof RuntimeException) throw (RuntimeException) t; + throw new IllegalArgumentException("Failed to instantiate bean of type "+_classInfo.getAnnotated().getName()+": ("+t.getClass().getName()+") "+t.getMessage(), t); + } + } + + /* + /********************************************************** + /* Simple accessors, extended + /********************************************************** + */ + + @Override + public AnnotatedMethod findMethod(String name, Class[] paramTypes) { + return _classInfo.findMethod(name, paramTypes); + } + + /* + /********************************************************** + /* General per-class annotation introspection + /********************************************************** + */ + + @Override + public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue) + { + if (_annotationIntrospector != null) { + JsonFormat.Value v = _annotationIntrospector.findFormat(_classInfo); + if (v != null) { + return v; + } + } + return defValue; + } + + /* + /********************************************************** + /* Introspection for serialization + /********************************************************** + */ + + @Override + public Converter findSerializationConverter() + { + if (_annotationIntrospector == null) { + return null; + } + return _createConverter(_annotationIntrospector.findSerializationConverter(_classInfo)); + } + + /** + * Method for determining whether null properties should be written + * out for a Bean of introspected type. This is based on global + * feature (lowest priority, passed as argument) + * and per-class annotation (highest priority). + */ + @Override + public JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue) { + if (_annotationIntrospector != null) { + JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_classInfo); + if (incl != null) { + return defValue.withOverrides(incl); + } + } + return defValue; + } + + /** + * Method used to locate the method of introspected class that + * implements {@link com.fasterxml.jackson.annotation.JsonAnyGetter}. + * If no such method exists null is returned. + * If more than one are found, an exception is thrown. + */ + @Override + public AnnotatedMember findAnyGetter() throws IllegalArgumentException + { + AnnotatedMember anyGetter = (_propCollector == null) ? null + : _propCollector.getAnyGetter(); + if (anyGetter != null) { + /* For now let's require a Map; in future can add support for other + * types like perhaps Iterable? + */ + Class type = anyGetter.getRawType(); + if (!Map.class.isAssignableFrom(type)) { + throw new IllegalArgumentException("Invalid 'any-getter' annotation on method "+anyGetter.getName()+"(): return type is not instance of java.util.Map"); + } + } + return anyGetter; + } + + @Override + public Map findBackReferenceProperties() + { + HashMap result = null; +// boolean hasIgnored = (_ignoredPropertyNames != null); + + for (BeanPropertyDefinition property : _properties()) { + /* 23-Sep-2014, tatu: As per [Databind#426], we _should_ try to avoid + * calling accessor, as it triggers exception from seeming conflict. + * But the problem is that _ignoredPropertyNames here only contains + * ones ignored on per-property annotations, but NOT class annotations... + * so commented out part does not work, alas + */ + /* + if (hasIgnored && _ignoredPropertyNames.contains(property.getName())) { + continue; + } + */ + AnnotatedMember am = property.getMutator(); + if (am == null) { + continue; + } + AnnotationIntrospector.ReferenceProperty refDef = _annotationIntrospector.findReferenceType(am); + if (refDef != null && refDef.isBackReference()) { + if (result == null) { + result = new HashMap(); + } + String refName = refDef.getName(); + if (result.put(refName, am) != null) { + throw new IllegalArgumentException("Multiple back-reference properties with name '"+refName+"'"); + } + } + } + return result; + } + + /* + /********************************************************** + /* Introspection for deserialization, factories + /********************************************************** + */ + + @Override + public List getFactoryMethods() + { + // must filter out anything that clearly is not a factory method + List candidates = _classInfo.getStaticMethods(); + if (candidates.isEmpty()) { + return candidates; + } + ArrayList result = new ArrayList(); + for (AnnotatedMethod am : candidates) { + if (isFactoryMethod(am)) { + result.add(am); + } + } + return result; + } + + @Override + public Constructor findSingleArgConstructor(Class... argTypes) + { + for (AnnotatedConstructor ac : _classInfo.getConstructors()) { + // This list is already filtered to only include accessible + /* (note: for now this is a redundant check; but in future + * that may change; thus leaving here for now) + */ + if (ac.getParameterCount() == 1) { + Class actArg = ac.getRawParameterType(0); + for (Class expArg : argTypes) { + if (expArg == actArg) { + return ac.getAnnotated(); + } + } + } + } + return null; + } + + @Override + public Method findFactoryMethod(Class... expArgTypes) + { + // So, of all single-arg static methods: + for (AnnotatedMethod am : _classInfo.getStaticMethods()) { + if (isFactoryMethod(am)) { + // And must take one of expected arg types (or supertype) + Class actualArgType = am.getRawParameterType(0); + for (Class expArgType : expArgTypes) { + // And one that matches what we would pass in + if (actualArgType.isAssignableFrom(expArgType)) { + return am.getAnnotated(); + } + } + } + } + return null; + } + + protected boolean isFactoryMethod(AnnotatedMethod am) + { + /* First: return type must be compatible with the introspected class + * (i.e. allowed to be sub-class, although usually is the same + * class) + */ + Class rt = am.getRawReturnType(); + if (!getBeanClass().isAssignableFrom(rt)) { + return false; + } + + /* Also: must be a recognized factory method, meaning: + * (a) marked with @JsonCreator annotation, or + * (b) "valueOf" (at this point, need not be public) + */ + if (_annotationIntrospector.hasCreatorAnnotation(am)) { + return true; + } + final String name = am.getName(); + if ("valueOf".equals(name)) { + return true; + } + // [Issue#208] Also accept "fromString()", if takes String or CharSequence + if ("fromString".equals(name)) { + if (1 == am.getParameterCount()) { + Class cls = am.getRawParameterType(0); + if (cls == String.class || CharSequence.class.isAssignableFrom(cls)) { + return true; + } + } + } + return false; + } + + /** + * @deprecated Since 2.4, use findCreatorParameterNames() instead. + */ + @Deprecated + public List findCreatorPropertyNames() + { + List params = findCreatorParameterNames(); + if (params.isEmpty()) { + return Collections.emptyList(); + } + List result = new ArrayList(params.size()); + for (PropertyName name : params) { + result.add(name.getSimpleName()); + } + return result; + } + + /** + * @deprecated Since 2.5, does not seem to be used at all. + */ + @Deprecated + public List findCreatorParameterNames() + { + for (int i = 0; i < 2; ++i) { + List l = (i == 0) + ? getConstructors() : getFactoryMethods(); + for (AnnotatedWithParams creator : l) { + int argCount = creator.getParameterCount(); + if (argCount < 1) continue; + PropertyName name = _findCreatorPropertyName(creator.getParameter(0)); + if (name == null || name.isEmpty()) { + continue; + } + List names = new ArrayList(); + names.add(name); + for (int p = 1; p < argCount; ++p) { + name = _findCreatorPropertyName(creator.getParameter(p)); + names.add(name); + } + return names; + } + } + return Collections.emptyList(); + } + + protected PropertyName _findCreatorPropertyName(AnnotatedParameter param) + { + PropertyName name = _annotationIntrospector.findNameForDeserialization(param); + if (name == null || name.isEmpty()) { + String str = _annotationIntrospector.findImplicitPropertyName(param); + if (str != null && !str.isEmpty()) { + name = PropertyName.construct(str); + } + } + return name; + } + + /* + /********************************************************** + /* Introspection for deserialization, other + /********************************************************** + */ + + @Override + public Class findPOJOBuilder() { + return (_annotationIntrospector == null) ? + null : _annotationIntrospector.findPOJOBuilder(_classInfo); + } + + @Override + public JsonPOJOBuilder.Value findPOJOBuilderConfig() + { + return (_annotationIntrospector == null) ? + null : _annotationIntrospector.findPOJOBuilderConfig(_classInfo); + } + + @Override + public Converter findDeserializationConverter() + { + if (_annotationIntrospector == null) { + return null; + } + return _createConverter(_annotationIntrospector.findDeserializationConverter(_classInfo)); + } + + @Override + public String findClassDescription() { + return (_annotationIntrospector == null) ? + null : _annotationIntrospector.findClassDescription(_classInfo); + } + + /* + /********************************************************** + /* Helper methods for field introspection + /********************************************************** + */ + + /** + * @param ignoredProperties (optional) names of properties to ignore; + * any fields that would be recognized as one of these properties + * is ignored. + * @param forSerialization If true, will collect serializable property + * fields; if false, deserializable + * + * @return Ordered Map with logical property name as key, and + * matching field as value. + * + * @deprecated Since 2.7.2, does not seem to be used? + */ + @Deprecated + public LinkedHashMap _findPropertyFields( + Collection ignoredProperties, boolean forSerialization) + { + LinkedHashMap results = new LinkedHashMap(); + for (BeanPropertyDefinition property : _properties()) { + AnnotatedField f = property.getField(); + if (f != null) { + String name = property.getName(); + if (ignoredProperties != null) { + if (ignoredProperties.contains(name)) { + continue; + } + } + results.put(name, f); + } + } + return results; + } + + /* + /********************************************************** + /* Helper methods, other + /********************************************************** + */ + + @SuppressWarnings("unchecked") + public Converter _createConverter(Object converterDef) + { + if (converterDef == null) { + return null; + } + if (converterDef instanceof Converter) { + return (Converter) converterDef; + } + if (!(converterDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type " + +converterDef.getClass().getName()+"; expected type Converter or Class instead"); + } + Class converterClass = (Class)converterDef; + // there are some known "no class" markers to consider too: + if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) { + return null; + } + if (!Converter.class.isAssignableFrom(converterClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class " + +converterClass.getName()+"; expected Class"); + } + HandlerInstantiator hi = _config.getHandlerInstantiator(); + Converter conv = (hi == null) ? null : hi.converterInstance(_config, _classInfo, converterClass); + if (conv == null) { + conv = (Converter) ClassUtil.createInstance(converterClass, + _config.canOverrideAccessModifiers()); + } + return (Converter) conv; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,275 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.util.Collection; +import java.util.Map; + +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.type.SimpleType; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.LRUMap; + +public class BasicClassIntrospector + extends ClassIntrospector + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /* We keep a small set of pre-constructed descriptions to use for + * common non-structured values, such as Numbers and Strings. + * This is strictly performance optimization to reduce what is + * usually one-time cost, but seems useful for some cases considering + * simplicity. + * + * @since 2.4 + */ + + protected final static BasicBeanDescription STRING_DESC; + static { + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(String.class, null); + STRING_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(String.class), ac); + } + protected final static BasicBeanDescription BOOLEAN_DESC; + static { + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Boolean.TYPE, null); + BOOLEAN_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Boolean.TYPE), ac); + } + protected final static BasicBeanDescription INT_DESC; + static { + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Integer.TYPE, null); + INT_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Integer.TYPE), ac); + } + protected final static BasicBeanDescription LONG_DESC; + static { + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Long.TYPE, null); + LONG_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Long.TYPE), ac); + } + + /* + /********************************************************** + /* Life cycle + /********************************************************** + */ + + @Deprecated // since 2.5: construct instance directly + public final static BasicClassIntrospector instance = new BasicClassIntrospector(); + + /** + * Looks like 'forClassAnnotations()' gets called so frequently that we + * should consider caching to avoid some of the lookups. + * + * @since 2.5 + */ + protected final LRUMap _cachedFCA; + + public BasicClassIntrospector() { + // a small cache should go a long way here + _cachedFCA = new LRUMap(16, 64); + } + + /* + /********************************************************** + /* Factory method impls + /********************************************************** + */ + + @Override + public BasicBeanDescription forSerialization(SerializationConfig cfg, + JavaType type, MixInResolver r) + { + // minor optimization: for some JDK types do minimal introspection + BasicBeanDescription desc = _findStdTypeDesc(type); + if (desc == null) { + // As per [Databind#550], skip full introspection for some of standard + // structured types as well + desc = _findStdJdkCollectionDesc(cfg, type); + if (desc == null) { + desc = BasicBeanDescription.forSerialization(collectProperties(cfg, + type, r, true, "set")); + } + // Also: this is a superset of "forClassAnnotations", so may optimize by optional add: + _cachedFCA.putIfAbsent(type, desc); + } + return desc; + } + + @Override + public BasicBeanDescription forDeserialization(DeserializationConfig cfg, + JavaType type, MixInResolver r) + { + // minor optimization: for some JDK types do minimal introspection + BasicBeanDescription desc = _findStdTypeDesc(type); + if (desc == null) { + // As per [Databind#550], skip full introspection for some of standard + // structured types as well + desc = _findStdJdkCollectionDesc(cfg, type); + if (desc == null) { + desc = BasicBeanDescription.forDeserialization(collectProperties(cfg, + type, r, false, "set")); + } + // Also: this is a superset of "forClassAnnotations", so may optimize by optional add: + _cachedFCA.putIfAbsent(type, desc); + } + return desc; + } + + @Override + public BasicBeanDescription forDeserializationWithBuilder(DeserializationConfig cfg, + JavaType type, MixInResolver r) + { + // no std JDK types with Builders, so: + + BasicBeanDescription desc = BasicBeanDescription.forDeserialization(collectPropertiesWithBuilder(cfg, + type, r, false)); + // this is still a superset of "forClassAnnotations", so may optimize by optional add: + _cachedFCA.putIfAbsent(type, desc); + return desc; + } + + @Override + public BasicBeanDescription forCreation(DeserializationConfig cfg, + JavaType type, MixInResolver r) + { + BasicBeanDescription desc = _findStdTypeDesc(type); + if (desc == null) { + + // As per [Databind#550], skip full introspection for some of standard + // structured types as well + desc = _findStdJdkCollectionDesc(cfg, type); + if (desc == null) { + desc = BasicBeanDescription.forDeserialization( + collectProperties(cfg, type, r, false, "set")); + } + } + // should this be cached for FCA? + return desc; + } + + @Override + public BasicBeanDescription forClassAnnotations(MapperConfig config, + JavaType type, MixInResolver r) + { + BasicBeanDescription desc = _findStdTypeDesc(type); + if (desc == null) { + desc = _cachedFCA.get(type); + if (desc == null) { + AnnotatedClass ac = AnnotatedClass.construct(type, config, r); + desc = BasicBeanDescription.forOtherUse(config, type, ac); + _cachedFCA.put(type, desc); + } + } + return desc; + } + + @Override + public BasicBeanDescription forDirectClassAnnotations(MapperConfig config, + JavaType type, MixInResolver r) + { + BasicBeanDescription desc = _findStdTypeDesc(type); + if (desc == null) { + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(type.getRawClass(), config, r); + desc = BasicBeanDescription.forOtherUse(config, type, ac); + } + return desc; + } + + /* + /********************************************************** + /* Overridable helper methods + /********************************************************** + */ + + protected POJOPropertiesCollector collectProperties(MapperConfig config, + JavaType type, MixInResolver r, boolean forSerialization, + String mutatorPrefix) + { + AnnotatedClass ac = AnnotatedClass.construct(type, config, r); + return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix); + } + + protected POJOPropertiesCollector collectPropertiesWithBuilder(MapperConfig config, + JavaType type, MixInResolver r, boolean forSerialization) + { + boolean useAnnotations = config.isAnnotationProcessingEnabled(); + AnnotationIntrospector ai = useAnnotations ? config.getAnnotationIntrospector() : null; + AnnotatedClass ac = AnnotatedClass.construct(type, config, r); + JsonPOJOBuilder.Value builderConfig = (ai == null) ? null : ai.findPOJOBuilderConfig(ac); + String mutatorPrefix = (builderConfig == null) ? "with" : builderConfig.withPrefix; + return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix); + } + + /** + * Overridable method called for creating {@link POJOPropertiesCollector} instance + * to use; override is needed if a custom sub-class is to be used. + */ + protected POJOPropertiesCollector constructPropertyCollector(MapperConfig config, + AnnotatedClass ac, JavaType type, boolean forSerialization, String mutatorPrefix) + { + return new POJOPropertiesCollector(config, forSerialization, type, ac, mutatorPrefix); + } + + /** + * Method called to see if type is one of core JDK types + * that we have cached for efficiency. + */ + protected BasicBeanDescription _findStdTypeDesc(JavaType type) + { + Class cls = type.getRawClass(); + if (cls.isPrimitive()) { + if (cls == Boolean.TYPE) { + return BOOLEAN_DESC; + } + if (cls == Integer.TYPE) { + return INT_DESC; + } + if (cls == Long.TYPE) { + return LONG_DESC; + } + } else { + if (cls == String.class) { + return STRING_DESC; + } + } + return null; + } + + /** + * Helper method used to decide whether we can omit introspection + * for members (methods, fields, constructors); we may do so for + * a limited number of container types JDK provides. + */ + protected boolean _isStdJDKCollection(JavaType type) + { + if (!type.isContainerType() || type.isArrayType()) { + return false; + } + Class raw = type.getRawClass(); + String pkgName = ClassUtil.getPackageName(raw); + if (pkgName != null) { + if (pkgName.startsWith("java.lang") + || pkgName.startsWith("java.util")) { + /* 23-Sep-2014, tatu: Should we be conservative here (minimal number + * of matches), or ambitious? Let's do latter for now. + */ + if (Collection.class.isAssignableFrom(raw) + || Map.class.isAssignableFrom(raw)) { + return true; + } + } + } + return false; + } + + protected BasicBeanDescription _findStdJdkCollectionDesc(MapperConfig cfg, JavaType type) + { + if (_isStdJDKCollection(type)) { + AnnotatedClass ac = AnnotatedClass.construct(type, cfg); + return BasicBeanDescription.forOtherUse(cfg, type, ac); + } + return null; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,239 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.util.Iterator; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Named; + +/** + * Simple value classes that contain definitions of properties, + * used during introspection of properties to use for + * serialization and deserialization purposes. + * These instances are created before actual {@link BeanProperty} + * instances are created, i.e. they are used earlier in the process + * flow, and are typically use to construct actual + * {@link BeanProperty} instances. + */ +public abstract class BeanPropertyDefinition + implements Named +{ + protected final static JsonInclude.Value EMPTY_INCLUDE = JsonInclude.Value.empty(); + + /* + /********************************************************** + /* Fluent factory methods for creating modified copies + /********************************************************** + */ + + /** + * Method that can be used to create a definition with + * same settings as this one, but with different + * (external) name; that is, one for which + * {@link #getName()} would return newName. + * + * @since 2.3 + */ + public abstract BeanPropertyDefinition withName(PropertyName newName); + + /** + * Alternate "mutant factory" that will only change simple name, but + * leave other optional parts (like namespace) as is. + * + * @since 2.3 + */ + public abstract BeanPropertyDefinition withSimpleName(String newSimpleName); + + /* + /********************************************************** + /* Basic property information, name, type + /********************************************************** + */ + + /** + * Accessor for name used for external representation (in JSON). + */ + @Override // from Named + public abstract String getName(); + + public abstract PropertyName getFullName(); + + /** + * @since 2.6 + */ + public boolean hasName(PropertyName name) { + return getFullName().equals(name); + } + + /** + * Accessor that can be used to determine implicit name from underlying + * element(s) before possible renaming. This is the "internal" + * name derived from accessor ("x" from "getX"), and is not based on + * annotations or naming strategy. + */ + public abstract String getInternalName(); + + /** + * Accessor for finding wrapper name to use for property (if any). + * + * @since 2.2 + */ + public abstract PropertyName getWrapperName(); + + /** + * Method for accessing additional metadata. + * NOTE: will never return null, so de-referencing return value + * is safe. + * + * @since 2.3 + */ + public abstract PropertyMetadata getMetadata(); + + /** + * Accessor that can be called to check whether property was included + * due to an explicit marker (usually annotation), or just by naming + * convention. + * + * @return True if property was explicitly included (usually by having + * one of components being annotated); false if inclusion was purely + * due to naming or visibility definitions (that is, implicit) + */ + public abstract boolean isExplicitlyIncluded(); + + /** + * Accessor that can be called to check whether property name was + * due to an explicit marker (usually annotation), or just by naming + * convention or use of "use-default-name" marker (annotation). + *

+ * Note that entries that return true from this method will always + * return true for {@link #isExplicitlyIncluded()}, but not necessarily + * vice versa. + * + * @since 2.4 + */ + public boolean isExplicitlyNamed() { + return isExplicitlyIncluded(); + } + + /* + /********************************************************** + /* Capabilities + /********************************************************** + */ + + public boolean couldDeserialize() { return getMutator() != null; } + public boolean couldSerialize() { return getAccessor() != null; } + + /* + /********************************************************** + /* Access to accessors (fields, methods etc) + /********************************************************** + */ + + public abstract boolean hasGetter(); + public abstract boolean hasSetter(); + public abstract boolean hasField(); + public abstract boolean hasConstructorParameter(); + + public abstract AnnotatedMethod getGetter(); + public abstract AnnotatedMethod getSetter(); + public abstract AnnotatedField getField(); + public abstract AnnotatedParameter getConstructorParameter(); + + /** + * Additional method that may be called instead of {@link #getConstructorParameter()} + * to get access to all constructor parameters, not just the highest priority one. + * + * @since 2.5 + */ + public Iterator getConstructorParameters() { + return ClassUtil.emptyIterator(); + } + + /** + * Method used to find accessor (getter, field to access) to use for accessing + * value of the property. + * Null if no such member exists. + */ + public abstract AnnotatedMember getAccessor(); + + /** + * Method used to find mutator (constructor parameter, setter, field) to use for + * changing value of the property. + * Null if no such member exists. + */ + public abstract AnnotatedMember getMutator(); + + /** + * @since 2.3 + */ + public abstract AnnotatedMember getNonConstructorMutator(); + + /** + * Method used to find the property member (getter, setter, field) that has + * the highest precedence in current context (getter method when serializing, + * if available, and so forth), if any. + *

+ * Note: abstract since 2.5 + * + * @since 2.1 + */ + public abstract AnnotatedMember getPrimaryMember(); + + /* + /********************************************************** + /* More refined access to configuration features + /* (usually based on annotations) + /* Since most trivial implementations do not support + /* these methods, they are implemented as no-ops. + /********************************************************** + */ + + /** + * Method used to find View-inclusion definitions for the property. + */ + public Class[] findViews() { return null; } + + /** + * Method used to find whether property is part of a bi-directional + * reference. + */ + public AnnotationIntrospector.ReferenceProperty findReferenceType() { return null; } + + /** + * Method used to check whether this logical property has a marker + * to indicate it should be used as the type id for polymorphic type + * handling. + */ + public boolean isTypeId() { return false; } + + /** + * Method used to check whether this logical property indicates that + * value POJOs should be written using additional Object Identifier + * (or, when multiple references exist, all but first AS Object Identifier). + */ + public ObjectIdInfo findObjectIdInfo() { return null; } + + /** + * Method used to check if this property is expected to have a value; + * and if none found, should either be considered invalid (and most likely + * fail deserialization), or handled by other means (by providing default + * value) + */ + public boolean isRequired() { + PropertyMetadata md = getMetadata(); + return (md != null) && md.isRequired(); + } + + /** + * Method used to check if this property has specific inclusion override + * associated with it or not. + * + * @since 2.5 + */ + public JsonInclude.Value findInclusion() { + return EMPTY_INCLUDE; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,103 @@ +package com.fasterxml.jackson.databind.introspect; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.cfg.MapperConfig; + +/** + * Helper class used to introspect features of POJO value classes + * used with Jackson. The main use is for finding out + * POJO construction (creator) and value access (getters, setters) + * methods and annotations that define configuration of using + * those methods. + */ +public abstract class ClassIntrospector +{ + /* + /********************************************************** + /* Helper interfaces + /********************************************************** + */ + + /** + * Interface used for decoupling details of how mix-in annotation + * definitions are accessed (via this interface), and how + * they are stored (defined by classes that implement the interface) + */ + public interface MixInResolver + { + /** + * Method that will check if there are "mix-in" classes (with mix-in + * annotations) for given class + */ + public Class findMixInClassFor(Class cls); + + /** + * Method called to create a new, non-shared copy, to be used by different + * ObjectMapper instance, and one that should not be connected + * to this instance, if resolver has mutable state. + * If resolver is immutable may simply return `this`. + * + * @since 2.6 + */ + public MixInResolver copy(); + } + + protected ClassIntrospector() { } + + /* + /********************************************************** + /* Public API: factory methods + /********************************************************** + */ + + /** + * Factory method that constructs an introspector that has all + * information needed for serialization purposes. + */ + public abstract BeanDescription forSerialization(SerializationConfig cfg, + JavaType type, MixInResolver r); + + /** + * Factory method that constructs an introspector that has all + * information needed for deserialization purposes. + */ + public abstract BeanDescription forDeserialization(DeserializationConfig cfg, + JavaType type, MixInResolver r); + + /** + * Factory method that constructs an introspector that has all + * information needed for constructing deserializers that use + * intermediate Builder objects. + */ + public abstract BeanDescription forDeserializationWithBuilder(DeserializationConfig cfg, + JavaType type, MixInResolver r); + + /** + * Factory method that constructs an introspector that has + * information necessary for creating instances of given + * class ("creator"), as well as class annotations, but + * no information on member methods + */ + public abstract BeanDescription forCreation(DeserializationConfig cfg, JavaType type, + MixInResolver r); + + /** + * Factory method that constructs an introspector that only has + * information regarding annotations class itself (or its supertypes) has, + * but nothing on methods or constructors. + */ + public abstract BeanDescription forClassAnnotations(MapperConfig cfg, JavaType type, + MixInResolver r); + + /** + * Factory method that constructs an introspector that only has + * information regarding annotations class itself has (but NOT including + * its supertypes), but nothing on methods or constructors. + */ + public abstract BeanDescription forDirectClassAnnotations(MapperConfig cfg, JavaType type, + MixInResolver r); +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,103 @@ +package com.fasterxml.jackson.databind.introspect; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.PropertyMetadata; +import com.fasterxml.jackson.databind.cfg.MapperConfig; + +/** + * Intermediate {@link BeanProperty} class shared by concrete readable- and + * writable property implementations for sharing common functionality. + * + * @since 2.7 + */ +public abstract class ConcreteBeanPropertyBase + implements BeanProperty, java.io.Serializable +{ + private static final long serialVersionUID = 1; + + /** + * Additional information about property + * + * @since 2.3 + */ + protected final PropertyMetadata _metadata; + + /** + * Lazily accessed value for per-property format override definition. + * + * @since 2.6 + */ + protected transient JsonFormat.Value _format; + + protected ConcreteBeanPropertyBase(PropertyMetadata md) { + _metadata = (md == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : md; + } + + protected ConcreteBeanPropertyBase(ConcreteBeanPropertyBase src) { + _metadata = src._metadata; + _format = src._format; + } + + @Override + public boolean isRequired() { return _metadata.isRequired(); } + + @Override + public PropertyMetadata getMetadata() { return _metadata; } + + @Override + public boolean isVirtual() { return false; } + + @Override + @Deprecated + public final JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr) { + JsonFormat.Value f = _format; + if (f == null) { // not yet looked up, do that + if (intr != null) { + AnnotatedMember member = getMember(); + if (member != null) { + f = intr.findFormat(member); + } + } + if (f == null) { + f = EMPTY_FORMAT; + } + } + return f; + } + + @Override + public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType) + { + // 08-Oct-2015, tatu: Unlike with Format, let's not cache locally here, for now? + JsonFormat.Value v0 = config.getDefaultPropertyFormat(baseType); + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + AnnotatedMember member = getMember(); + if ((intr == null) || (member == null)) { + return v0; + } + JsonFormat.Value v = intr.findFormat(member); + if (v == null) { + return v0; + } + return v0.withOverrides(v); + } + + @Override + public JsonInclude.Value findPropertyInclusion(MapperConfig config, Class baseType) + { + JsonInclude.Value v0 = config.getDefaultPropertyInclusion(baseType); + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + AnnotatedMember member = getMember(); + if ((intr == null) || (member == null)) { + return v0; + } + JsonInclude.Value v = intr.findPropertyInclusion(member); + if (v == null) { + return v0; + } + return v0.withOverrides(v); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1316 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.beans.ConstructorProperties; +import java.beans.Transient; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.*; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.*; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter; +import com.fasterxml.jackson.databind.ser.std.RawSerializer; +import com.fasterxml.jackson.databind.util.*; + +/** + * {@link AnnotationIntrospector} implementation that handles standard + * Jackson annotations. + */ +public class JacksonAnnotationIntrospector + extends AnnotationIntrospector + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + @SuppressWarnings("unchecked") + private final static Class[] ANNOTATIONS_TO_INFER_SER = (Class[]) + new Class[] { + JsonSerialize.class, + JsonView.class, + JsonFormat.class, + JsonTypeInfo.class, + JsonRawValue.class, + JsonUnwrapped.class, + JsonBackReference.class, + JsonManagedReference.class + }; + + @SuppressWarnings("unchecked") + private final static Class[] ANNOTATIONS_TO_INFER_DESER = (Class[]) + new Class[] { + JsonDeserialize.class, + JsonView.class, + JsonFormat.class, + JsonTypeInfo.class, + JsonUnwrapped.class, + JsonBackReference.class, + JsonManagedReference.class + }; + + private static final Java7Support _jdk7Helper; + static { + Java7Support x = null; + try { + x = Java7Support.class.newInstance(); + } catch (Throwable t) { + // 24-Nov-2015, tatu: Should we log or not? + java.util.logging.Logger.getLogger(JacksonAnnotationIntrospector.class.getName()) + .warning("Unable to load JDK7 annotation types; will have to skip"); + } + _jdk7Helper = x; + } + + /** + * Since introspection of annotation types is a performance issue in some + * use cases (rare, but do exist), let's try a simple cache to reduce + * need for actual meta-annotation introspection. + *

+ * Non-final only because it needs to be re-created after deserialization. + * + * @since 2.7 + */ + protected transient LRUMap,Boolean> _annotationsInside = new LRUMap,Boolean>(48, 48); + + /* + /********************************************************** + /* Local configuration settings + /********************************************************** + */ + + /** + * See {@link #setConstructorPropertiesImpliesCreator(boolean)} for + * explanation. + *

+ * Defaults to true. + * + * @since 2.7.4 + */ + protected boolean _cfgConstructorPropertiesImpliesCreator = true; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public JacksonAnnotationIntrospector() { } + + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + protected Object readResolve() { + if (_annotationsInside == null) { + _annotationsInside = new LRUMap,Boolean>(48, 48); + } + return this; + } + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Method for changing behavior of {@link java.beans.ConstructorProperties}: + * if set to `true`, existence DOES indicate that the given constructor should + * be considered a creator; `false` that it should NOT be considered a creator + * without explicit use of JsonCreator annotation. + *

+ * Default setting is `true` + * + * @since 2.7.4 + */ + public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b) + { + _cfgConstructorPropertiesImpliesCreator = b; + return this; + } + + /* + /********************************************************** + /* General annotation properties + /********************************************************** + */ + + /** + * Annotations with meta-annotation {@link JacksonAnnotationsInside} + * are considered bundles. + */ + @Override + public boolean isAnnotationBundle(Annotation ann) { + // 22-Sep-2015, tatu: Caching here has modest effect on JavaSE, and only + // mostly in degenerate cases where introspection used more often than + // it should (like recreating ObjectMapper once per read/write). + // But it may be more beneficial on platforms like Android (should verify) + Class type = ann.annotationType(); + Boolean b = _annotationsInside.get(type); + if (b == null) { + b = type.getAnnotation(JacksonAnnotationsInside.class) != null; + _annotationsInside.putIfAbsent(type, b); + } + return b.booleanValue(); + } + + /* + /********************************************************** + /* General annotations + /********************************************************** + */ + + /** + * Since 2.6, we have supported use of {@link JsonProperty} for specifying + * explicit serialized name + */ + @Override + public String findEnumValue(Enum value) + { + // 11-Jun-2015, tatu: As per [databind#677], need to allow explicit naming. + // Unfortunately can not quite use standard AnnotatedClass here (due to various + // reasons, including odd representation JVM uses); has to do for now + try { + // We know that values are actually static fields with matching name so: + Field f = value.getClass().getField(value.name()); + if (f != null) { + JsonProperty prop = f.getAnnotation(JsonProperty.class); + if (prop != null) { + String n = prop.value(); + if (n != null && !n.isEmpty()) { + return n; + } + } + } + } catch (SecurityException e) { + // 17-Sep-2015, tatu: Anything we could/should do here? + } catch (NoSuchFieldException e) { + // 17-Sep-2015, tatu: should not really happen. But... can we do anything? + } + return value.name(); + } + + @Override // since 2.7 + public String[] findEnumValues(Class enumType, Enum[] enumValues, String[] names) { + HashMap expl = null; + for (Field f : ClassUtil.getDeclaredFields(enumType)) { + if (!f.isEnumConstant()) { + continue; + } + JsonProperty prop = f.getAnnotation(JsonProperty.class); + if (prop == null) { + continue; + } + String n = prop.value(); + if (n.isEmpty()) { + continue; + } + if (expl == null) { + expl = new HashMap(); + } + expl.put(f.getName(), n); + } + // and then stitch them together if and as necessary + if (expl != null) { + for (int i = 0, end = enumValues.length; i < end; ++i) { + String defName = enumValues[i].name(); + String explValue = expl.get(defName); + if (explValue != null) { + names[i] = explValue; + } + } + } + return names; + } + + /* + /********************************************************** + /* General class annotations + /********************************************************** + */ + + @Override + public PropertyName findRootName(AnnotatedClass ac) + { + JsonRootName ann = _findAnnotation(ac, JsonRootName.class); + if (ann == null) { + return null; + } + String ns = ann.namespace(); + if (ns != null && ns.length() == 0) { + ns = null; + } + return PropertyName.construct(ann.value(), ns); + } + + @Override + @Deprecated // since 2.6, remove from 2.7 or later + public String[] findPropertiesToIgnore(Annotated ac) { + JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class); + return (ignore == null) ? null : ignore.value(); + } + + @Override // since 2.6 + public String[] findPropertiesToIgnore(Annotated ac, boolean forSerialization) { + JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class); + if (ignore == null) { + return null; + } + // 13-May-2015, tatu: As per [databind#95], allow read-only/write-only props + if (forSerialization) { + if (ignore.allowGetters()) { + return null; + } + } else { + if (ignore.allowSetters()) { + return null; + } + } + return ignore.value(); + } + + @Override + public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) { + JsonIgnoreProperties ignore = _findAnnotation(ac, JsonIgnoreProperties.class); + return (ignore == null) ? null : ignore.ignoreUnknown(); + } + + @Override + public Boolean isIgnorableType(AnnotatedClass ac) { + JsonIgnoreType ignore = _findAnnotation(ac, JsonIgnoreType.class); + return (ignore == null) ? null : ignore.value(); + } + + @Override + public Object findFilterId(Annotated a) { + JsonFilter ann = _findAnnotation(a, JsonFilter.class); + if (ann != null) { + String id = ann.value(); + // Empty String is same as not having annotation, to allow overrides + if (id.length() > 0) { + return id; + } + } + return null; + } + + @Override + public Object findNamingStrategy(AnnotatedClass ac) + { + JsonNaming ann = _findAnnotation(ac, JsonNaming.class); + return (ann == null) ? null : ann.value(); + } + + @Override + public String findClassDescription(AnnotatedClass ac) { + JsonClassDescription ann = _findAnnotation(ac, JsonClassDescription.class); + return (ann == null) ? null : ann.value(); + } + + /* + /********************************************************** + /* Property auto-detection + /********************************************************** + */ + + @Override + public VisibilityChecker findAutoDetectVisibility(AnnotatedClass ac, + VisibilityChecker checker) + { + JsonAutoDetect ann = _findAnnotation(ac, JsonAutoDetect.class); + return (ann == null) ? checker : checker.with(ann); + } + + /* + /********************************************************** + /* General member (field, method/constructor) annotations + /********************************************************** + */ + + @Override + public String findImplicitPropertyName(AnnotatedMember m) { + PropertyName n = _findConstructorName(m); + return (n == null) ? null : n.getSimpleName(); + } + + @Override + public boolean hasIgnoreMarker(AnnotatedMember m) { + return _isIgnorable(m); + } + + @Override + public Boolean hasRequiredMarker(AnnotatedMember m) + { + JsonProperty ann = _findAnnotation(m, JsonProperty.class); + if (ann != null) { + return ann.required(); + } + return null; + } + + @Override + public JsonProperty.Access findPropertyAccess(Annotated m) { + JsonProperty ann = _findAnnotation(m, JsonProperty.class); + if (ann != null) { + return ann.access(); + } + return null; + } + + @Override + public String findPropertyDescription(Annotated ann) { + JsonPropertyDescription desc = _findAnnotation(ann, JsonPropertyDescription.class); + return (desc == null) ? null : desc.value(); + } + + @Override + public Integer findPropertyIndex(Annotated ann) { + JsonProperty prop = _findAnnotation(ann, JsonProperty.class); + if (prop != null) { + int ix = prop.index(); + if (ix != JsonProperty.INDEX_UNKNOWN) { + return Integer.valueOf(ix); + } + } + return null; + } + + @Override + public String findPropertyDefaultValue(Annotated ann) { + JsonProperty prop = _findAnnotation(ann, JsonProperty.class); + if (prop == null) { + return null; + } + String str = prop.defaultValue(); + // Since annotations do not allow nulls, need to assume empty means "none" + return str.isEmpty() ? null : str; + } + + @Override + public JsonFormat.Value findFormat(Annotated ann) { + JsonFormat f = _findAnnotation(ann, JsonFormat.class); + return (f == null) ? null : new JsonFormat.Value(f); + } + + @Override + public ReferenceProperty findReferenceType(AnnotatedMember member) + { + JsonManagedReference ref1 = _findAnnotation(member, JsonManagedReference.class); + if (ref1 != null) { + return AnnotationIntrospector.ReferenceProperty.managed(ref1.value()); + } + JsonBackReference ref2 = _findAnnotation(member, JsonBackReference.class); + if (ref2 != null) { + return AnnotationIntrospector.ReferenceProperty.back(ref2.value()); + } + return null; + } + + @Override + public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) + { + JsonUnwrapped ann = _findAnnotation(member, JsonUnwrapped.class); + // if not enabled, just means annotation is not enabled; not necessarily + // that unwrapping should not be done (relevant when using chained introspectors) + if (ann == null || !ann.enabled()) { + return null; + } + String prefix = ann.prefix(); + String suffix = ann.suffix(); + return NameTransformer.simpleTransformer(prefix, suffix); + } + + @Override + public Object findInjectableValueId(AnnotatedMember m) + { + JacksonInject ann = _findAnnotation(m, JacksonInject.class); + if (ann == null) { + return null; + } + /* Empty String means that we should use name of declared + * value class. + */ + String id = ann.value(); + if (id.length() == 0) { + // slight complication; for setters, type + if (!(m instanceof AnnotatedMethod)) { + return m.getRawType().getName(); + } + AnnotatedMethod am = (AnnotatedMethod) m; + if (am.getParameterCount() == 0) { + return m.getRawType().getName(); + } + return am.getRawParameterType(0).getName(); + } + return id; + } + + @Override + public Class[] findViews(Annotated a) + { + JsonView ann = _findAnnotation(a, JsonView.class); + return (ann == null) ? null : ann.value(); + } + + @Override // since 2.7 + public AnnotatedMethod resolveSetterConflict(MapperConfig config, + AnnotatedMethod setter1, AnnotatedMethod setter2) + { + Class cls1 = setter1.getRawParameterType(0); + Class cls2 = setter2.getRawParameterType(0); + + // First: prefer primitives over non-primitives + // 11-Dec-2015, tatu: TODO, perhaps consider wrappers for primitives too? + if (cls1.isPrimitive()) { + if (!cls2.isPrimitive()) { + return setter1; + } + } else if (cls2.isPrimitive()) { + return setter2; + } + + if (cls1 == String.class) { + if (cls2 != String.class) { + return setter1; + } + } else if (cls2 == String.class) { + return setter2; + } + + return null; + } + + /* + /********************************************************** + /* Annotations for Polymorphic Type handling + /********************************************************** + */ + + @Override + public TypeResolverBuilder findTypeResolver(MapperConfig config, + AnnotatedClass ac, JavaType baseType) + { + return _findTypeResolver(config, ac, baseType); + } + + @Override + public TypeResolverBuilder findPropertyTypeResolver(MapperConfig config, + AnnotatedMember am, JavaType baseType) + { + /* As per definition of @JsonTypeInfo, should only apply to contents of container + * (collection, map) types, not container types themselves: + */ + // 17-Apr-2016, tatu: For 2.7.4 make sure ReferenceType also included + if (baseType.isContainerType() || baseType.isReferenceType()) { + return null; + } + // No per-member type overrides (yet) + return _findTypeResolver(config, am, baseType); + } + + @Override + public TypeResolverBuilder findPropertyContentTypeResolver(MapperConfig config, + AnnotatedMember am, JavaType containerType) + { + /* First: let's ensure property is a container type: caller should have + * verified but just to be sure + */ + if (containerType.getContentType() == null) { + throw new IllegalArgumentException("Must call method with a container or reference type (got "+containerType+")"); + } + return _findTypeResolver(config, am, containerType); + } + + @Override + public List findSubtypes(Annotated a) + { + JsonSubTypes t = _findAnnotation(a, JsonSubTypes.class); + if (t == null) return null; + JsonSubTypes.Type[] types = t.value(); + ArrayList result = new ArrayList(types.length); + for (JsonSubTypes.Type type : types) { + result.add(new NamedType(type.value(), type.name())); + } + return result; + } + + @Override + public String findTypeName(AnnotatedClass ac) + { + JsonTypeName tn = _findAnnotation(ac, JsonTypeName.class); + return (tn == null) ? null : tn.value(); + } + + @Override + public Boolean isTypeId(AnnotatedMember member) { + return _hasAnnotation(member, JsonTypeId.class); + } + + /* + /********************************************************** + /* Annotations for Object Id handling + /********************************************************** + */ + + @Override + public ObjectIdInfo findObjectIdInfo(Annotated ann) { + JsonIdentityInfo info = _findAnnotation(ann, JsonIdentityInfo.class); + if (info == null || info.generator() == ObjectIdGenerators.None.class) { + return null; + } + // In future may need to allow passing namespace? + PropertyName name = PropertyName.construct(info.property()); + return new ObjectIdInfo(name, info.scope(), info.generator(), info.resolver()); + } + + @Override + public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { + JsonIdentityReference ref = _findAnnotation(ann, JsonIdentityReference.class); + if (ref != null) { + objectIdInfo = objectIdInfo.withAlwaysAsId(ref.alwaysAsId()); + } + return objectIdInfo; + } + + /* + /********************************************************** + /* Serialization: general annotations + /********************************************************** + */ + + @Override + public Object findSerializer(Annotated a) + { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + if (ann != null) { + @SuppressWarnings("rawtypes") + Class serClass = ann.using(); + if (serClass != JsonSerializer.None.class) { + return serClass; + } + } + + /* 18-Oct-2010, tatu: [JACKSON-351] @JsonRawValue handled just here, for now; + * if we need to get raw indicator from other sources need to add + * separate accessor within {@link AnnotationIntrospector} interface. + */ + JsonRawValue annRaw = _findAnnotation(a, JsonRawValue.class); + if ((annRaw != null) && annRaw.value()) { + // let's construct instance with nominal type: + Class cls = a.getRawType(); + return new RawSerializer(cls); + } + return null; + } + + @Override + public Object findKeySerializer(Annotated a) + { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + if (ann != null) { + @SuppressWarnings("rawtypes") + Class serClass = ann.keyUsing(); + if (serClass != JsonSerializer.None.class) { + return serClass; + } + } + return null; + } + + @Override + public Object findContentSerializer(Annotated a) + { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + if (ann != null) { + @SuppressWarnings("rawtypes") + Class serClass = ann.contentUsing(); + if (serClass != JsonSerializer.None.class) { + return serClass; + } + } + return null; + } + + @Override + public Object findNullSerializer(Annotated a) + { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + if (ann != null) { + @SuppressWarnings("rawtypes") + Class serClass = ann.nullsUsing(); + if (serClass != JsonSerializer.None.class) { + return serClass; + } + } + return null; + } + + @Override + @SuppressWarnings("deprecation") + public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue) + { + JsonInclude inc = _findAnnotation(a, JsonInclude.class); + if (inc != null) { + JsonInclude.Include v = inc.value(); + if (v != JsonInclude.Include.USE_DEFAULTS) { + return v; + } + } + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + if (ann != null) { + JsonSerialize.Inclusion i2 = ann.include(); + switch (i2) { + case ALWAYS: + return JsonInclude.Include.ALWAYS; + case NON_NULL: + return JsonInclude.Include.NON_NULL; + case NON_DEFAULT: + return JsonInclude.Include.NON_DEFAULT; + case NON_EMPTY: + return JsonInclude.Include.NON_EMPTY; + case DEFAULT_INCLUSION: // since 2.3 -- fall through, use default + break; + } + } + return defValue; + } + + @Override + @Deprecated + public JsonInclude.Include findSerializationInclusionForContent(Annotated a, JsonInclude.Include defValue) + { + JsonInclude inc = _findAnnotation(a, JsonInclude.class); + if (inc != null) { + JsonInclude.Include incl = inc.content(); + if (incl != JsonInclude.Include.USE_DEFAULTS) { + return incl; + } + } + return defValue; + } + + @Override + @SuppressWarnings("deprecation") + public JsonInclude.Value findPropertyInclusion(Annotated a) + { + JsonInclude inc = _findAnnotation(a, JsonInclude.class); + JsonInclude.Include valueIncl = (inc == null) ? JsonInclude.Include.USE_DEFAULTS : inc.value(); + if (valueIncl == JsonInclude.Include.USE_DEFAULTS) { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + if (ann != null) { + JsonSerialize.Inclusion i2 = ann.include(); + switch (i2) { + case ALWAYS: + valueIncl = JsonInclude.Include.ALWAYS; + break; + case NON_NULL: + valueIncl = JsonInclude.Include.NON_NULL; + break; + case NON_DEFAULT: + valueIncl = JsonInclude.Include.NON_DEFAULT; + break; + case NON_EMPTY: + valueIncl = JsonInclude.Include.NON_EMPTY; + break; + case DEFAULT_INCLUSION: + default: + } + } + } + JsonInclude.Include contentIncl = (inc == null) ? JsonInclude.Include.USE_DEFAULTS : inc.content(); + return JsonInclude.Value.construct(valueIncl, contentIncl); + } + + @Override + @Deprecated + public Class findSerializationType(Annotated am) + { + JsonSerialize ann = _findAnnotation(am, JsonSerialize.class); + return (ann == null) ? null : _classIfExplicit(ann.as()); + } + + @Override + @Deprecated + public Class findSerializationKeyType(Annotated am, JavaType baseType) + { + JsonSerialize ann = _findAnnotation(am, JsonSerialize.class); + return (ann == null) ? null : _classIfExplicit(ann.keyAs()); + } + + @Override + @Deprecated + public Class findSerializationContentType(Annotated am, JavaType baseType) + { + JsonSerialize ann = _findAnnotation(am, JsonSerialize.class); + return (ann == null) ? null : _classIfExplicit(ann.contentAs()); + } + + @Override + public JsonSerialize.Typing findSerializationTyping(Annotated a) + { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + return (ann == null) ? null : ann.typing(); + } + + @Override + public Object findSerializationConverter(Annotated a) { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class); + } + + @Override + public Object findSerializationContentConverter(AnnotatedMember a) { + JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); + return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class); + } + + /* + /********************************************************** + /* Serialization: class annotations + /********************************************************** + */ + + @Override + public String[] findSerializationPropertyOrder(AnnotatedClass ac) { + JsonPropertyOrder order = _findAnnotation(ac, JsonPropertyOrder.class); + return (order == null) ? null : order.value(); + } + + @Override + public Boolean findSerializationSortAlphabetically(Annotated ann) { + return _findSortAlpha(ann); + } + + private final Boolean _findSortAlpha(Annotated ann) { + JsonPropertyOrder order = _findAnnotation(ann, JsonPropertyOrder.class); + /* 23-Jun-2015, tatu: as per [databind#840], let's only consider + * `true` to have any significance. + */ + if ((order != null) && order.alphabetic()) { + return Boolean.TRUE; + } + return null; + } + + @Override + public void findAndAddVirtualProperties(MapperConfig config, AnnotatedClass ac, + List properties) { + JsonAppend ann = _findAnnotation(ac, JsonAppend.class); + if (ann == null) { + return; + } + final boolean prepend = ann.prepend(); + JavaType propType = null; + + // First: any attribute-backed properties? + JsonAppend.Attr[] attrs = ann.attrs(); + for (int i = 0, len = attrs.length; i < len; ++i) { + if (propType == null) { + propType = config.constructType(Object.class); + } + BeanPropertyWriter bpw = _constructVirtualProperty(attrs[i], + config, ac, propType); + if (prepend) { + properties.add(i, bpw); + } else { + properties.add(bpw); + } + } + + // Then: general-purpose virtual properties? + JsonAppend.Prop[] props = ann.props(); + for (int i = 0, len = props.length; i < len; ++i) { + BeanPropertyWriter bpw = _constructVirtualProperty(props[i], + config, ac); + if (prepend) { + properties.add(i, bpw); + } else { + properties.add(bpw); + } + } + } + + protected BeanPropertyWriter _constructVirtualProperty(JsonAppend.Attr attr, + MapperConfig config, AnnotatedClass ac, JavaType type) + { + PropertyMetadata metadata = attr.required() ? + PropertyMetadata.STD_REQUIRED : PropertyMetadata.STD_OPTIONAL; + // could add Index, Description in future, if those matter + String attrName = attr.value(); + + // allow explicit renaming; if none, default to attribute name + PropertyName propName = _propertyName(attr.propName(), attr.propNamespace()); + if (!propName.hasSimpleName()) { + propName = PropertyName.construct(attrName); + } + // now, then, we need a placeholder for member (no real Field/Method): + AnnotatedMember member = new VirtualAnnotatedMember(ac, ac.getRawType(), + attrName, type.getRawClass()); + // and with that and property definition + SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(config, + member, propName, metadata, attr.include()); + // can construct the property writer + return AttributePropertyWriter.construct(attrName, propDef, + ac.getAnnotations(), type); + } + + protected BeanPropertyWriter _constructVirtualProperty(JsonAppend.Prop prop, + MapperConfig config, AnnotatedClass ac) + { + PropertyMetadata metadata = prop.required() ? + PropertyMetadata.STD_REQUIRED : PropertyMetadata.STD_OPTIONAL; + PropertyName propName = _propertyName(prop.name(), prop.namespace()); + JavaType type = config.constructType(prop.type()); + // now, then, we need a placeholder for member (no real Field/Method): + AnnotatedMember member = new VirtualAnnotatedMember(ac, ac.getRawType(), + propName.getSimpleName(), type.getRawClass()); + // and with that and property definition + SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(config, + member, propName, metadata, prop.include()); + + Class implClass = prop.value(); + + HandlerInstantiator hi = config.getHandlerInstantiator(); + VirtualBeanPropertyWriter bpw = (hi == null) ? null + : hi.virtualPropertyWriterInstance(config, implClass); + if (bpw == null) { + bpw = (VirtualBeanPropertyWriter) ClassUtil.createInstance(implClass, + config.canOverrideAccessModifiers()); + } + + // one more thing: give it necessary contextual information + return bpw.withConfig(config, ac, propDef, type); + } + + /* + /********************************************************** + /* Serialization: property annotations + /********************************************************** + */ + + @Override + public PropertyName findNameForSerialization(Annotated a) + { + JsonGetter jg = _findAnnotation(a, JsonGetter.class); + if (jg != null) { + return PropertyName.construct(jg.value()); + } + JsonProperty pann = _findAnnotation(a, JsonProperty.class); + if (pann != null) { + return PropertyName.construct(pann.value()); + } + if (_hasOneOf(a, ANNOTATIONS_TO_INFER_SER)) { + return PropertyName.USE_DEFAULT; + } + return null; + } + + @Override + public boolean hasAsValueAnnotation(AnnotatedMethod am) { + JsonValue ann = _findAnnotation(am, JsonValue.class); + // value of 'false' means disabled... + return (ann != null && ann.value()); + } + + /* + /********************************************************** + /* Deserialization: general annotations + /********************************************************** + */ + + @Override + public Object findDeserializer(Annotated a) + { + JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); + if (ann != null) { + @SuppressWarnings("rawtypes") + Class deserClass = ann.using(); + if (deserClass != JsonDeserializer.None.class) { + return deserClass; + } + } + return null; + } + + @Override + public Object findKeyDeserializer(Annotated a) + { + JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); + if (ann != null) { + Class deserClass = ann.keyUsing(); + if (deserClass != KeyDeserializer.None.class) { + return deserClass; + } + } + return null; + } + + @Override + public Object findContentDeserializer(Annotated a) + { + JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); + if (ann != null) { + @SuppressWarnings("rawtypes") + Class deserClass = ann.contentUsing(); + if (deserClass != JsonDeserializer.None.class) { + return deserClass; + } + } + return null; + } + + @Override + public Object findDeserializationConverter(Annotated a) + { + JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); + return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class); + } + + @Override + public Object findDeserializationContentConverter(AnnotatedMember a) + { + JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); + return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class); + } + + /* + /********************************************************** + /* Deserialization: type modifications + /********************************************************** + */ + + @Override + @Deprecated + public Class findDeserializationContentType(Annotated am, JavaType baseContentType) + { + JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class); + return (ann == null) ? null : _classIfExplicit(ann.contentAs()); + } + + @Deprecated + @Override + public Class findDeserializationType(Annotated am, JavaType baseType) { + JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class); + return (ann == null) ? null : _classIfExplicit(ann.as()); + } + + @Override + @Deprecated + public Class findDeserializationKeyType(Annotated am, JavaType baseKeyType) { + JsonDeserialize ann = _findAnnotation(am, JsonDeserialize.class); + return (ann == null) ? null : _classIfExplicit(ann.keyAs()); + } + + /* + /********************************************************** + /* Deserialization: Class annotations + /********************************************************** + */ + + @Override + public Object findValueInstantiator(AnnotatedClass ac) + { + JsonValueInstantiator ann = _findAnnotation(ac, JsonValueInstantiator.class); + // no 'null' marker yet, so: + return (ann == null) ? null : ann.value(); + } + + @Override + public Class findPOJOBuilder(AnnotatedClass ac) + { + JsonDeserialize ann = _findAnnotation(ac, JsonDeserialize.class); + return (ann == null) ? null : _classIfExplicit(ann.builder()); + } + + @Override + public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) + { + JsonPOJOBuilder ann = _findAnnotation(ac, JsonPOJOBuilder.class); + return (ann == null) ? null : new JsonPOJOBuilder.Value(ann); + } + + /* + /********************************************************** + /* Deserialization: property annotations + /********************************************************** + */ + + @Override + public PropertyName findNameForDeserialization(Annotated a) + { + // @JsonSetter has precedence over @JsonProperty, being more specific + // @JsonDeserialize implies that there is a property, but no name + JsonSetter js = _findAnnotation(a, JsonSetter.class); + if (js != null) { + return PropertyName.construct(js.value()); + } + JsonProperty pann = _findAnnotation(a, JsonProperty.class); + if (pann != null) { + return PropertyName.construct(pann.value()); + } + if (_hasOneOf(a, ANNOTATIONS_TO_INFER_DESER)) { + return PropertyName.USE_DEFAULT; + } + return null; + } + + @Override + public boolean hasAnySetterAnnotation(AnnotatedMethod am) + { + /* No dedicated disabling; regular @JsonIgnore used + * if needs to be ignored (and if so, is handled prior + * to this method getting called) + */ + return _hasAnnotation(am, JsonAnySetter.class); + } + + @Override + public boolean hasAnyGetterAnnotation(AnnotatedMethod am) + { + /* No dedicated disabling; regular @JsonIgnore used + * if needs to be ignored (handled separately + */ + return _hasAnnotation(am, JsonAnyGetter.class); + } + + @Override + public boolean hasCreatorAnnotation(Annotated a) + { + /* No dedicated disabling; regular @JsonIgnore used + * if needs to be ignored (and if so, is handled prior + * to this method getting called) + */ + JsonCreator ann = _findAnnotation(a, JsonCreator.class); + if (ann != null) { + return (ann.mode() != JsonCreator.Mode.DISABLED); + } + // 19-Apr-2016, tatu: As per [databind#1197], [databind#1122] (and some related), + // may or may not consider it a creator + if (_cfgConstructorPropertiesImpliesCreator ) { + if (a instanceof AnnotatedConstructor) { + if (_jdk7Helper != null) { + Boolean b = _jdk7Helper.hasCreatorAnnotation(a); + if (b != null) { + return b.booleanValue(); + } + } + } + } + return false; + } + + @Override + public JsonCreator.Mode findCreatorBinding(Annotated a) { + JsonCreator ann = _findAnnotation(a, JsonCreator.class); + return (ann == null) ? null : ann.mode(); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected boolean _isIgnorable(Annotated a) + { + JsonIgnore ann = _findAnnotation(a, JsonIgnore.class); + if (ann != null) { + return ann.value(); + } + if (_jdk7Helper != null) { + Boolean b = _jdk7Helper.findTransient(a); + if (b != null) { + return b.booleanValue(); + } + } + return false; + } + + protected Class _classIfExplicit(Class cls) { + if (cls == null || ClassUtil.isBogusClass(cls)) { + return null; + } + return cls; + } + + protected Class _classIfExplicit(Class cls, Class implicit) { + cls = _classIfExplicit(cls); + return (cls == null || cls == implicit) ? null : cls; + } + + protected PropertyName _propertyName(String localName, String namespace) { + if (localName.isEmpty()) { + return PropertyName.USE_DEFAULT; + } + if (namespace == null || namespace.isEmpty()) { + return PropertyName.construct(localName); + } + return PropertyName.construct(localName, namespace); + } + + protected PropertyName _findConstructorName(Annotated a) + { + if (a instanceof AnnotatedParameter) { + AnnotatedParameter p = (AnnotatedParameter) a; + AnnotatedWithParams ctor = p.getOwner(); + + if (ctor != null) { + if (_jdk7Helper != null) { + PropertyName name = _jdk7Helper.findConstructorName(p); + if (name != null) { + return name; + } + } + } + } + return null; + } + + /** + * Helper method called to construct and initialize instance of {@link TypeResolverBuilder} + * if given annotated element indicates one is needed. + */ + @SuppressWarnings("deprecation") + protected TypeResolverBuilder _findTypeResolver(MapperConfig config, + Annotated ann, JavaType baseType) + { + // First: maybe we have explicit type resolver? + TypeResolverBuilder b; + JsonTypeInfo info = _findAnnotation(ann, JsonTypeInfo.class); + JsonTypeResolver resAnn = _findAnnotation(ann, JsonTypeResolver.class); + + if (resAnn != null) { + if (info == null) { + return null; + } + /* let's not try to force access override (would need to pass + * settings through if we did, since that's not doable on some + * platforms) + */ + b = config.typeResolverBuilderInstance(ann, resAnn.value()); + } else { // if not, use standard one, if indicated by annotations + if (info == null) { + return null; + } + // bit special; must return 'marker' to block use of default typing: + if (info.use() == JsonTypeInfo.Id.NONE) { + return _constructNoTypeResolverBuilder(); + } + b = _constructStdTypeResolverBuilder(); + } + // Does it define a custom type id resolver? + JsonTypeIdResolver idResInfo = _findAnnotation(ann, JsonTypeIdResolver.class); + TypeIdResolver idRes = (idResInfo == null) ? null + : config.typeIdResolverInstance(ann, idResInfo.value()); + if (idRes != null) { + idRes.init(baseType); + } + b = b.init(info.use(), idRes); + /* 13-Aug-2011, tatu: One complication; external id + * only works for properties; so if declared for a Class, we will need + * to map it to "PROPERTY" instead of "EXTERNAL_PROPERTY" + */ + JsonTypeInfo.As inclusion = info.include(); + if (inclusion == JsonTypeInfo.As.EXTERNAL_PROPERTY && (ann instanceof AnnotatedClass)) { + inclusion = JsonTypeInfo.As.PROPERTY; + } + b = b.inclusion(inclusion); + b = b.typeProperty(info.property()); + Class defaultImpl = info.defaultImpl(); + + // 08-Dec-2014, tatu: To deprecate `JsonTypeInfo.None` we need to use other placeholder(s); + // and since `java.util.Void` has other purpose (to indicate "deser as null"), we'll instead + // use `JsonTypeInfo.class` itself. But any annotation type will actually do, as they have no + // valid use (can not instantiate as default) + if (defaultImpl != JsonTypeInfo.None.class && !defaultImpl.isAnnotation()) { + b = b.defaultImpl(defaultImpl); + } + b = b.typeIdVisibility(info.visible()); + return b; + } + + /** + * Helper method for constructing standard {@link TypeResolverBuilder} + * implementation. + */ + protected StdTypeResolverBuilder _constructStdTypeResolverBuilder() { + return new StdTypeResolverBuilder(); + } + + /** + * Helper method for dealing with "no type info" marker; can't be null + * (as it'd be replaced by default typing) + */ + protected StdTypeResolverBuilder _constructNoTypeResolverBuilder() { + return StdTypeResolverBuilder.noTypeInfoBuilder(); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * To support Java7-incomplete platforms, we will offer support for JDK 7 + * annotations through this class, loaded dynamically; if loading fails, + * support will be missing. + */ + private static class Java7Support + { + @SuppressWarnings("unused") // compiler warns, just needed side-effects + private final Class _bogus; + + @SuppressWarnings("unused") // compiler warns; called via Reflection + public Java7Support() { + // Trigger loading of annotations that only JDK 7 has... + Class cls = Transient.class; + cls = ConstructorProperties.class; + _bogus = cls; + } + + public Boolean findTransient(Annotated a) { + Transient t = a.getAnnotation(Transient.class); + if (t != null) { + return t.value(); + } + return null; + } + + public Boolean hasCreatorAnnotation(Annotated a) { + ConstructorProperties props = a.getAnnotation(ConstructorProperties.class); + // 08-Nov-2015, tatu: One possible check would be to ensure there is at least + // one name iff constructor has arguments. But seems unnecessary for now. + if (props != null) { + return Boolean.TRUE; + } + return null; + } + + public PropertyName findConstructorName(AnnotatedParameter p) + { + AnnotatedWithParams ctor = p.getOwner(); + if (ctor != null) { + ConstructorProperties props = ctor.getAnnotation(ConstructorProperties.class); + if (props != null) { + String[] names = props.value(); + int ix = p.getIndex(); + if (ix < names.length) { + return PropertyName.construct(names[ix]); + } + } + } + return null; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/MemberKey.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/MemberKey.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/MemberKey.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * Helper class needed to be able to efficiently access class + * member functions ({@link Method}s and {@link Constructor}s) + * in {@link java.util.Map}s. + */ +public final class MemberKey +{ + final static Class[] NO_CLASSES = new Class[0]; + + final String _name; + final Class[] _argTypes; + + public MemberKey(Method m) + { + this(m.getName(), m.getParameterTypes()); + } + + public MemberKey(Constructor ctor) + { + this("", ctor.getParameterTypes()); + } + + public MemberKey(String name, Class[] argTypes) + { + _name = name; + _argTypes = (argTypes == null) ? NO_CLASSES : argTypes; + } + + @Override + public String toString() { + return _name + "(" + _argTypes.length+"-args)"; + } + + @Override + public int hashCode() + { + return _name.hashCode() + _argTypes.length; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) { + return false; + } + MemberKey other = (MemberKey) o; + if (!_name.equals(other._name)) { + return false; + } + Class[] otherArgs = other._argTypes; + int len = _argTypes.length; + if (otherArgs.length != len) { + return false; + } + for (int i = 0; i < len; ++i) { + Class type1 = otherArgs[i]; + Class type2 = _argTypes[i]; + if (type1 == type2) { + continue; + } + /* 23-Feb-2009, tatu: Are there any cases where we would have to + * consider some narrowing conversions or such? For now let's + * assume exact type match is enough + */ + /* 07-Apr-2009, tatu: Indeed there are (see [JACKSON-97]). + * This happens with generics when a bound is specified. + * I hope this works; check here must be transitive + */ + /* 14-Oct-2014, tatu: No, doing that is wrong. Conflicts may (and will) be + * handled at a later point; trying to change definition of equality + * will just cause problems like [jackson-core#158] + */ + /* + if (type1.isAssignableFrom(type2) || type2.isAssignableFrom(type1)) { + continue; + } + */ + return false; + } + return true; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,36 @@ +package com.fasterxml.jackson.databind.introspect; + +import com.fasterxml.jackson.core.Version; + +import com.fasterxml.jackson.databind.*; + +/** + * Dummy, "no-operation" implementation of {@link AnnotationIntrospector}. + * Can be used as is to suppress handling of annotations; or as a basis + * for simple configuration overrides (whether based on annotations or not). + */ +public abstract class NopAnnotationIntrospector + extends AnnotationIntrospector + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Static immutable and shareable instance that can be used as + * "null" introspector: one that never finds any annotation + * information. + */ + public final static NopAnnotationIntrospector instance = new NopAnnotationIntrospector() { + private static final long serialVersionUID = 1L; + + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + }; + + @Override + public Version version() { + return Version.unknownVersion(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,79 @@ +package com.fasterxml.jackson.databind.introspect; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.annotation.SimpleObjectIdResolver; +import com.fasterxml.jackson.databind.PropertyName; + +/** + * Container object that encapsulates information usually + * derived from {@link JsonIdentityInfo} annotation or its + * custom alternatives + */ +public class ObjectIdInfo +{ + protected final PropertyName _propertyName; + protected final Class> _generator; + protected final Class _resolver; + protected final Class _scope; + protected final boolean _alwaysAsId; + + public ObjectIdInfo(PropertyName name, Class scope, Class> gen, + Class resolver) + { + this(name, scope, gen, false, resolver); + } + + @Deprecated // since 2.4 + public ObjectIdInfo(PropertyName name, Class scope, Class> gen) + { + this(name, scope, gen, false); + } + + @Deprecated // since 2.3 + public ObjectIdInfo(String name, Class scope, Class> gen) { + this(new PropertyName(name), scope, gen, false); + } + + protected ObjectIdInfo(PropertyName prop, Class scope, Class> gen, + boolean alwaysAsId) + { + this(prop, scope, gen, alwaysAsId, SimpleObjectIdResolver.class); + + } + + protected ObjectIdInfo(PropertyName prop, Class scope, Class> gen, + boolean alwaysAsId, Class resolver) + { + _propertyName = prop; + _scope = scope; + _generator = gen; + _alwaysAsId = alwaysAsId; + if (resolver == null) { + resolver = SimpleObjectIdResolver.class; + } + _resolver = resolver; + } + + public ObjectIdInfo withAlwaysAsId(boolean state) { + if (_alwaysAsId == state) { + return this; + } + return new ObjectIdInfo(_propertyName, _scope, _generator, state, _resolver); + } + + public PropertyName getPropertyName() { return _propertyName; } + public Class getScope() { return _scope; } + public Class> getGeneratorType() { return _generator; } + public Class getResolverType() { return _resolver; } + public boolean getAlwaysAsId() { return _alwaysAsId; } + + @Override + public String toString() { + return "ObjectIdInfo: propName="+_propertyName + +", scope="+(_scope == null ? "null" : _scope.getName()) + +", generatorType="+(_generator == null ? "null" : _generator.getName()) + +", alwaysAsId="+_alwaysAsId; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1040 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.Modifier; +import java.util.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.util.BeanUtil; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Helper class used for aggregating information about all possible + * properties of a POJO. + */ +public class POJOPropertiesCollector +{ + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Configuration settings + */ + protected final MapperConfig _config; + + /** + * True if introspection is done for serialization (giving + * precedence for serialization annotations), or not (false, deserialization) + */ + protected final boolean _forSerialization; + + /** + * @since 2.5 + */ + protected final boolean _stdBeanNaming; + + /** + * Type of POJO for which properties are being collected. + */ + protected final JavaType _type; + + /** + * Low-level introspected class information (methods, fields etc) + */ + protected final AnnotatedClass _classDef; + + protected final VisibilityChecker _visibilityChecker; + + protected final AnnotationIntrospector _annotationIntrospector; + + /** + * Prefix used by auto-detected mutators ("setters"): usually "set", + * but differs for builder objects ("with" by default). + */ + protected final String _mutatorPrefix; + + /* + /********************************************************** + /* Collected property information + /********************************************************** + */ + + /** + * State flag we keep to indicate whether actual property information + * has been collected or not. + */ + protected boolean _collected; + + /** + * Set of logical property information collected so far. + *

+ * Since 2.6, this has been constructed (more) lazily, to defer + * throwing of exceptions for potential conflicts in cases where + * this may not be an actual problem. + */ + protected LinkedHashMap _properties; + + protected LinkedList _creatorProperties ; + + protected LinkedList _anyGetters; + + protected LinkedList _anySetters; + + /** + * Method(s) marked with 'JsonValue' annotation + */ + protected LinkedList _jsonValueGetters; + + /** + * Lazily collected list of properties that can be implicitly + * ignored during serialization; only updated when collecting + * information for deserialization purposes + */ + protected HashSet _ignoredPropertyNames; + + /** + * Lazily collected list of members that were annotated to + * indicate that they represent mutators for deserializer + * value injection. + */ + protected LinkedHashMap _injectables; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected POJOPropertiesCollector(MapperConfig config, boolean forSerialization, + JavaType type, AnnotatedClass classDef, String mutatorPrefix) + { + _config = config; + _stdBeanNaming = config.isEnabled(MapperFeature.USE_STD_BEAN_NAMING); + _forSerialization = forSerialization; + _type = type; + _classDef = classDef; + _mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix; + _annotationIntrospector = config.isAnnotationProcessingEnabled() ? + _config.getAnnotationIntrospector() : null; + if (_annotationIntrospector == null) { + _visibilityChecker = _config.getDefaultVisibilityChecker(); + } else { + _visibilityChecker = _annotationIntrospector.findAutoDetectVisibility(classDef, + _config.getDefaultVisibilityChecker()); + } + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + public MapperConfig getConfig() { + return _config; + } + + public JavaType getType() { + return _type; + } + + public AnnotatedClass getClassDef() { + return _classDef; + } + + public AnnotationIntrospector getAnnotationIntrospector() { + return _annotationIntrospector; + } + + public List getProperties() { + // make sure we return a copy, so caller can remove entries if need be: + Map props = getPropertyMap(); + return new ArrayList(props.values()); + } + + public Map getInjectables() { + if (!_collected) { + collectAll(); + } + return _injectables; + } + + public AnnotatedMethod getJsonValueMethod() + { + if (!_collected) { + collectAll(); + } + // If @JsonValue defined, must have a single one + if (_jsonValueGetters != null) { + if (_jsonValueGetters.size() > 1) { + reportProblem("Multiple value properties defined ("+_jsonValueGetters.get(0)+" vs " + +_jsonValueGetters.get(1)+")"); + } + // otherwise we won't greatly care + return _jsonValueGetters.get(0); + } + return null; + } + + public AnnotatedMember getAnyGetter() + { + if (!_collected) { + collectAll(); + } + if (_anyGetters != null) { + if (_anyGetters.size() > 1) { + reportProblem("Multiple 'any-getters' defined ("+_anyGetters.get(0)+" vs " + +_anyGetters.get(1)+")"); + } + return _anyGetters.getFirst(); + } + return null; + } + + public AnnotatedMethod getAnySetterMethod() + { + if (!_collected) { + collectAll(); + } + if (_anySetters != null) { + if (_anySetters.size() > 1) { + reportProblem("Multiple 'any-setters' defined ("+_anySetters.get(0)+" vs " + +_anySetters.get(1)+")"); + } + return _anySetters.getFirst(); + } + return null; + } + + /** + * Accessor for set of properties that are explicitly marked to be ignored + * via per-property markers (but NOT class annotations). + */ + public Set getIgnoredPropertyNames() { + return _ignoredPropertyNames; + } + + /** + * Accessor to find out whether type specified requires inclusion + * of Object Identifier. + */ + public ObjectIdInfo getObjectIdInfo() + { + if (_annotationIntrospector == null) { + return null; + } + ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_classDef); + if (info != null) { // 2.1: may also have different defaults for refs: + info = _annotationIntrospector.findObjectReferenceInfo(_classDef, info); + } + return info; + } + + /** + * Method for finding Class to use as POJO builder, if any. + */ + public Class findPOJOBuilderClass() + { + return _annotationIntrospector.findPOJOBuilder(_classDef); + } + + // for unit tests: + protected Map getPropertyMap() { + if (!_collected) { + collectAll(); + } + return _properties; + } + + /* + /********************************************************** + /* Public API: main-level collection + /********************************************************** + */ + + /** + * Method that orchestrates collection activities, and needs to be called + * after creating the instance. + *

+ * Since 2.6 has become a no-op and actual collection is done more lazily + * at point where properties are actually needed. + * + * @deprecated Since 2.6; no need to call + */ + @Deprecated + public POJOPropertiesCollector collect() { + return this; + } + + /** + * Internal method that will collect actual property information. + * + * @since 2.6 + */ + protected void collectAll() + { + LinkedHashMap props = new LinkedHashMap(); + + // First: gather basic data + _addFields(props); + _addMethods(props); + _addCreators(props); + _addInjectables(props); + + // Remove ignored properties, first; this MUST precede annotation merging + // since logic relies on knowing exactly which accessor has which annotation + _removeUnwantedProperties(props); + + // then merge annotations, to simplify further processing + for (POJOPropertyBuilder property : props.values()) { + property.mergeAnnotations(_forSerialization); + } + + // and then remove unneeded accessors (wrt read-only, read-write) + _removeUnwantedAccessor(props); + + // Rename remaining properties + _renameProperties(props); + + // And use custom naming strategy, if applicable... + PropertyNamingStrategy naming = _findNamingStrategy(); + if (naming != null) { + _renameUsing(props, naming); + } + + /* Sort by visibility (explicit over implicit); drop all but first + * of member type (getter, setter etc) if there is visibility + * difference + */ + for (POJOPropertyBuilder property : props.values()) { + property.trimByVisibility(); + } + + /* and, if required, apply wrapper name: note, MUST be done after + * annotations are merged. + */ + if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) { + _renameWithWrappers(props); + } + + // well, almost last: there's still ordering... + _sortProperties(props); + _properties = props; + + _collected = true; + } + + /* + /********************************************************** + /* Overridable internal methods, adding members + /********************************************************** + */ + + /** + * Method for collecting basic information on all fields found + */ + protected void _addFields(Map props) + { + final AnnotationIntrospector ai = _annotationIntrospector; + /* 28-Mar-2013, tatu: For deserialization we may also want to remove + * final fields, as often they won't make very good mutators... + * (although, maybe surprisingly, JVM _can_ force setting of such fields!) + */ + final boolean pruneFinalFields = !_forSerialization && !_config.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS); + final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER); + + for (AnnotatedField f : _classDef.fields()) { + String implName = (ai == null) ? null : ai.findImplicitPropertyName(f); + if (implName == null) { + implName = f.getName(); + } + + PropertyName pn; + + if (ai == null) { + pn = null; + } else if (_forSerialization) { + /* 18-Aug-2011, tatu: As per existing unit tests, we should only + * use serialization annotation (@JsonSerialize) when serializing + * fields, and similarly for deserialize-only annotations... so + * no fallbacks in this particular case. + */ + pn = ai.findNameForSerialization(f); + } else { + pn = ai.findNameForDeserialization(f); + } + boolean nameExplicit = (pn != null); + + if (nameExplicit && pn.isEmpty()) { // empty String meaning "use default name", here just means "same as field name" + pn = _propNameFromSimple(implName); + nameExplicit = false; + } + // having explicit name means that field is visible; otherwise need to check the rules + boolean visible = (pn != null); + if (!visible) { + visible = _visibilityChecker.isFieldVisible(f); + } + // and finally, may also have explicit ignoral + boolean ignored = (ai != null) && ai.hasIgnoreMarker(f); + + // 13-May-2015, tatu: Moved from earlier place (AnnotatedClass) in 2.6 + if (f.isTransient()) { + visible = false; + if (transientAsIgnoral) { + ignored = true; + } + } + /* [databind#190]: this is the place to prune final fields, if they are not + * to be used as mutators. Must verify they are not explicitly included. + * Also: if 'ignored' is set, need to included until a later point, to + * avoid losing ignoral information. + */ + if (pruneFinalFields && (pn == null) && !ignored && Modifier.isFinal(f.getModifiers())) { + continue; + } + _property(props, implName).addField(f, pn, nameExplicit, visible, ignored); + } + } + + /** + * Method for collecting basic information on constructor(s) found + */ + protected void _addCreators(Map props) + { + // can be null if annotation processing is disabled... + if (_annotationIntrospector != null) { + for (AnnotatedConstructor ctor : _classDef.getConstructors()) { + if (_creatorProperties == null) { + _creatorProperties = new LinkedList(); + } + for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) { + _addCreatorParam(props, ctor.getParameter(i)); + } + } + for (AnnotatedMethod factory : _classDef.getStaticMethods()) { + if (_creatorProperties == null) { + _creatorProperties = new LinkedList(); + } + for (int i = 0, len = factory.getParameterCount(); i < len; ++i) { + _addCreatorParam(props, factory.getParameter(i)); + } + } + } + } + + /** + * @since 2.4 + */ + protected void _addCreatorParam(Map props, + AnnotatedParameter param) + { + // JDK 8, paranamer, Scala can give implicit name + String impl = _annotationIntrospector.findImplicitPropertyName(param); + if (impl == null) { + impl = ""; + } + PropertyName pn = _annotationIntrospector.findNameForDeserialization(param); + boolean expl = (pn != null && !pn.isEmpty()); + if (!expl) { + if (impl.isEmpty()) { + /* Important: if neither implicit nor explicit name, can not make use + * of this creator parameter -- may or may not be a problem, verified + * at a later point. + */ + return; + } + // Also: if this occurs, there MUST be explicit annotation on creator itself + if (!_annotationIntrospector.hasCreatorAnnotation(param.getOwner())) { + return; + } + pn = PropertyName.construct(impl); + } + + // shouldn't need to worry about @JsonIgnore, since creators only added + // if so annotated + + /* 13-May-2015, tatu: We should try to start with implicit name, similar to how + * fields and methods work; but unlike those, we don't necessarily have + * implicit name to use (pre-Java8 at least). So: + */ + POJOPropertyBuilder prop = (expl && impl.isEmpty()) + ? _property(props, pn) : _property(props, impl); + prop.addCtor(param, pn, expl, true, false); + _creatorProperties.add(prop); + } + + /** + * Method for collecting basic information on all fields found + */ + protected void _addMethods(Map props) + { + final AnnotationIntrospector ai = _annotationIntrospector; + + for (AnnotatedMethod m : _classDef.memberMethods()) { + /* For methods, handling differs between getters and setters; and + * we will also only consider entries that either follow the bean + * naming convention or are explicitly marked: just being visible + * is not enough (unlike with fields) + */ + int argCount = m.getParameterCount(); + if (argCount == 0) { // getters (including 'any getter') + _addGetterMethod(props, m, ai); + } else if (argCount == 1) { // setters + _addSetterMethod(props, m, ai); + } else if (argCount == 2) { // any getter? + if (ai != null && ai.hasAnySetterAnnotation(m)) { + if (_anySetters == null) { + _anySetters = new LinkedList(); + } + _anySetters.add(m); + } + } + } + } + + protected void _addGetterMethod(Map props, + AnnotatedMethod m, AnnotationIntrospector ai) + { + // Very first thing: skip if not returning any value + if (!m.hasReturnType()) { + return; + } + + // any getter? + if (ai != null) { + if (ai.hasAnyGetterAnnotation(m)) { + if (_anyGetters == null) { + _anyGetters = new LinkedList(); + } + _anyGetters.add(m); + return; + } + // @JsonValue? + if (ai.hasAsValueAnnotation(m)) { + if (_jsonValueGetters == null) { + _jsonValueGetters = new LinkedList(); + } + _jsonValueGetters.add(m); + return; + } + } + String implName; // from naming convention + boolean visible; + + PropertyName pn = (ai == null) ? null : ai.findNameForSerialization(m); + boolean nameExplicit = (pn != null); + + if (!nameExplicit) { // no explicit name; must consider implicit + implName = (ai == null) ? null : ai.findImplicitPropertyName(m); + if (implName == null) { + implName = BeanUtil.okNameForRegularGetter(m, m.getName(), _stdBeanNaming); + } + if (implName == null) { // if not, must skip + implName = BeanUtil.okNameForIsGetter(m, m.getName(), _stdBeanNaming); + if (implName == null) { + return; + } + visible = _visibilityChecker.isIsGetterVisible(m); + } else { + visible = _visibilityChecker.isGetterVisible(m); + } + } else { // explicit indication of inclusion, but may be empty + // we still need implicit name to link with other pieces + implName = (ai == null) ? null : ai.findImplicitPropertyName(m); + if (implName == null) { + implName = BeanUtil.okNameForGetter(m, _stdBeanNaming); + } + // if not regular getter name, use method name as is + if (implName == null) { + implName = m.getName(); + } + if (pn.isEmpty()) { + // !!! TODO: use PropertyName for implicit names too + pn = _propNameFromSimple(implName); + nameExplicit = false; + } + visible = true; + } + boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m); + _property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore); + } + + protected void _addSetterMethod(Map props, + AnnotatedMethod m, AnnotationIntrospector ai) + { + String implName; // from naming convention + boolean visible; + PropertyName pn = (ai == null) ? null : ai.findNameForDeserialization(m); + boolean nameExplicit = (pn != null); + if (!nameExplicit) { // no explicit name; must follow naming convention + implName = (ai == null) ? null : ai.findImplicitPropertyName(m); + if (implName == null) { + implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming); + } + if (implName == null) { // if not, must skip + return; + } + visible = _visibilityChecker.isSetterVisible(m); + } else { // explicit indication of inclusion, but may be empty + // we still need implicit name to link with other pieces + implName = (ai == null) ? null : ai.findImplicitPropertyName(m); + if (implName == null) { + implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming); + } + // if not regular getter name, use method name as is + if (implName == null) { + implName = m.getName(); + } + if (pn.isEmpty()) { + // !!! TODO: use PropertyName for implicit names too + pn = _propNameFromSimple(implName); + nameExplicit = false; + } + visible = true; + } + boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m); + _property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore); + } + + protected void _addInjectables(Map props) + { + final AnnotationIntrospector ai = _annotationIntrospector; + if (ai == null) { + return; + } + + // first fields, then methods + for (AnnotatedField f : _classDef.fields()) { + _doAddInjectable(ai.findInjectableValueId(f), f); + } + + for (AnnotatedMethod m : _classDef.memberMethods()) { + /* for now, only allow injection of a single arg + * (to be changed in future) + */ + if (m.getParameterCount() != 1) { + continue; + } + _doAddInjectable(ai.findInjectableValueId(m), m); + } + } + + protected void _doAddInjectable(Object id, AnnotatedMember m) + { + if (id == null) { + return; + } + if (_injectables == null) { + _injectables = new LinkedHashMap(); + } + AnnotatedMember prev = _injectables.put(id, m); + if (prev != null) { + String type = id.getClass().getName(); + throw new IllegalArgumentException("Duplicate injectable value with id '" + +String.valueOf(id)+"' (of type "+type+")"); + } + } + + private PropertyName _propNameFromSimple(String simpleName) { + return PropertyName.construct(simpleName, null); + } + + /* + /********************************************************** + /* Internal methods; removing ignored properties + /********************************************************** + */ + + /** + * Method called to get rid of candidate properties that are marked + * as ignored. + */ + protected void _removeUnwantedProperties(Map props) + { + Iterator it = props.values().iterator(); + + while (it.hasNext()) { + POJOPropertyBuilder prop = it.next(); + + // First: if nothing visible, just remove altogether + if (!prop.anyVisible()) { + it.remove(); + continue; + } + // Otherwise, check ignorals + if (prop.anyIgnorals()) { + // first: if one or more ignorals, and no explicit markers, remove the whole thing + if (!prop.isExplicitlyIncluded()) { + it.remove(); + _collectIgnorals(prop.getName()); + continue; + } + // otherwise just remove ones marked to be ignored + prop.removeIgnored(); + if (!_forSerialization && !prop.couldDeserialize()) { + _collectIgnorals(prop.getName()); + } + } + } + } + + /** + * Method called to further get rid of unwanted individual accessors, + * based on read/write settings and rules for "pulling in" accessors + * (or not). + */ + protected void _removeUnwantedAccessor(Map props) + { + final boolean inferMutators = _config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS); + Iterator it = props.values().iterator(); + + while (it.hasNext()) { + POJOPropertyBuilder prop = it.next(); + prop.removeNonVisible(inferMutators); + } + } + + /** + * Helper method called to add explicitly ignored properties to a list + * of known ignored properties; this helps in proper reporting of + * errors. + */ + private void _collectIgnorals(String name) + { + if (!_forSerialization) { + if (_ignoredPropertyNames == null) { + _ignoredPropertyNames = new HashSet(); + } + _ignoredPropertyNames.add(name); + } + } + + /* + /********************************************************** + /* Internal methods; renaming properties + /********************************************************** + */ + + protected void _renameProperties(Map props) + { + // With renaming need to do in phases: first, find properties to rename + Iterator> it = props.entrySet().iterator(); + LinkedList renamed = null; + while (it.hasNext()) { + Map.Entry entry = it.next(); + POJOPropertyBuilder prop = entry.getValue(); + + Collection l = prop.findExplicitNames(); + + // no explicit names? Implicit one is fine as is + if (l.isEmpty()) { + continue; + } + it.remove(); // need to replace with one or more renamed + if (renamed == null) { + renamed = new LinkedList(); + } + // simple renaming? Just do it + if (l.size() == 1) { + PropertyName n = l.iterator().next(); + renamed.add(prop.withName(n)); + continue; + } + // but this may be problematic... + renamed.addAll(prop.explode(l)); + + /* + String newName = prop.findNewName(); + if (newName != null) { + if (renamed == null) { + renamed = new LinkedList(); + } + prop = prop.withSimpleName(newName); + renamed.add(prop); + it.remove(); + } + */ + } + + // and if any were renamed, merge back in... + if (renamed != null) { + for (POJOPropertyBuilder prop : renamed) { + String name = prop.getName(); + POJOPropertyBuilder old = props.get(name); + if (old == null) { + props.put(name, prop); + } else { + old.addAll(prop); + } + // replace the creatorProperty too, if there is one + _updateCreatorProperty(prop, _creatorProperties); + } + } + } + + protected void _renameUsing(Map propMap, + PropertyNamingStrategy naming) + { + POJOPropertyBuilder[] props = propMap.values().toArray(new POJOPropertyBuilder[propMap.size()]); + propMap.clear(); + for (POJOPropertyBuilder prop : props) { + PropertyName fullName = prop.getFullName(); + String rename = null; + // As per [databind#428] need to skip renaming if property has + // explicitly defined name, unless feature is enabled + if (!prop.isExplicitlyNamed() || _config.isEnabled(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING)) { + if (_forSerialization) { + if (prop.hasGetter()) { + rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName()); + } else if (prop.hasField()) { + rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName()); + } + } else { + if (prop.hasSetter()) { + rename = naming.nameForSetterMethod(_config, prop.getSetter(), fullName.getSimpleName()); + } else if (prop.hasConstructorParameter()) { + rename = naming.nameForConstructorParameter(_config, prop.getConstructorParameter(), fullName.getSimpleName()); + } else if (prop.hasField()) { + rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName()); + } else if (prop.hasGetter()) { + /* Plus, when getter-as-setter is used, need to convert that too.. + * (should we verify that's enabled? For now, assume it's ok always) + */ + rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName()); + } + } + } + final String simpleName; + if (rename != null && !fullName.hasSimpleName(rename)) { + prop = prop.withSimpleName(rename); + simpleName = rename; + } else { + simpleName = fullName.getSimpleName(); + } + /* As per [JACKSON-687], need to consider case where there may already be + * something in there... + */ + POJOPropertyBuilder old = propMap.get(simpleName); + if (old == null) { + propMap.put(simpleName, prop); + } else { + old.addAll(prop); + } + // replace the creatorProperty too, if there is one + _updateCreatorProperty(prop, _creatorProperties); + } + } + + protected void _renameWithWrappers(Map props) + { + /* 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME', + * need another round of renaming... + */ + Iterator> it = props.entrySet().iterator(); + LinkedList renamed = null; + while (it.hasNext()) { + Map.Entry entry = it.next(); + POJOPropertyBuilder prop = entry.getValue(); + AnnotatedMember member = prop.getPrimaryMember(); + if (member == null) { + continue; + } + PropertyName wrapperName = _annotationIntrospector.findWrapperName(member); + // One trickier part (wrt [Issue#24] of JAXB annotations: wrapper that + // indicates use of actual property... But hopefully has been taken care + // of previously + if (wrapperName == null || !wrapperName.hasSimpleName()) { + continue; + } + if (!wrapperName.equals(prop.getFullName())) { + if (renamed == null) { + renamed = new LinkedList(); + } + prop = prop.withName(wrapperName); + renamed.add(prop); + it.remove(); + } + } + // and if any were renamed, merge back in... + if (renamed != null) { + for (POJOPropertyBuilder prop : renamed) { + String name = prop.getName(); + POJOPropertyBuilder old = props.get(name); + if (old == null) { + props.put(name, prop); + } else { + old.addAll(prop); + } + } + } + } + + /* + /********************************************************** + /* Overridable internal methods, sorting, other stuff + /********************************************************** + */ + + /* First, order by [JACKSON-90] (explicit ordering and/or alphabetic) + * and then for [JACKSON-170] (implicitly order creator properties before others) + */ + protected void _sortProperties(Map props) + { + // Then how about explicit ordering? + AnnotationIntrospector intr = _annotationIntrospector; + boolean sort; + Boolean alpha = (intr == null) ? null : intr.findSerializationSortAlphabetically((Annotated) _classDef); + + if (alpha == null) { + sort = _config.shouldSortPropertiesAlphabetically(); + } else { + sort = alpha.booleanValue(); + } + String[] propertyOrder = (intr == null) ? null : intr.findSerializationPropertyOrder(_classDef); + + // no sorting? no need to shuffle, then + if (!sort && (_creatorProperties == null) && (propertyOrder == null)) { + return; + } + int size = props.size(); + Map all; + // Need to (re)sort alphabetically? + if (sort) { + all = new TreeMap(); + } else { + all = new LinkedHashMap(size+size); + } + + for (POJOPropertyBuilder prop : props.values()) { + all.put(prop.getName(), prop); + } + Map ordered = new LinkedHashMap(size+size); + // Ok: primarily by explicit order + if (propertyOrder != null) { + for (String name : propertyOrder) { + POJOPropertyBuilder w = all.get(name); + if (w == null) { // also, as per [JACKSON-268], we will allow use of "implicit" names + for (POJOPropertyBuilder prop : props.values()) { + if (name.equals(prop.getInternalName())) { + w = prop; + // plus re-map to external name, to avoid dups: + name = prop.getName(); + break; + } + } + } + if (w != null) { + ordered.put(name, w); + } + } + } + // And secondly by sorting Creator properties before other unordered properties + if (_creatorProperties != null) { + /* As per [databind#311], this is bit delicate; but if alphabetic ordering + * is mandated, at least ensure creator properties are in alphabetic + * order. Related question of creator vs non-creator is punted for now, + * so creator properties still fully predate non-creator ones. + */ + Collection cr; + if (sort) { + TreeMap sorted = + new TreeMap(); + for (POJOPropertyBuilder prop : _creatorProperties) { + sorted.put(prop.getName(), prop); + } + cr = sorted.values(); + } else { + cr = _creatorProperties; + } + for (POJOPropertyBuilder prop : cr) { + ordered.put(prop.getName(), prop); + } + } + // And finally whatever is left (trying to put again will not change ordering) + ordered.putAll(all); + + props.clear(); + props.putAll(ordered); + } + + /* + /********************************************************** + /* Internal methods; helpers + /********************************************************** + */ + + protected void reportProblem(String msg) { + throw new IllegalArgumentException("Problem with definition of "+_classDef+": "+msg); + } + + protected POJOPropertyBuilder _property(Map props, + PropertyName name) { + return _property(props, name.getSimpleName()); + } + + // !!! TODO: deprecate, require use of PropertyName + protected POJOPropertyBuilder _property(Map props, + String implName) + { + POJOPropertyBuilder prop = props.get(implName); + if (prop == null) { + prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization, + PropertyName.construct(implName)); + props.put(implName, prop); + } + return prop; + } + + private PropertyNamingStrategy _findNamingStrategy() + { + Object namingDef = (_annotationIntrospector == null)? null + : _annotationIntrospector.findNamingStrategy(_classDef); + if (namingDef == null) { + return _config.getPropertyNamingStrategy(); + } + if (namingDef instanceof PropertyNamingStrategy) { + return (PropertyNamingStrategy) namingDef; + } + /* Alas, there's no way to force return type of "either class + * X or Y" -- need to throw an exception after the fact + */ + if (!(namingDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned PropertyNamingStrategy definition of type " + +namingDef.getClass().getName()+"; expected type PropertyNamingStrategy or Class instead"); + } + Class namingClass = (Class)namingDef; + // 09-Nov-2015, tatu: Need to consider pseudo-value of STD, which means "use default" + if (namingClass == PropertyNamingStrategy.class) { + return null; + } + + if (!PropertyNamingStrategy.class.isAssignableFrom(namingClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class " + +namingClass.getName()+"; expected Class"); + } + HandlerInstantiator hi = _config.getHandlerInstantiator(); + if (hi != null) { + PropertyNamingStrategy pns = hi.namingStrategyInstance(_config, _classDef, namingClass); + if (pns != null) { + return pns; + } + } + return (PropertyNamingStrategy) ClassUtil.createInstance(namingClass, + _config.canOverrideAccessModifiers()); + } + + protected void _updateCreatorProperty(POJOPropertyBuilder prop, List creatorProperties) { + if (creatorProperties != null) { + for (int i = 0, len = creatorProperties.size(); i < len; ++i) { + if (creatorProperties.get(i).getInternalName().equals(prop.getInternalName())) { + creatorProperties.set(i, prop); + break; + } + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1235 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Helper class used for aggregating information about a single + * potential POJO property. + */ +public class POJOPropertyBuilder + extends BeanPropertyDefinition + implements Comparable +{ + /** + * Whether property is being composed for serialization + * (true) or deserialization (false) + */ + protected final boolean _forSerialization; + + protected final MapperConfig _config; + + protected final AnnotationIntrospector _annotationIntrospector; + + /** + * External name of logical property; may change with + * renaming (by new instance being constructed using + * a new name) + */ + protected final PropertyName _name; + + /** + * Original internal name, derived from accessor, of this + * property. Will not be changed by renaming. + */ + protected final PropertyName _internalName; + + protected Linked _fields; + + protected Linked _ctorParameters; + + protected Linked _getters; + + protected Linked _setters; + + public POJOPropertyBuilder(MapperConfig config, AnnotationIntrospector ai, + boolean forSerialization, PropertyName internalName) { + this(config, ai, forSerialization, internalName, internalName); + } + + protected POJOPropertyBuilder(MapperConfig config, AnnotationIntrospector ai, + boolean forSerialization, PropertyName internalName, PropertyName name) + { + _config = config; + _annotationIntrospector = ai; + _internalName = internalName; + _name = name; + _forSerialization = forSerialization; + } + + public POJOPropertyBuilder(POJOPropertyBuilder src, PropertyName newName) + { + _config = src._config; + _annotationIntrospector = src._annotationIntrospector; + _internalName = src._internalName; + _name = newName; + _fields = src._fields; + _ctorParameters = src._ctorParameters; + _getters = src._getters; + _setters = src._setters; + _forSerialization = src._forSerialization; + } + + /* + /********************************************************** + /* Fluent factory methods + /********************************************************** + */ + + @Override + public POJOPropertyBuilder withName(PropertyName newName) { + return new POJOPropertyBuilder(this, newName); + } + + @Override + public POJOPropertyBuilder withSimpleName(String newSimpleName) + { + PropertyName newName = _name.withSimpleName(newSimpleName); + return (newName == _name) ? this : new POJOPropertyBuilder(this, newName); + } + + /* + /********************************************************** + /* Comparable implementation: sort alphabetically, except + /* that properties with constructor parameters sorted + /* before other properties + /********************************************************** + */ + + @Override + public int compareTo(POJOPropertyBuilder other) + { + // first, if one has ctor params, that should come first: + if (_ctorParameters != null) { + if (other._ctorParameters == null) { + return -1; + } + } else if (other._ctorParameters != null) { + return 1; + } + /* otherwise sort by external name (including sorting of + * ctor parameters) + */ + return getName().compareTo(other.getName()); + } + + /* + /********************************************************** + /* BeanPropertyDefinition implementation, name/type + /********************************************************** + */ + + @Override + public String getName() { + return (_name == null) ? null : _name.getSimpleName(); + } + + @Override + public PropertyName getFullName() { + return _name; + } + + @Override + public boolean hasName(PropertyName name) { + return _name.equals(name); + } + + @Override + public String getInternalName() { return _internalName.getSimpleName(); } + + @Override + public PropertyName getWrapperName() { + /* 13-Mar-2013, tatu: Accessing via primary member SHOULD work, + * due to annotation merging. However, I have seen some problems + * with this access (for other annotations)... so if this should + * occur, try commenting out full traversal code + */ + AnnotatedMember member = getPrimaryMember(); + return (member == null || _annotationIntrospector == null) ? null + : _annotationIntrospector.findWrapperName(member); + /* + return fromMemberAnnotations(new WithMember() { + @Override + public PropertyName withMember(AnnotatedMember member) { + return _annotationIntrospector.findWrapperName(member); + } + }); + */ + } + + @Override + public boolean isExplicitlyIncluded() { + return _anyExplicits(_fields) + || _anyExplicits(_getters) + || _anyExplicits(_setters) + || _anyExplicits(_ctorParameters) + ; + } + + @Override + public boolean isExplicitlyNamed() { + return _anyExplicitNames(_fields) + || _anyExplicitNames(_getters) + || _anyExplicitNames(_setters) + || _anyExplicitNames(_ctorParameters) + ; + } + + /* + /********************************************************** + /* BeanPropertyDefinition implementation, accessor access + /********************************************************** + */ + + @Override + public boolean hasGetter() { return _getters != null; } + + @Override + public boolean hasSetter() { return _setters != null; } + + @Override + public boolean hasField() { return _fields != null; } + + @Override + public boolean hasConstructorParameter() { return _ctorParameters != null; } + + @Override + public boolean couldDeserialize() { + return (_ctorParameters != null) || (_setters != null) || (_fields != null); + } + + @Override + public boolean couldSerialize() { + return (_getters != null) || (_fields != null); + } + + @Override + public AnnotatedMethod getGetter() + { + // Easy with zero or one getters... + Linked curr = _getters; + if (curr == null) { + return null; + } + Linked next = curr.next; + if (next == null) { + return curr.value; + } + // But if multiple, verify that they do not conflict... + for (; next != null; next = next.next) { + /* [JACKSON-255] Allow masking, i.e. do not report exception if one + * is in super-class from the other + */ + Class currClass = curr.value.getDeclaringClass(); + Class nextClass = next.value.getDeclaringClass(); + if (currClass != nextClass) { + if (currClass.isAssignableFrom(nextClass)) { // next is more specific + curr = next; + continue; + } + if (nextClass.isAssignableFrom(currClass)) { // current more specific + continue; + } + } + /* 30-May-2014, tatu: Three levels of precedence: + * + * 1. Regular getters ("getX") + * 2. Is-getters ("isX") + * 3. Implicit, possible getters ("x") + */ + int priNext = _getterPriority(next.value); + int priCurr = _getterPriority(curr.value); + + if (priNext != priCurr) { + if (priNext < priCurr) { + curr = next; + } + continue; + } + throw new IllegalArgumentException("Conflicting getter definitions for property \""+getName()+"\": " + +curr.value.getFullName()+" vs "+next.value.getFullName()); + } + // One more thing; to avoid having to do it again... + _getters = curr.withoutNext(); + return curr.value; + } + + @Override + public AnnotatedMethod getSetter() + { + // Easy with zero or one getters... + Linked curr = _setters; + if (curr == null) { + return null; + } + Linked next = curr.next; + if (next == null) { + return curr.value; + } + // But if multiple, verify that they do not conflict... + for (; next != null; next = next.next) { + // Allow masking, i.e. do not fail if one is in super-class from the other + Class currClass = curr.value.getDeclaringClass(); + Class nextClass = next.value.getDeclaringClass(); + if (currClass != nextClass) { + if (currClass.isAssignableFrom(nextClass)) { // next is more specific + curr = next; + continue; + } + if (nextClass.isAssignableFrom(currClass)) { // current more specific + continue; + } + } + AnnotatedMethod nextM = next.value; + AnnotatedMethod currM = curr.value; + + /* 30-May-2014, tatu: Two levels of precedence: + * + * 1. Regular setters ("setX(...)") + * 2. Implicit, possible setters ("x(...)") + */ + int priNext = _setterPriority(nextM); + int priCurr = _setterPriority(currM); + + if (priNext != priCurr) { + if (priNext < priCurr) { + curr = next; + } + continue; + } + // 11-Dec-2015, tatu: As per [databind#1033] allow pluggable conflict resolution + if (_annotationIntrospector != null) { + AnnotatedMethod pref = _annotationIntrospector.resolveSetterConflict(_config, + currM, nextM); + + // note: should be one of nextM/currM; but no need to check + if (pref == currM) { + continue; + } + if (pref == nextM) { + curr = next; + continue; + } + } + + throw new IllegalArgumentException("Conflicting setter definitions for property \""+getName()+"\": " + +curr.value.getFullName()+" vs "+next.value.getFullName()); + } + // One more thing; to avoid having to do it again... + _setters = curr.withoutNext(); + return curr.value; + } + + @Override + public AnnotatedField getField() + { + if (_fields == null) { + return null; + } + // If multiple, verify that they do not conflict... + AnnotatedField field = _fields.value; + Linked next = _fields.next; + for (; next != null; next = next.next) { + AnnotatedField nextField = next.value; + Class fieldClass = field.getDeclaringClass(); + Class nextClass = nextField.getDeclaringClass(); + if (fieldClass != nextClass) { + if (fieldClass.isAssignableFrom(nextClass)) { // next is more specific + field = nextField; + continue; + } + if (nextClass.isAssignableFrom(fieldClass)) { // getter more specific + continue; + } + } + throw new IllegalArgumentException("Multiple fields representing property \""+getName()+"\": " + +field.getFullName()+" vs "+nextField.getFullName()); + } + return field; + } + + @Override + public AnnotatedParameter getConstructorParameter() + { + if (_ctorParameters == null) { + return null; + } + /* Hmmh. Checking for constructor parameters is trickier; for one, + * we must allow creator and factory method annotations. + * If this is the case, constructor parameter has the precedence. + * + * So, for now, just try finding the first constructor parameter; + * if none, first factory method. And don't check for dups, if we must, + * can start checking for them later on. + */ + Linked curr = _ctorParameters; + do { + if (curr.value.getOwner() instanceof AnnotatedConstructor) { + return curr.value; + } + curr = curr.next; + } while (curr != null); + return _ctorParameters.value; + } + + @Override + public Iterator getConstructorParameters() { + if (_ctorParameters == null) { + return ClassUtil.emptyIterator(); + } + return new MemberIterator(_ctorParameters); + } + + @Override + public AnnotatedMember getAccessor() + { + AnnotatedMember m = getGetter(); + if (m == null) { + m = getField(); + } + return m; + } + + @Override + public AnnotatedMember getMutator() + { + AnnotatedMember m = getConstructorParameter(); + if (m == null) { + m = getSetter(); + if (m == null) { + m = getField(); + } + } + return m; + } + + @Override + public AnnotatedMember getNonConstructorMutator() { + AnnotatedMember m = getSetter(); + if (m == null) { + m = getField(); + } + return m; + } + + @Override + public AnnotatedMember getPrimaryMember() { + if (_forSerialization) { + return getAccessor(); + } + return getMutator(); + } + + protected int _getterPriority(AnnotatedMethod m) + { + final String name = m.getName(); + // [databind#238]: Also, regular getters have precedence over "is-getters" + if (name.startsWith("get") && name.length() > 3) { + // should we check capitalization? + return 1; + } + if (name.startsWith("is") && name.length() > 2) { + return 2; + } + return 3; + } + + protected int _setterPriority(AnnotatedMethod m) + { + final String name = m.getName(); + if (name.startsWith("set") && name.length() > 3) { + // should we check capitalization? + return 1; + } + return 2; + } + + /* + /********************************************************** + /* Implementations of refinement accessors + /********************************************************** + */ + + @Override + public Class[] findViews() { + return fromMemberAnnotations(new WithMember[]>() { + @Override + public Class[] withMember(AnnotatedMember member) { + return _annotationIntrospector.findViews(member); + } + }); + } + + @Override + public AnnotationIntrospector.ReferenceProperty findReferenceType() { + return fromMemberAnnotations(new WithMember() { + @Override + public AnnotationIntrospector.ReferenceProperty withMember(AnnotatedMember member) { + return _annotationIntrospector.findReferenceType(member); + } + }); + } + + @Override + public boolean isTypeId() { + Boolean b = fromMemberAnnotations(new WithMember() { + @Override + public Boolean withMember(AnnotatedMember member) { + return _annotationIntrospector.isTypeId(member); + } + }); + return (b != null) && b.booleanValue(); + } + + @Override + public PropertyMetadata getMetadata() { + final Boolean b = _findRequired(); + final String desc = _findDescription(); + final Integer idx = _findIndex(); + final String def = _findDefaultValue(); + if (b == null && idx == null && def == null) { + return (desc == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL + : PropertyMetadata.STD_REQUIRED_OR_OPTIONAL.withDescription(desc); + } + return PropertyMetadata.construct(b.booleanValue(), desc, idx, def); + } + + protected Boolean _findRequired() { + Boolean b = fromMemberAnnotations(new WithMember() { + @Override + public Boolean withMember(AnnotatedMember member) { + return _annotationIntrospector.hasRequiredMarker(member); + } + }); + return b; + } + + protected String _findDescription() { + return fromMemberAnnotations(new WithMember() { + @Override + public String withMember(AnnotatedMember member) { + return _annotationIntrospector.findPropertyDescription(member); + } + }); + } + + protected Integer _findIndex() { + return fromMemberAnnotations(new WithMember() { + @Override + public Integer withMember(AnnotatedMember member) { + return _annotationIntrospector.findPropertyIndex(member); + } + }); + } + + protected String _findDefaultValue() { + return fromMemberAnnotations(new WithMember() { + @Override + public String withMember(AnnotatedMember member) { + return _annotationIntrospector.findPropertyDefaultValue(member); + } + }); + } + + @Override + public ObjectIdInfo findObjectIdInfo() { + return fromMemberAnnotations(new WithMember() { + @Override + public ObjectIdInfo withMember(AnnotatedMember member) { + ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(member); + if (info != null) { + info = _annotationIntrospector.findObjectReferenceInfo(member, info); + } + return info; + } + }); + } + + @Override + public JsonInclude.Value findInclusion() { + if (_annotationIntrospector != null) { + AnnotatedMember a = getAccessor(); + JsonInclude.Value v = _annotationIntrospector.findPropertyInclusion(a); + if (v != null) { + return v; + } + } + return JsonInclude.Value.empty(); + } + + public JsonProperty.Access findAccess() { + return fromMemberAnnotationsExcept(new WithMember() { + @Override + public JsonProperty.Access withMember(AnnotatedMember member) { + return _annotationIntrospector.findPropertyAccess(member); + } + }, JsonProperty.Access.AUTO); + } + + /* + /********************************************************** + /* Data aggregation + /********************************************************** + */ + + public void addField(AnnotatedField a, PropertyName name, boolean explName, boolean visible, boolean ignored) { + _fields = new Linked(a, _fields, name, explName, visible, ignored); + } + + public void addCtor(AnnotatedParameter a, PropertyName name, boolean explName, boolean visible, boolean ignored) { + _ctorParameters = new Linked(a, _ctorParameters, name, explName, visible, ignored); + } + + public void addGetter(AnnotatedMethod a, PropertyName name, boolean explName, boolean visible, boolean ignored) { + _getters = new Linked(a, _getters, name, explName, visible, ignored); + } + + public void addSetter(AnnotatedMethod a, PropertyName name, boolean explName, boolean visible, boolean ignored) { + _setters = new Linked(a, _setters, name, explName, visible, ignored); + } + + /** + * Method for adding all property members from specified collector into + * this collector. + */ + public void addAll(POJOPropertyBuilder src) + { + _fields = merge(_fields, src._fields); + _ctorParameters = merge(_ctorParameters, src._ctorParameters); + _getters= merge(_getters, src._getters); + _setters = merge(_setters, src._setters); + } + + private static Linked merge(Linked chain1, Linked chain2) + { + if (chain1 == null) { + return chain2; + } + if (chain2 == null) { + return chain1; + } + return chain1.append(chain2); + } + + /* + /********************************************************** + /* Modifications + /********************************************************** + */ + + /** + * Method called to remove all entries that are marked as + * ignored. + */ + public void removeIgnored() + { + _fields = _removeIgnored(_fields); + _getters = _removeIgnored(_getters); + _setters = _removeIgnored(_setters); + _ctorParameters = _removeIgnored(_ctorParameters); + } + + /** + * @param inferMutators Whether mutators can be "pulled in" by visible + * accessors or not. + */ + public void removeNonVisible(boolean inferMutators) + { + /* 07-Jun-2015, tatu: With 2.6, we will allow optional definition + * of explicit access type for property; if not "AUTO", it will + * dictate how visibility checks are applied. + */ + JsonProperty.Access acc = findAccess(); + if (acc == null) { + acc = JsonProperty.Access.AUTO; + } + switch (acc) { + case READ_ONLY: + // Remove setters, creators for sure, but fields too if deserializing + _setters = null; + _ctorParameters = null; + if (!_forSerialization) { + _fields = null; + } + break; + case READ_WRITE: + // no trimming whatsoever? + break; + case WRITE_ONLY: + // remove getters, definitely, but also fields if serializing + _getters = null; + if (_forSerialization) { + _fields = null; + } + break; + default: + case AUTO: // the default case: base it imply on visibility + _getters = _removeNonVisible(_getters); + _ctorParameters = _removeNonVisible(_ctorParameters); + + if (!inferMutators || (_getters == null)) { + _fields = _removeNonVisible(_fields); + _setters = _removeNonVisible(_setters); + } + } + } + + /** + * Mutator that will simply drop any constructor parameters property may have. + * + * @since 2.5 + */ + public void removeConstructors() { + _ctorParameters = null; + } + + /** + * Method called to trim unnecessary entries, such as implicit + * getter if there is an explict one available. This is important + * for later stages, to avoid unnecessary conflicts. + */ + public void trimByVisibility() + { + _fields = _trimByVisibility(_fields); + _getters = _trimByVisibility(_getters); + _setters = _trimByVisibility(_setters); + _ctorParameters = _trimByVisibility(_ctorParameters); + } + + @SuppressWarnings("unchecked") + public void mergeAnnotations(boolean forSerialization) + { + if (forSerialization) { + if (_getters != null) { + AnnotationMap ann = _mergeAnnotations(0, _getters, _fields, _ctorParameters, _setters); + _getters = _applyAnnotations(_getters, ann); + } else if (_fields != null) { + AnnotationMap ann = _mergeAnnotations(0, _fields, _ctorParameters, _setters); + _fields = _applyAnnotations(_fields, ann); + } + } else { // for deserialization + if (_ctorParameters != null) { + AnnotationMap ann = _mergeAnnotations(0, _ctorParameters, _setters, _fields, _getters); + _ctorParameters = _applyAnnotations(_ctorParameters, ann); + } else if (_setters != null) { + AnnotationMap ann = _mergeAnnotations(0, _setters, _fields, _getters); + _setters = _applyAnnotations(_setters, ann); + } else if (_fields != null) { + AnnotationMap ann = _mergeAnnotations(0, _fields, _getters); + _fields = _applyAnnotations(_fields, ann); + } + } + } + + private AnnotationMap _mergeAnnotations(int index, + Linked... nodes) + { + AnnotationMap ann = _getAllAnnotations(nodes[index]); + while (++index < nodes.length) { + if (nodes[index] != null) { + return AnnotationMap.merge(ann, _mergeAnnotations(index, nodes)); + } + } + return ann; + } + + /** + * Replacement, as per [databind#868], of simple access to annotations, which + * does "deep merge" if an as necessary. + *

+     * nodes[index].value.getAllAnnotations()
+     *
+ * + * @since 2.6 + */ + private AnnotationMap _getAllAnnotations(Linked node) { + AnnotationMap ann = node.value.getAllAnnotations(); + if (node.next != null) { + ann = AnnotationMap.merge(ann, _getAllAnnotations(node.next)); + } + return ann; + } + + /** + * Helper method to handle recursive merging of annotations within accessor class, + * to ensure no annotations are accidentally dropped within chain when non-visible + * and secondary accessors are pruned later on. + *

+ * See [databind#868] for more information. + * + * @since 2.6 + */ + private Linked _applyAnnotations(Linked node, AnnotationMap ann) { + @SuppressWarnings("unchecked") + T value = (T) node.value.withAnnotations(ann); + if (node.next != null) { + node = node.withNext(_applyAnnotations(node.next, ann)); + } + return node.withValue(value); + } + + private Linked _removeIgnored(Linked node) + { + if (node == null) { + return node; + } + return node.withoutIgnored(); + } + + private Linked _removeNonVisible(Linked node) + { + if (node == null) { + return node; + } + return node.withoutNonVisible(); + } + + private Linked _trimByVisibility(Linked node) + { + if (node == null) { + return node; + } + return node.trimByVisibility(); + } + + /* + /********************************************************** + /* Accessors for aggregate information + /********************************************************** + */ + + private boolean _anyExplicits(Linked n) + { + for (; n != null; n = n.next) { + if (n.name != null && n.name.hasSimpleName()) { + return true; + } + } + return false; + } + + private boolean _anyExplicitNames(Linked n) + { + for (; n != null; n = n.next) { + if (n.name != null && n.isNameExplicit) { + return true; + } + } + return false; + } + + public boolean anyVisible() { + return _anyVisible(_fields) + || _anyVisible(_getters) + || _anyVisible(_setters) + || _anyVisible(_ctorParameters) + ; + } + + private boolean _anyVisible(Linked n) + { + for (; n != null; n = n.next) { + if (n.isVisible) { + return true; + } + } + return false; + } + + public boolean anyIgnorals() { + return _anyIgnorals(_fields) + || _anyIgnorals(_getters) + || _anyIgnorals(_setters) + || _anyIgnorals(_ctorParameters) + ; + } + + private boolean _anyIgnorals(Linked n) + { + for (; n != null; n = n.next) { + if (n.isMarkedIgnored) { + return true; + } + } + return false; + } + + /** + * Method called to find out set of explicit names for accessors + * bound together due to implicit name. + * + * @since 2.4 + */ + public Set findExplicitNames() + { + Set renamed = null; + renamed = _findExplicitNames(_fields, renamed); + renamed = _findExplicitNames(_getters, renamed); + renamed = _findExplicitNames(_setters, renamed); + renamed = _findExplicitNames(_ctorParameters, renamed); + if (renamed == null) { + return Collections.emptySet(); + } + return renamed; + } + + /** + * Method called when a previous call to {@link #findExplicitNames} found + * multiple distinct explicit names, and the property this builder represents + * basically needs to be broken apart and replaced by a set of more than + * one properties. + * + * @since 2.4 + */ + public Collection explode(Collection newNames) + { + HashMap props = new HashMap(); + _explode(newNames, props, _fields); + _explode(newNames, props, _getters); + _explode(newNames, props, _setters); + _explode(newNames, props, _ctorParameters); + return props.values(); + } + + @SuppressWarnings("unchecked") + private void _explode(Collection newNames, + Map props, + Linked accessors) + { + final Linked firstAcc = accessors; // clumsy, part 1 + for (Linked node = accessors; node != null; node = node.next) { + PropertyName name = node.name; + if (!node.isNameExplicit || name == null) { // no explicit name -- problem! + // [databind#541] ... but only as long as it's visible + if (!node.isVisible) { + continue; + } + + throw new IllegalStateException("Conflicting/ambiguous property name definitions (implicit name '" + +_name+"'): found multiple explicit names: " + +newNames+", but also implicit accessor: "+node); + } + POJOPropertyBuilder prop = props.get(name); + if (prop == null) { + prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization, + _internalName, name); + props.put(name, prop); + } + // ultra-clumsy, part 2 -- lambdas would be nice here + if (firstAcc == _fields) { + Linked n2 = (Linked) node; + prop._fields = n2.withNext(prop._fields); + } else if (firstAcc == _getters) { + Linked n2 = (Linked) node; + prop._getters = n2.withNext(prop._getters); + } else if (firstAcc == _setters) { + Linked n2 = (Linked) node; + prop._setters = n2.withNext(prop._setters); + } else if (firstAcc == _ctorParameters) { + Linked n2 = (Linked) node; + prop._ctorParameters = n2.withNext(prop._ctorParameters); + } else { + throw new IllegalStateException("Internal error: mismatched accessors, property: "+this); + } + } + } + + private Set _findExplicitNames(Linked node, + Set renamed) + { + for (; node != null; node = node.next) { + /* 30-Mar-2014, tatu: Second check should not be needed, but seems like + * removing it can cause nasty exceptions with certain version + * combinations (2.4 databind, an older module). + * So leaving it in for now until this is resolved + * (or version beyond 2.4) + */ + if (!node.isNameExplicit || node.name == null) { + continue; + } + if (renamed == null) { + renamed = new HashSet(); + } + renamed.add(node.name); + } + return renamed; + } + + // For trouble-shooting + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("[Property '").append(_name) + .append("'; ctors: ").append(_ctorParameters) + .append(", field(s): ").append(_fields) + .append(", getter(s): ").append(_getters) + .append(", setter(s): ").append(_setters) + ; + sb.append("]"); + return sb.toString(); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + /** + * Helper method used for finding annotation values, from accessors + * relevant to current usage (deserialization, serialization) + */ + protected T fromMemberAnnotations(WithMember func) + { + T result = null; + if (_annotationIntrospector != null) { + if (_forSerialization) { + if (_getters != null) { + result = func.withMember(_getters.value); + } + } else { + if (_ctorParameters != null) { + result = func.withMember(_ctorParameters.value); + } + if (result == null && _setters != null) { + result = func.withMember(_setters.value); + } + } + if (result == null && _fields != null) { + result = func.withMember(_fields.value); + } + } + return result; + } + + protected T fromMemberAnnotationsExcept(WithMember func, T defaultValue) + { + if (_annotationIntrospector == null) { + return null; + } + + // NOTE: here we must ask ALL accessors, but the order varies between + // serialization, deserialization + if (_forSerialization) { + if (_getters != null) { + T result = func.withMember(_getters.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + if (_fields != null) { + T result = func.withMember(_fields.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + if (_ctorParameters != null) { + T result = func.withMember(_ctorParameters.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + if (_setters != null) { + T result = func.withMember(_setters.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + return null; + } + if (_ctorParameters != null) { + T result = func.withMember(_ctorParameters.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + if (_setters != null) { + T result = func.withMember(_setters.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + if (_fields != null) { + T result = func.withMember(_fields.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + if (_getters != null) { + T result = func.withMember(_getters.value); + if ((result != null) && (result != defaultValue)) { + return result; + } + } + return null; + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + private interface WithMember { + public T withMember(AnnotatedMember member); + } + + /** + * @since 2.5 + */ + protected static class MemberIterator + implements Iterator + { + private Linked next; + + public MemberIterator(Linked first) { + next = first; + } + + @Override + public boolean hasNext() { + return (next != null); + } + + @Override + public T next() { + if (next == null) throw new NoSuchElementException(); + T result = next.value; + next = next.next; + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + /** + * Node used for creating simple linked lists to efficiently store small sets + * of things. + */ + protected final static class Linked + { + public final T value; + public final Linked next; + + public final PropertyName name; + public final boolean isNameExplicit; + public final boolean isVisible; + public final boolean isMarkedIgnored; + + public Linked(T v, Linked n, + PropertyName name, boolean explName, boolean visible, boolean ignored) + { + value = v; + next = n; + // ensure that we'll never have missing names + this.name = (name == null || name.isEmpty()) ? null : name; + + if (explName) { + if (this.name == null) { // sanity check to catch internal problems + throw new IllegalArgumentException("Can not pass true for 'explName' if name is null/empty"); + } + // 03-Apr-2014, tatu: But how about name-space only override? + // Probably should not be explicit? Or, need to merge somehow? + if (!name.hasSimpleName()) { + explName = false; + } + } + + isNameExplicit = explName; + isVisible = visible; + isMarkedIgnored = ignored; + } + + public Linked withoutNext() { + if (next == null) { + return this; + } + return new Linked(value, null, name, isNameExplicit, isVisible, isMarkedIgnored); + } + + public Linked withValue(T newValue) { + if (newValue == value) { + return this; + } + return new Linked(newValue, next, name, isNameExplicit, isVisible, isMarkedIgnored); + } + + public Linked withNext(Linked newNext) { + if (newNext == next) { + return this; + } + return new Linked(value, newNext, name, isNameExplicit, isVisible, isMarkedIgnored); + } + + public Linked withoutIgnored() { + if (isMarkedIgnored) { + return (next == null) ? null : next.withoutIgnored(); + } + if (next != null) { + Linked newNext = next.withoutIgnored(); + if (newNext != next) { + return withNext(newNext); + } + } + return this; + } + + public Linked withoutNonVisible() { + Linked newNext = (next == null) ? null : next.withoutNonVisible(); + return isVisible ? withNext(newNext) : newNext; + } + + /** + * Method called to append given node(s) at the end of this + * node chain. + */ + protected Linked append(Linked appendable) { + if (next == null) { + return withNext(appendable); + } + return withNext(next.append(appendable)); + } + + public Linked trimByVisibility() { + if (next == null) { + return this; + } + Linked newNext = next.trimByVisibility(); + if (name != null) { // this already has highest; how about next one? + if (newNext.name == null) { // next one not, drop it + return withNext(null); + } + // both have it, keep + return withNext(newNext); + } + if (newNext.name != null) { // next one has higher, return it... + return newNext; + } + // neither has explicit name; how about visibility? + if (isVisible == newNext.isVisible) { // same; keep both in current order + return withNext(newNext); + } + return isVisible ? withNext(null) : newNext; + } + + @Override + public String toString() { + String msg = value.toString()+"[visible="+isVisible+",ignore="+isMarkedIgnored + +",explicitName="+isNameExplicit+"]"; + if (next != null) { + msg = msg + ", "+next.toString(); + } + return msg; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,102 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * Simple implementation of {@link ClassIntrospector.MixInResolver} + * that just uses a {@link java.util.Map} for containing mapping + * from target to mix-in classes. + *

+ * Implementation is only thread-safe after initialization (that is, + * when underlying Map is not modified but only read). + * + * @since 2.6 + */ +public class SimpleMixInResolver + implements ClassIntrospector.MixInResolver, + java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * External resolver that gets called before looking at any locally defined + * mix-in target classes. + */ + protected final ClassIntrospector.MixInResolver _overrides; + + /** + * Simple mix-in targets defined locally. + */ + protected Map> _localMixIns; + + public SimpleMixInResolver(ClassIntrospector.MixInResolver overrides) { + _overrides = overrides; + } + + protected SimpleMixInResolver(ClassIntrospector.MixInResolver overrides, + Map> mixins) { + _overrides = overrides; + _localMixIns = mixins; + } + + /** + * Mutant factory for constructor a new resolver instance with given + * mix-in resolver override. + */ + public SimpleMixInResolver withOverrides(ClassIntrospector.MixInResolver overrides) { + return new SimpleMixInResolver(overrides, _localMixIns); + } + + /** + * Mutant factory method that constructs a new instance that has no locally + * defined mix-in/target mappings. + */ + public SimpleMixInResolver withoutLocalDefinitions() { + return new SimpleMixInResolver(_overrides, null); + } + + public void setLocalDefinitions(Map, Class> sourceMixins) { + if (sourceMixins == null || sourceMixins.isEmpty()) { + _localMixIns = null; + } else { + Map> mixIns = new HashMap>(sourceMixins.size()); + for (Map.Entry,Class> en : sourceMixins.entrySet()) { + mixIns.put(new ClassKey(en.getKey()), en.getValue()); + } + _localMixIns = mixIns; + } + } + + public void addLocalDefinition(Class target, Class mixinSource) { + if (_localMixIns == null) { + _localMixIns = new HashMap>(); + } + _localMixIns.put(new ClassKey(target), mixinSource); + } + + @Override + public SimpleMixInResolver copy() { + ClassIntrospector.MixInResolver overrides = (_overrides == null) + ? null : _overrides.copy(); + Map> mixIns = (_localMixIns == null) + ? null : new HashMap>(_localMixIns); + return new SimpleMixInResolver(overrides, mixIns); + } + + @Override + public Class findMixInClassFor(Class cls) + { + Class mixin = (_overrides == null) ? null : _overrides.findMixInClassFor(cls); + if (mixin == null && (_localMixIns != null)) { + mixin = _localMixIns.get(new ClassKey(cls)); + } + return mixin; + } + + public int localSize() { + return (_localMixIns == null) ? 0 : _localMixIns.size(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/TypeResolutionContext.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/TypeResolutionContext.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/TypeResolutionContext.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,34 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.Type; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeBindings; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Interface that defines API used by members (like {@link AnnotatedMethod}) + * to dynamically resolve types they have. + * + * @since 2.7 + */ +public interface TypeResolutionContext { + public JavaType resolveType(Type t); + + public static class Basic + implements TypeResolutionContext + { + private final TypeFactory _typeFactory; + private final TypeBindings _bindings; + + public Basic(TypeFactory tf, TypeBindings b) { + _typeFactory = tf; + _bindings = b; + } + + @Override + public JavaType resolveType(Type type) { + return _typeFactory.constructType(type, _bindings); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/VirtualAnnotatedMember.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,121 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.*; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Placeholder used by virtual properties as placeholder for + * underlying {@link AnnotatedMember}. + * + * @since 2.5 + */ +public class VirtualAnnotatedMember extends AnnotatedMember + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final Class _declaringClass; + + protected final Class _rawType; + + protected final String _name; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public VirtualAnnotatedMember(TypeResolutionContext typeContext, Class declaringClass, + String name, Class rawType) + { + super(typeContext, /* AnnotationMap*/ null); + _declaringClass = declaringClass; + _rawType = rawType; + _name = name; + } + + @Override + public Annotated withAnnotations(AnnotationMap fallback) { + return this; + } + + /* + /********************************************************** + /* Annotated impl + /********************************************************** + */ + + @Override + public Field getAnnotated() { return null; } + + @Override + public int getModifiers() { return 0; } + + @Override + public String getName() { return _name; } + + @Override + public Class getRawType() { + return _rawType; + } + + @Override + public JavaType getType() { + return _typeContext.resolveType(_rawType); + } + + /* + /********************************************************** + /* AnnotatedMember impl + /********************************************************** + */ + + @Override + public Class getDeclaringClass() { return _declaringClass; } + + @Override + public Member getMember() { return null; } + + @Override + public void setValue(Object pojo, Object value) throws IllegalArgumentException { + throw new IllegalArgumentException("Can not set virtual property '"+_name+"'"); + } + + @Override + public Object getValue(Object pojo) throws IllegalArgumentException { + throw new IllegalArgumentException("Can not get virtual property '"+_name+"'"); + } + + /* + /********************************************************** + /* Extended API, generic + /********************************************************** + */ + + public String getFullName() { + return getDeclaringClass().getName() + "#" + getName(); + } + + public int getAnnotationCount() { return 0; } + + @Override + public int hashCode() { + return _name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + VirtualAnnotatedMember other = (VirtualAnnotatedMember) o; + return (other._declaringClass == _declaringClass) + && other._name.equals(_name); + } + + @Override + public String toString() { + return "[field "+getFullName()+"]"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,393 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; + +/** + * Interface for object used for determine which property elements + * (methods, fields, constructors) can be auto-detected, with respect + * to their visibility modifiers. + *

+ * Note on type declaration: funky recursive type is necessary to + * support builder/fluent pattern. + */ +public interface VisibilityChecker> +{ + // // Builder methods + + /** + * Builder method that will return an instance that has same + * settings as this instance has, except for values that + * given annotation overrides. + */ + public T with(JsonAutoDetect ann); + + /** + * Builder method that will create and return an instance that has specified + * {@link Visibility} value to use for all property elements. + * Typical usage would be something like: + *

+     *  mapper.setVisibilityChecker(
+     *     mapper.getVisibilityChecker().with(Visibility.NONE));
+     *
+ * (which would basically disable all auto-detection) + */ + public T with(Visibility v); + + /** + * Builder method that will create and return an instance that has specified + * {@link Visibility} value to use for specified property. + * Typical usage would be: + *
+     *  mapper.setVisibilityChecker(
+     *     mapper.getVisibilityChecker().withVisibility(JsonMethod.FIELD, Visibility.ANY));
+     *
+ * (which would basically enable auto-detection for all member fields) + */ + public T withVisibility(PropertyAccessor method, Visibility v); + + /** + * Builder method that will return a checker instance that has + * specified minimum visibility level for regular ("getXxx") getters. + */ + public T withGetterVisibility(Visibility v); + + /** + * Builder method that will return a checker instance that has + * specified minimum visibility level for "is-getters" ("isXxx"). + */ + public T withIsGetterVisibility(Visibility v); + + /** + * Builder method that will return a checker instance that has + * specified minimum visibility level for setters. + */ + public T withSetterVisibility(Visibility v); + + /** + * Builder method that will return a checker instance that has + * specified minimum visibility level for creator methods + * (constructors, factory methods) + */ + public T withCreatorVisibility(Visibility v); + + /** + * Builder method that will return a checker instance that has + * specified minimum visibility level for fields. + */ + public T withFieldVisibility(Visibility v); + + // // Accessors + + /** + * Method for checking whether given method is auto-detectable + * as regular getter, with respect to its visibility (not considering + * method signature or name, just visibility) + */ + public boolean isGetterVisible(Method m); + public boolean isGetterVisible(AnnotatedMethod m); + + /** + * Method for checking whether given method is auto-detectable + * as is-getter, with respect to its visibility (not considering + * method signature or name, just visibility) + */ + public boolean isIsGetterVisible(Method m); + public boolean isIsGetterVisible(AnnotatedMethod m); + + /** + * Method for checking whether given method is auto-detectable + * as setter, with respect to its visibility (not considering + * method signature or name, just visibility) + */ + public boolean isSetterVisible(Method m); + public boolean isSetterVisible(AnnotatedMethod m); + + /** + * Method for checking whether given method is auto-detectable + * as Creator, with respect to its visibility (not considering + * method signature or name, just visibility) + */ + public boolean isCreatorVisible(Member m); + public boolean isCreatorVisible(AnnotatedMember m); + + /** + * Method for checking whether given field is auto-detectable + * as property, with respect to its visibility (not considering + * method signature or name, just visibility) + */ + public boolean isFieldVisible(Field f); + public boolean isFieldVisible(AnnotatedField f); + + /* + /******************************************************** + /* Standard implementation suitable for basic use + /******************************************************** + */ + + /** + * Default standard implementation is purely based on visibility + * modifier of given class members, and its configured minimum + * levels. + * Implemented using "builder" (or "Fluent") pattern, whereas instances + * are immutable, and configuration is achieved by chainable factory + * methods. As a result, type is declared is funky recursive generic + * type, to allow for sub-classing of build methods with property type + * co-variance. + *

+ * Note on JsonAutoDetect annotation: it is used to + * access default minimum visibility access definitions. + */ + @JsonAutoDetect( + getterVisibility = Visibility.PUBLIC_ONLY, + isGetterVisibility = Visibility.PUBLIC_ONLY, + setterVisibility = Visibility.ANY, + /** + * By default, all matching single-arg constructed are found, + * regardless of visibility. Does not apply to factory methods, + * they can not be auto-detected; ditto for multiple-argument + * constructors. + */ + creatorVisibility = Visibility.ANY, + fieldVisibility = Visibility.PUBLIC_ONLY + ) + public static class Std + implements VisibilityChecker, + java.io.Serializable + { + private static final long serialVersionUID = 1; + + /** + * This is the canonical base instance, configured with default + * visibility values + */ + protected final static Std DEFAULT = new Std(Std.class.getAnnotation(JsonAutoDetect.class)); + + protected final Visibility _getterMinLevel; + protected final Visibility _isGetterMinLevel; + protected final Visibility _setterMinLevel; + protected final Visibility _creatorMinLevel; + protected final Visibility _fieldMinLevel; + + public static Std defaultInstance() { return DEFAULT; } + + /** + * Constructor used for building instance that has minumum visibility + * levels as indicated by given annotation instance + * + * @param ann Annotations to use for determining minimum visibility levels + */ + public Std(JsonAutoDetect ann) + { + // let's combine checks for enabled/disabled, with minimimum level checks: + _getterMinLevel = ann.getterVisibility(); + _isGetterMinLevel = ann.isGetterVisibility(); + _setterMinLevel = ann.setterVisibility(); + _creatorMinLevel = ann.creatorVisibility(); + _fieldMinLevel = ann.fieldVisibility(); + } + + /** + * Constructor that allows directly specifying minimum visibility levels to use + */ + public Std(Visibility getter, Visibility isGetter, Visibility setter, Visibility creator, Visibility field) + { + _getterMinLevel = getter; + _isGetterMinLevel = isGetter; + _setterMinLevel = setter; + _creatorMinLevel = creator; + _fieldMinLevel = field; + } + + /** + * Constructor that will assign given visibility value for all + * properties. + * + * @param v level to use for all property types + */ + public Std(Visibility v) + { + // typically we shouldn't get this value; but let's handle it if we do: + if (v == Visibility.DEFAULT) { + _getterMinLevel = DEFAULT._getterMinLevel; + _isGetterMinLevel = DEFAULT._isGetterMinLevel; + _setterMinLevel = DEFAULT._setterMinLevel; + _creatorMinLevel = DEFAULT._creatorMinLevel; + _fieldMinLevel = DEFAULT._fieldMinLevel; + } else { + _getterMinLevel = v; + _isGetterMinLevel = v; + _setterMinLevel = v; + _creatorMinLevel = v; + _fieldMinLevel = v; + } + } + + /* + /******************************************************** + /* Builder/fluent methods for instantiating configured + /* instances + /******************************************************** + */ + + @Override + public Std with(JsonAutoDetect ann) + { + Std curr = this; + if (ann != null) { + curr = curr.withGetterVisibility(ann.getterVisibility()); + curr = curr.withIsGetterVisibility(ann.isGetterVisibility()); + curr = curr.withSetterVisibility(ann.setterVisibility()); + curr = curr.withCreatorVisibility(ann.creatorVisibility()); + curr = curr.withFieldVisibility(ann.fieldVisibility()); + } + return curr; + } + + @Override + public Std with(Visibility v) + { + if (v == Visibility.DEFAULT) { + return DEFAULT; + } + return new Std(v); + } + + @Override + public Std withVisibility(PropertyAccessor method, Visibility v) + { + switch (method) { + case GETTER: + return withGetterVisibility(v); + case SETTER: + return withSetterVisibility(v); + case CREATOR: + return withCreatorVisibility(v); + case FIELD: + return withFieldVisibility(v); + case IS_GETTER: + return withIsGetterVisibility(v); + case ALL: + return with(v); + //case NONE: + default: + // break; + return this; + } + } + + @Override + public Std withGetterVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._getterMinLevel; + if (_getterMinLevel == v) return this; + return new Std(v, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, _fieldMinLevel); + } + + @Override + public Std withIsGetterVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._isGetterMinLevel; + if (_isGetterMinLevel == v) return this; + return new Std(_getterMinLevel, v, _setterMinLevel, _creatorMinLevel, _fieldMinLevel); + } + + @Override + public Std withSetterVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._setterMinLevel; + if (_setterMinLevel == v) return this; + return new Std(_getterMinLevel, _isGetterMinLevel, v, _creatorMinLevel, _fieldMinLevel); + } + + @Override + public Std withCreatorVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._creatorMinLevel; + if (_creatorMinLevel == v) return this; + return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, v, _fieldMinLevel); + } + + @Override + public Std withFieldVisibility(Visibility v) { + if (v == Visibility.DEFAULT) v = DEFAULT._fieldMinLevel; + if (_fieldMinLevel == v) return this; + return new Std(_getterMinLevel, _isGetterMinLevel, _setterMinLevel, _creatorMinLevel, v); + } + + /* + /******************************************************** + /* Public API impl + /******************************************************** + */ + + @Override + public boolean isCreatorVisible(Member m) { + return _creatorMinLevel.isVisible(m); + } + + @Override + public boolean isCreatorVisible(AnnotatedMember m) { + return isCreatorVisible(m.getMember()); + } + + @Override + public boolean isFieldVisible(Field f) { + return _fieldMinLevel.isVisible(f); + } + + @Override + public boolean isFieldVisible(AnnotatedField f) { + return isFieldVisible(f.getAnnotated()); + } + + @Override + public boolean isGetterVisible(Method m) { + return _getterMinLevel.isVisible(m); + } + + @Override + public boolean isGetterVisible(AnnotatedMethod m) { + return isGetterVisible(m.getAnnotated()); + } + + @Override + public boolean isIsGetterVisible(Method m) { + return _isGetterMinLevel.isVisible(m); + } + + @Override + public boolean isIsGetterVisible(AnnotatedMethod m) { + return isIsGetterVisible(m.getAnnotated()); + } + + @Override + public boolean isSetterVisible(Method m) { + return _setterMinLevel.isVisible(m); + } + + @Override + public boolean isSetterVisible(AnnotatedMethod m) { + return isSetterVisible(m.getAnnotated()); + } + + /* + /******************************************************** + /* Standard methods + /******************************************************** + */ + + @Override + public String toString() { + return new StringBuilder("[Visibility:") + .append(" getter: ").append(_getterMinLevel) + .append(", isGetter: ").append(_isGetterMinLevel) + .append(", setter: ").append(_setterMinLevel) + .append(", creator: ").append(_creatorMinLevel) + .append(", field: ").append(_fieldMinLevel) + .append("]").toString(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/WithMember.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/WithMember.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/WithMember.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,6 @@ +package com.fasterxml.jackson.databind.introspect; + +public interface WithMember +{ + public T withMember(AnnotatedMember member); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/introspect/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,12 @@ +/** + * Functionality needed for Bean introspection, required for detecting + * accessors and mutators for Beans, as well as locating and handling + * method annotations. + *

+ * Beyond collecting annotations, additional "method annotation inheritance" + * is also supported: whereas regular JDK classes do not add annotations + * from overridden methods in any situation. But code in this package does. + * Similarly class-annotations are inherited properly from interfaces, in + * addition to abstract and concrete classes. + */ +package com.fasterxml.jackson.databind.introspect; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,11 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonAnyFormatVisitor +{ + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base implements JsonAnyFormatVisitor { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,55 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; + +public interface JsonArrayFormatVisitor extends JsonFormatVisitorWithSerializerProvider +{ + /** + * Visit method called for structured types, as well as possibly + * for leaf types (especially if handled by custom serializers). + * + * @param handler Serializer used, to allow for further callbacks + * @param elementType Type of elements in JSON array value + */ + void itemsFormat(JsonFormatVisitable handler, JavaType elementType) + throws JsonMappingException; + + /** + * Visit method that is called if the content type is a simple + * scalar type like {@link JsonFormatTypes#STRING} (but not + * for structured types like {@link JsonFormatTypes#OBJECT} since + * they would be missing type information). + */ + void itemsFormat(JsonFormatTypes format) + throws JsonMappingException; + + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base implements JsonArrayFormatVisitor { + protected SerializerProvider _provider; + + public Base() { } + public Base(SerializerProvider p) { _provider = p; } + + @Override + public SerializerProvider getProvider() { return _provider; } + + @Override + public void setProvider(SerializerProvider p) { _provider = p; } + + @Override + public void itemsFormat(JsonFormatVisitable handler, JavaType elementType) + throws JsonMappingException { } + + @Override + public void itemsFormat(JsonFormatTypes format) + throws JsonMappingException { } + } + +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,12 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonBooleanFormatVisitor extends JsonValueFormatVisitor +{ + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base extends JsonValueFormatVisitor.Base + implements JsonBooleanFormatVisitor { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatTypes.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatTypes.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatTypes.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum JsonFormatTypes +{ + STRING, + NUMBER, + INTEGER, + BOOLEAN, + OBJECT, + ARRAY, + NULL, + ANY; + + private static final Map _byLCName = new HashMap(); + static { + for (JsonFormatTypes t : values()) { + _byLCName.put(t.name().toLowerCase(), t); + } + } + + @JsonValue + public String value() { + return name().toLowerCase(); + } + + @JsonCreator + public static JsonFormatTypes forValue(String s) { + return _byLCName.get(s); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitable.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitable.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitable.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,19 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * Interface {@link com.fasterxml.jackson.databind.JsonSerializer} implements + * to allow for visiting type hierarchy. + */ +public interface JsonFormatVisitable +{ + /** + * Get the representation of the schema to which this serializer will conform. + * + * @param typeHint Type of element (entity like property) being visited + */ + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,14 @@ +/** + * + */ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * @author jphelan + */ +public interface JsonFormatVisitorWithSerializerProvider { + public SerializerProvider getProvider(); + public abstract void setProvider(SerializerProvider provider); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,145 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * Interface for visitor callbacks, when type in question can be any of + * legal JSON types. + *

+ * In most cases it will make more sense to extend {@link JsonFormatVisitorWrapper.Base} + * instead of directly implementing this interface. + */ +public interface JsonFormatVisitorWrapper extends JsonFormatVisitorWithSerializerProvider +{ + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonObjectFormatVisitor expectObjectFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonArrayFormatVisitor expectArrayFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonStringFormatVisitor expectStringFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonNumberFormatVisitor expectNumberFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonNullFormatVisitor expectNullFormat(JavaType type) throws JsonMappingException; + + /** + * @param type Declared type of visited property (or List element) in Java + */ + public JsonAnyFormatVisitor expectAnyFormat(JavaType type) throws JsonMappingException; + + /** + * Method called when type is of Java {@link java.util.Map} type, and will + * be serialized as a JSON Object. + * + * @since 2.2 + */ + public JsonMapFormatVisitor expectMapFormat(JavaType type) throws JsonMappingException; + + /** + * Empty "no-op" implementation of {@link JsonFormatVisitorWrapper}, suitable for + * sub-classing. Does implement {@link #setProvider(SerializerProvider)} and + * {@link #getProvider()} as expected; other methods simply return null + * and do nothing. + * + * @since 2.5 + */ + public static class Base implements JsonFormatVisitorWrapper { + protected SerializerProvider _provider; + + public Base() { } + + public Base(SerializerProvider p) { + _provider = p; + } + + @Override + public SerializerProvider getProvider() { + return _provider; + } + + @Override + public void setProvider(SerializerProvider p) { + _provider = p; + } + + @Override + public JsonObjectFormatVisitor expectObjectFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonArrayFormatVisitor expectArrayFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonStringFormatVisitor expectStringFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonNumberFormatVisitor expectNumberFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonNullFormatVisitor expectNullFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonAnyFormatVisitor expectAnyFormat(JavaType type) + throws JsonMappingException { + return null; + } + + @Override + public JsonMapFormatVisitor expectMapFormat(JavaType type) + throws JsonMappingException { + return null; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,25 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.core.JsonParser; + +public interface JsonIntegerFormatVisitor extends JsonValueFormatVisitor +{ + /** + * Method called to provide more exact type of number being serialized + * (regardless of logical type, which may be {@link java.util.Date} or + * {@link java.lang.Enum}, in addition to actual numeric types like + * {@link java.lang.Integer}). + */ + public void numberType(JsonParser.NumberType type); + + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base extends JsonValueFormatVisitor.Base + implements JsonIntegerFormatVisitor { + @Override + public void numberType(JsonParser.NumberType type) { } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonMapFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonMapFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonMapFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; + +public interface JsonMapFormatVisitor extends JsonFormatVisitorWithSerializerProvider +{ + /** + * Visit method called to indicate type of keys of the Map type + * being visited + */ + public void keyFormat(JsonFormatVisitable handler, JavaType keyType) throws JsonMappingException; + + /** + * Visit method called after {@link #keyFormat} to allow visiting of + * the value type + */ + public void valueFormat(JsonFormatVisitable handler, JavaType valueType) throws JsonMappingException; + + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base + implements JsonMapFormatVisitor + { + protected SerializerProvider _provider; + + public Base() { } + public Base(SerializerProvider p) { _provider = p; } + + @Override + public SerializerProvider getProvider() { return _provider; } + + @Override + public void setProvider(SerializerProvider p) { _provider = p; } + + @Override + public void keyFormat(JsonFormatVisitable handler, JavaType keyType) throws JsonMappingException { } + @Override + public void valueFormat(JsonFormatVisitable handler, JavaType valueType) throws JsonMappingException { } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,10 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonNullFormatVisitor { + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base implements JsonNullFormatVisitor { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,25 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.core.JsonParser; + +public interface JsonNumberFormatVisitor extends JsonValueFormatVisitor +{ + /** + * Method called to provide more exact type of number being serialized + * (regardless of logical type, which may be {@link java.util.Date} or + * {@link java.lang.Enum}, in addition to actual numeric types like + * {@link java.lang.Integer}). + */ + public void numberType(JsonParser.NumberType type); + + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base extends JsonValueFormatVisitor.Base + implements JsonNumberFormatVisitor { + @Override + public void numberType(JsonParser.NumberType type) { } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * Visitor called when properties of a type that maps to JSON Object + * are being visited: this usually means POJOs, but sometimes other + * types use it too (like {@link java.util.EnumMap}). + */ +public interface JsonObjectFormatVisitor extends JsonFormatVisitorWithSerializerProvider +{ + /** + * Callback method called when a POJO property is being traversed. + */ + public void property(BeanProperty writer) throws JsonMappingException; + + /** + * Callback method called when a non-POJO property (typically something + * like an Enum entry of {@link java.util.EnumMap} type) is being + * traversed. With POJOs, {@link #property(BeanProperty)} is called instead. + */ + public void property(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException; + + public void optionalProperty(BeanProperty writer) throws JsonMappingException; + public void optionalProperty(String name, JsonFormatVisitable handler, + JavaType propertyTypeHint) + throws JsonMappingException; + + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base + implements JsonObjectFormatVisitor + { + protected SerializerProvider _provider; + + public Base() { } + public Base(SerializerProvider p) { _provider = p; } + + @Override + public SerializerProvider getProvider() { return _provider; } + + @Override + public void setProvider(SerializerProvider p) { _provider = p; } + + @Override + public void property(BeanProperty prop) throws JsonMappingException { } + + @Override + public void property(String name, JsonFormatVisitable handler, + JavaType propertyTypeHint) throws JsonMappingException { } + + @Override + public void optionalProperty(BeanProperty prop) + throws JsonMappingException { } + + @Override + public void optionalProperty(String name, JsonFormatVisitable handler, + JavaType propertyTypeHint) throws JsonMappingException { } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,13 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +public interface JsonStringFormatVisitor extends JsonValueFormatVisitor +{ + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base extends JsonValueFormatVisitor.Base + implements JsonStringFormatVisitor { } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormat.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormat.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormat.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,99 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * This enum represents the encoded format for a jsonSchema value type + * @author jphelan + * + */ +public enum JsonValueFormat +{ + /** + * This is a CSS color (like "#FF0000" or "red"), based on CSS + 2.1 [W3C.CR-CSS21-20070719]. + */ + COLOR("color"), + + /** + * This SHOULD be a date in the format of YYYY-MM-DD. It is + recommended that you use the "date-time" format instead of "date" + unless you need to transfer only the date part. + */ + DATE("date"), + + /** + * This SHOULD be a date in ISO 8601 format of YYYY-MM- + DDThh:mm:ssZ in UTC time. This is the recommended form of date/ + timestamp. + */ + DATE_TIME("date-time"), + + /** + * This SHOULD be an email address. + */ + EMAIL("email"), + + /** + * This SHOULD be a host-name. + */ + HOST_NAME("host-name"), + + /** + * This SHOULD be an ip version 4 address. + */ + IP_ADDRESS("ip-address"), + + /** + * This SHOULD be an ip version 6 address. + */ + IPV6("ipv6"), + + /** + * This SHOULD be a phone number (format MAY follow E.123). + */ + PHONE("phone"), + + /** + * A regular expression, following the regular expression + * specification from ECMA 262/Perl 5. + */ + REGEX("regex"), + + /** + * This is a CSS style definition (like "color: red; background- + * color:#FFF"), based on CSS 2.1 [W3C.CR-CSS21-20070719]. + */ + STYLE("style"), + + /** + * This SHOULD be a time in the format of hh:mm:ss. It is + * recommended that you use the "date-time" format instead of "time" + * unless you need to transfer only the time part. + */ + TIME("time"), + + /** + * This value SHOULD be a URI.. + */ + URI("uri"), + + /** + * This SHOULD be the difference, measured in + milliseconds, between the specified time and midnight, 00:00 of + January 1, 1970 UTC. The value SHOULD be a number (integer or + float). + */ + UTC_MILLISEC("utc-millisec"), + ; + + private final String _desc; + + private JsonValueFormat(String desc) { + _desc = desc; + } + + @Override + @JsonValue // since 2.7 + public String toString() { return _desc; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind.jsonFormatVisitors; + +import java.util.Set; + +public interface JsonValueFormatVisitor { + /** + * Method called to indicate configured format for value type being visited. + */ + void format(JsonValueFormat format); + + /** + * Method called to indicate enumerated (String) values type being visited + * can take as values. + */ + void enumTypes(Set enums); + + /** + * Default "empty" implementation, useful as the base to start on; + * especially as it is guaranteed to implement all the method + * of the interface, even if new methods are getting added. + */ + public static class Base implements JsonValueFormatVisitor { + @Override + public void format(JsonValueFormat format) { } + @Override + public void enumTypes(Set enums) { } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonFormatVisitors/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,12 @@ +/** + * Classes used for exposing logical structure of POJOs as Jackson + * sees it, and exposed via + * {@link com.fasterxml.jackson.databind.ObjectMapper#acceptJsonFormatVisitor(Class, JsonFormatVisitorWrapper)} + * and + * {@link com.fasterxml.jackson.databind.ObjectMapper#acceptJsonFormatVisitor(com.fasterxml.jackson.databind.JavaType, JsonFormatVisitorWrapper)} + * methods. + *

+ * The main entrypoint for code, then, is {@link com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper} and other + * types are recursively needed during traversal. + */ +package com.fasterxml.jackson.databind.jsonFormatVisitors; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,96 @@ +package com.fasterxml.jackson.databind.jsonschema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Container for a logical JSON Schema instance. + * Internally schema data is stored as a JSON Tree + * (instance of {@link JsonNode} is the root + * of schema document) + * + * @author Ryan Heaton + * @see JSON Schema + * + * @deprecated Since 2.2, we recommend use of external + * JSON Schema generator module + */ +@Deprecated +public class JsonSchema +{ + private final ObjectNode schema; + + /** + * Main constructor for schema instances. + *

+ * This is the creator constructor used by Jackson itself when + * deserializing instances. It is so-called delegating creator, + * meaning that its argument will be bound by Jackson before + * constructor gets called. + */ + @JsonCreator + public JsonSchema(ObjectNode schema) + { + this.schema = schema; + } + + /** + * Method for accessing root JSON object of the contained schema. + *

+ * Note: this method is specified with {@link JsonValue} annotation + * to represent serialization to use; same as if explicitly + * serializing returned object. + * + * @return Root node of the schema tree + */ + @JsonValue + public ObjectNode getSchemaNode() + { + return schema; + } + + @Override + public String toString() + { + return this.schema.toString(); + } + + @Override + public int hashCode() + { + return schema.hashCode(); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (!(o instanceof JsonSchema)) return false; + + JsonSchema other = (JsonSchema) o; + if (schema == null) { + return other.schema == null; + } + return schema.equals(other.schema); + } + + /** + * Get the default schema node. + * + * @return The default schema node. + */ + public static JsonNode getDefaultSchemaNode() + { + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("type", "any"); + // "required" is false by default, no need to include + //objectNode.put("required", false); + return objectNode; + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,70 @@ +package com.fasterxml.jackson.databind.jsonschema; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Retention; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.annotation.JacksonAnnotation; + +/** + * Annotation that can be used to define JSON Schema definition for + * the annotated class. + *

+ * Note that annotation is often not needed: for example, regular + * Jackson beans that Jackson can introspect can be used without + * annotations, to produce JSON schema definition. + * + * @author Ryan Heaton + * @author Tatu Saloranta + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotation +public @interface JsonSerializableSchema +{ + /** + * Marker value used to indicate that property has "no value"; + * needed because annotations can not have null as default + * value. + */ + public final static String NO_VALUE = "##irrelevant"; + + /** + * Property that can be used to indicate id of the type when + * generating JSON Schema; empty String indicates that no id + * is defined. + */ + public String id() default ""; + + /** + * The schema type for this JsonSerializable instance. + * Possible values: "string", "number", "boolean", "object", "array", "null", "any" + * + * @return The schema type for this JsonSerializable instance. + */ + public String schemaType() default "any"; + + /** + * If the schema type is "object", JSON definition of properties of the object as + * a String. + * + * @return The node representing the schema properties, or "##irrelevant" if irrelevant. + * + * @deprecated (since 2.1) -- support will be dropped in future, since JSON-as-String is + * fundamentally bad way for customizing anything. No direct replacements offered. + */ + @Deprecated + public String schemaObjectPropertiesDefinition() default NO_VALUE; + + /** + * If the schema type if "array", JSON definition of the schema for item types contained. + * + * @return The schema for the items in the array, or "##irrelevant" if irrelevant. + * + * @deprecated (since 2.1) -- support will be dropped in future, since JSON-as-String is + * fundamentally bad way for customizing anything. No direct replacements offered. + */ + @Deprecated + public String schemaItemDefinition() default NO_VALUE; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,34 @@ +package com.fasterxml.jackson.databind.jsonschema; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.lang.reflect.Type; + +/** + * Marker interface for schema-aware serializers. + */ +public interface SchemaAware +{ + /** + * Get the representation of the schema to which this serializer will conform. + * + * @param provider The serializer provider. + * @param typeHint A hint about the type. + * @return Json-schema for this serializer. + */ + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException; + + /** + * Get the representation of the schema to which this serializer will conform. + * + * @param provider The serializer provider. + * @param isOptional Is the type optional + * @param typeHint A hint about the type. + * @return Json-schema for this serializer. + */ + public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsonschema/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,5 @@ +/** + * Classes needed for JSON schema support (currently just ability + * to generate schemas using serialization part of data mapping) + */ +package com.fasterxml.jackson.databind.jsonschema; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/NamedType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/NamedType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/NamedType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.databind.jsontype; + +/** + * Simple container class for types with optional logical name, used + * as external identifier + */ +public final class NamedType implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final Class _class; + protected final int _hashCode; + + protected String _name; + + public NamedType(Class c) { this(c, null); } + + public NamedType(Class c, String name) { + _class = c; + _hashCode = c.getName().hashCode(); + setName(name); + } + + public Class getType() { return _class; } + public String getName() { return _name; } + public void setName(String name) { _name = (name == null || name.length() == 0) ? null : name; } + + public boolean hasName() { return _name != null; } + + /** + * Equality is defined based on class only, not on name + */ + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + return _class == ((NamedType) o)._class; + } + + @Override + public int hashCode() { return _hashCode; } + + @Override + public String toString() { + return "[NamedType, class "+_class.getName()+", name: "+(_name == null ? "null" :("'"+_name+"'"))+"]"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,135 @@ +package com.fasterxml.jackson.databind.jsontype; + +import java.util.Collection; + +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; + +/** + * Helper object used for handling registration on resolving of super-types + * to sub-types. + */ +public abstract class SubtypeResolver +{ + /* + /********************************************************** + /* Methods for registering external subtype definitions + /********************************************************** + */ + + /** + * Method for registering specified subtypes (possibly including type + * names); for type entries without name, non-qualified class name + * as used as name (unless overridden by annotation). + */ + public abstract void registerSubtypes(NamedType... types); + + public abstract void registerSubtypes(Class... classes); + + /* + /********************************************************** + /* Subtype resolution + /********************************************************** + */ + + /** + * Method for finding out all reachable subtypes for a property specified + * by given element (method or field), + * such that access is by type, + * typically needed for serialization (converting from type to type name). + * + * @param baseType Effective property base type to use; may differ from + * actual type of property; for structured types it is content (value) type and NOT + * structured type. + * + * @since 2.6 + */ + public Collection collectAndResolveSubtypesByClass(MapperConfig config, + AnnotatedMember property, JavaType baseType) { + // for backwards compatibility... + return collectAndResolveSubtypes(property, config, + config.getAnnotationIntrospector(), baseType); + } + + /** + * Method for finding out all reachable subtypes for given type, + * such that access is by type, + * typically needed for serialization (converting from type to type name). + * + * @param baseType Effective property base type to use; may differ from + * actual type of property; for structured types it is content (value) type and NOT + * structured type. + * + * @since 2.6 + */ + public Collection collectAndResolveSubtypesByClass(MapperConfig config, + AnnotatedClass baseType) { + // for backwards compatibility... + return collectAndResolveSubtypes(baseType, config, config.getAnnotationIntrospector()); + } + + /** + * Method for finding out all reachable subtypes for a property specified + * by given element (method or field), + * such that access is by type id, + * typically needed for deserialization (converting from type id to type). + * + * @param baseType Effective property base type to use; may differ from + * actual type of property; for structured types it is content (value) type and NOT + * structured type. + * + * @since 2.6 + */ + public Collection collectAndResolveSubtypesByTypeId(MapperConfig config, + AnnotatedMember property, JavaType baseType) { + // for backwards compatibility... + return collectAndResolveSubtypes(property, config, + config.getAnnotationIntrospector(), baseType); + } + + /** + * Method for finding out all reachable subtypes for given type, + * such that access is by type id, + * typically needed for deserialization (converting from type id to type). + * + * @param baseType Effective property base type to use; may differ from + * actual type of property; for structured types it is content (value) type and NOT + * structured type. + * + * @since 2.6 + */ + public Collection collectAndResolveSubtypesByTypeId(MapperConfig config, + AnnotatedClass baseType) { + // for backwards compatibility... + return collectAndResolveSubtypes(baseType, config, config.getAnnotationIntrospector()); + } + + /* + /********************************************************** + /* Deprecated methods + /********************************************************** + */ + + /** + * @deprecated Since 2.6 Use either + * {@link #collectAndResolveSubtypesByClass(MapperConfig, AnnotatedMember, JavaType)} + * or {@link #collectAndResolveSubtypesByTypeId(MapperConfig, AnnotatedMember, JavaType)} + * instead. + */ + @Deprecated + public abstract Collection collectAndResolveSubtypes(AnnotatedMember property, + MapperConfig config, AnnotationIntrospector ai, JavaType baseType); + + /** + * @deprecated Since 2.6 Use either + * {@link #collectAndResolveSubtypesByClass(MapperConfig, AnnotatedClass)} + * or {@link #collectAndResolveSubtypesByTypeId(MapperConfig, AnnotatedClass)} + * instead. + */ + @Deprecated + public abstract Collection collectAndResolveSubtypes(AnnotatedClass baseType, + MapperConfig config, AnnotationIntrospector ai); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,179 @@ +package com.fasterxml.jackson.databind.jsontype; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; + + +/** + * Interface for deserializing type information from JSON content, to + * type-safely deserialize data into correct polymorphic instance + * (when type inclusion has been enabled for type handled). + *

+ * Separate deserialization methods are needed because serialized + * form for inclusion mechanism {@link As#PROPERTY} + * is slighty different if value is not expressed as JSON Object: + * and as such both type deserializer and serializer need to + * JSON Object form (array, object or other (== scalar)) being used. + */ +public abstract class TypeDeserializer +{ + /* + /********************************************************** + /* Initialization + /********************************************************** + */ + + /** + * Method called to create contextual version, to be used for + * values of given property. This may be the type itself + * (as is the case for bean properties), or values contained + * (for {@link java.util.Collection} or {@link java.util.Map} + * valued properties). + */ + public abstract TypeDeserializer forProperty(BeanProperty prop); + + /* + /********************************************************** + /* Introspection + /********************************************************** + */ + + /** + * Accessor for type information inclusion method + * that deserializer uses; indicates how type information + * is (expected to be) embedded in JSON input. + */ + public abstract As getTypeInclusion(); + + /** + * Name of property that contains type information, if + * property-based inclusion is used. + */ + public abstract String getPropertyName(); + + /** + * Accessor for object that handles conversions between + * types and matching type ids. + */ + public abstract TypeIdResolver getTypeIdResolver(); + + /** + * Accessor for "default implementation" type; optionally defined + * class to use in cases where type id is not + * accessible for some reason (either missing, or can not be + * resolved) + */ + public abstract Class getDefaultImpl(); + + /* + /********************************************************** + /* Type deserialization methods + /********************************************************** + */ + + /** + * Method called to let this type deserializer handle + * deserialization of "typed" object, when value itself + * is serialized as JSON Object (regardless of Java type). + * Method needs to figure out intended + * polymorphic type, locate {@link JsonDeserializer} to use, and + * call it with JSON data to deserializer (which does not contain + * type information). + */ + public abstract Object deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt) throws IOException; + + /** + * Method called to let this type deserializer handle + * deserialization of "typed" object, when value itself + * is serialized as JSON Array (regardless of Java type). + * Method needs to figure out intended + * polymorphic type, locate {@link JsonDeserializer} to use, and + * call it with JSON data to deserializer (which does not contain + * type information). + */ + public abstract Object deserializeTypedFromArray(JsonParser p, DeserializationContext ctxt) throws IOException; + + /** + * Method called to let this type deserializer handle + * deserialization of "typed" object, when value itself + * is serialized as a scalar JSON value (something other + * than Array or Object), regardless of Java type. + * Method needs to figure out intended + * polymorphic type, locate {@link JsonDeserializer} to use, and + * call it with JSON data to deserializer (which does not contain + * type information). + */ + public abstract Object deserializeTypedFromScalar(JsonParser p, DeserializationContext ctxt) throws IOException; + + /** + * Method called to let this type deserializer handle + * deserialization of "typed" object, when value itself + * may have been serialized using any kind of JSON value + * (Array, Object, scalar). Should only be called if JSON + * serialization is polymorphic (not Java type); for example when + * using JSON node representation, or "untyped" Java object + * (which may be Map, Collection, wrapper/primitive etc). + */ + public abstract Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt) throws IOException; + + /* + /********************************************************** + /* Shared helper methods + /********************************************************** + */ + + /** + * Helper method used to check if given parser might be pointing to + * a "natural" value, and one that would be acceptable as the + * result value (compatible with declared base type) + */ + public static Object deserializeIfNatural(JsonParser p, DeserializationContext ctxt, JavaType baseType) throws IOException { + return deserializeIfNatural(p, ctxt, baseType.getRawClass()); + } + + @SuppressWarnings("incomplete-switch") + public static Object deserializeIfNatural(JsonParser p, DeserializationContext ctxt, + Class base) throws IOException + { + JsonToken t = p.getCurrentToken(); + if (t == null) { + return null; + } + switch (t) { + case VALUE_STRING: + if (base.isAssignableFrom(String.class)) { + return p.getText(); + } + break; + case VALUE_NUMBER_INT: + if (base.isAssignableFrom(Integer.class)) { + return p.getIntValue(); + } + break; + + case VALUE_NUMBER_FLOAT: + if (base.isAssignableFrom(Double.class)) { + return Double.valueOf(p.getDoubleValue()); + } + break; + case VALUE_TRUE: + if (base.isAssignableFrom(Boolean.class)) { + return Boolean.TRUE; + } + break; + case VALUE_FALSE: + if (base.isAssignableFrom(Boolean.class)) { + return Boolean.FALSE; + } + break; + } + return null; + } +} + \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,101 @@ +package com.fasterxml.jackson.databind.jsontype; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.DatabindContext; +import com.fasterxml.jackson.databind.JavaType; + +/** + * Interface that defines standard API for converting types + * to type identifiers and vice versa. Used by type resolvers + * ({@link com.fasterxml.jackson.databind.jsontype.TypeSerializer}, + * {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}) for converting + * between type and matching id; id is stored in JSON and needed for + * creating instances of proper subtypes when deserializing values. + *

+ * NOTE: it is strongly recommended that developers always extend + * abstract base class {@link com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase} + * instead of directly implementing this interface; this helps prevent + * breakage in case new methds need to be added in this interface (something + * we try to avoid doing; but which may be necessary in some cases). + */ +public interface TypeIdResolver +{ + /* + /********************************************************** + /* Initialization/configuration methods + /********************************************************** + */ + + /** + * Method that will be called once before any type resolution calls; + * used to initialize instance with configuration. This is necessary + * since instances may be created via reflection, without ability to + * call specific constructor to pass in configuration settings. + * + * @param baseType Base type for which this id resolver instance is + * used + */ + public void init(JavaType baseType); + + /* + /********************************************************** + /* Conversions between types and type ids + /********************************************************** + */ + + /** + * Method called to serialize type of the type of given value + * as a String to include in serialized JSON content. + */ + public String idFromValue(Object value); + + /** + * Alternative method used for determining type from combination of + * value and type, using suggested type (that serializer provides) + * and possibly value of that type. Most common implementation will + * use suggested type as is. + */ + public String idFromValueAndType(Object value, Class suggestedType); + + /** + * Method that can be called to figure out type id to use for instances + * of base type (declared type of property). This is usually only used + * for fallback handling, for cases where real type information is not + * available for some reason. + */ + public String idFromBaseType(); + + // !!! TODO: remove from 2.8 + /** + * @deprecated since 2.5; call {@link #typeFromId(DatabindContext, String)} instead + */ + @Deprecated // since 2.5 + public JavaType typeFromId(String id); + + /** + * Method called to resolve type from given type identifier. + * + * @since 2.5 -- but since 2.3 has existed in {@link com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase} + */ + public JavaType typeFromId(DatabindContext context, String id); + + /** + * Method called for error-reporting and diagnostics purposes. + * + * @since 2.7 -- but since 2.5 has existed in {@link com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase} + */ + public String getDescForKnownTypeIds(); + + /* + /********************************************************** + /* Accessors for metadata + /********************************************************** + */ + + /** + * Accessor for mechanism that this resolver uses for determining + * type id from type. Mostly informational; not required to be called + * or used. + */ + public JsonTypeInfo.Id getMechanism(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,155 @@ +package com.fasterxml.jackson.databind.jsontype; + +import java.util.Collection; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; + +/** + * Interface that defines builders that are configured based on + * annotations (like {@link com.fasterxml.jackson.annotation.JsonTypeInfo} or JAXB annotations), + * and produce type serializers and deserializers used for + * handling type information embedded in JSON to allow for safe + * polymorphic type handling. + *

+ * Builder is first initialized by calling {@link #init} method, and then + * configured using 'set' methods like {@link #inclusion}. + * Finally, after calling all configuration methods, + * {@link #buildTypeSerializer} or {@link #buildTypeDeserializer} + * will be called to get actual type resolver constructed + * and used for resolving types for configured base type and its + * subtypes. + *

+ * Note that instances are used for two related but distinct use cases: + *

    + *
  • To create builders to use with explicit type information + * inclusion (usually via @JsonTypeInfo annotation) + *
  • + *
  • To create builders when "default typing" is used; if so, type information + * is automatically included for certain kind of types, regardless of annotations + *
  • + *
+ * Important distinction between the cases is that in first case, calls to + * create builders are only made when builders are certainly needed; whereas + * in second case builder has to first verify whether type information is + * applicable for given type, and if not, just return null to indicate this. + */ +public interface TypeResolverBuilder> +{ + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + /** + * Accessor for currently configured default type; implementation + * class that may be used in case no valid type information is + * available during type resolution + */ + public Class getDefaultImpl(); + + /* + /********************************************************** + /* Actual builder methods + /********************************************************** + */ + + /** + * Method for building type serializer based on current configuration + * of this builder. + * + * @param baseType Base type that constructed resolver will + * handle; super type of all types it will be used for. + */ + public TypeSerializer buildTypeSerializer(SerializationConfig config, + JavaType baseType, Collection subtypes); + + /** + * Method for building type deserializer based on current configuration + * of this builder. + * + * @param baseType Base type that constructed resolver will + * handle; super type of all types it will be used for. + * @param subtypes Known subtypes of the base type. + */ + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, + JavaType baseType, Collection subtypes); + + /* + /********************************************************** + /* Initialization method(s) that must be called before other + /* configuration + /********************************************************** + */ + + /** + * Initialization method that is called right after constructing + * the builder instance. + * + * @param idType Which type metadata is used + * @param res (optional) Custom type id resolver used, if any + * + * @return Resulting builder instance (usually this builder, + * but not necessarily) + */ + public T init(JsonTypeInfo.Id idType, TypeIdResolver res); + + /* + /********************************************************** + /* Methods for configuring resolver to build + /********************************************************** + */ + + /** + * Method for specifying mechanism to use for including type metadata + * in JSON. + * If not explicitly called, setting defaults to + * {@link As#PROPERTY}. + * + * @param includeAs Mechanism used for including type metadata in JSON + * + * @return Resulting builder instance (usually this builder, + * but may be a newly constructed instance for immutable builders} + */ + public T inclusion(As includeAs); + + /** + * Method for specifying name of property used for including type + * information. Not used for all inclusion mechanisms; + * usually only used with {@link As#PROPERTY}. + *

+ * If not explicitly called, name of property to use is based on + * defaults for {@link com.fasterxml.jackson.annotation.JsonTypeInfo.Id} configured. + * + * @param propName Name of JSON property to use for including + * type information + * + * @return Resulting builder instance (usually this builder, + * but may be a newly constructed instance for immutable builders} + */ + public T typeProperty(String propName); + + /** + * Method for specifying default implementation to use if type id + * is either not available, or can not be resolved. + * + * @return Resulting builder instance (usually this builder, + * but may be a newly constructed instance for immutable builders} + */ + public T defaultImpl(Class defaultImpl); + + /** + * Method for specifying whether type id should be visible to + * {@link com.fasterxml.jackson.databind.JsonDeserializer}s or not. + * + * @return Resulting builder instance (usually this builder, + * but may be a newly constructed instance for immutable builders} + * + * @since 2.0 + */ + public T typeIdVisibility(boolean isVisible); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,200 @@ +package com.fasterxml.jackson.databind.jsontype; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Interface for serializing type information regarding instances of specified + * base type (super class), so that exact subtype can be properly deserialized + * later on. These instances are to be called by regular + * {@link com.fasterxml.jackson.databind.JsonSerializer}s using proper contextual + * calls, to add type information using mechanism type serializer was + * configured with. + */ +public abstract class TypeSerializer +{ + /* + /********************************************************** + /* Initialization + /********************************************************** + */ + + /** + * Method called to create contextual version, to be used for + * values of given property. This may be the type itself + * (as is the case for bean properties), or values contained + * (for {@link java.util.Collection} or {@link java.util.Map} + * valued properties). + * + * @since 2.0 + */ + public abstract TypeSerializer forProperty(BeanProperty prop); + + /* + /********************************************************** + /* Introspection + /********************************************************** + */ + + /** + * Accessor for type information inclusion method + * that serializer uses; indicates how type information + * is embedded in resulting JSON. + */ + public abstract JsonTypeInfo.As getTypeInclusion(); + + /** + * Name of property that contains type information, if + * property-based inclusion is used. + */ + public abstract String getPropertyName(); + + /** + * Accessor for object that handles conversions between + * types and matching type ids. + */ + public abstract TypeIdResolver getTypeIdResolver(); + + /* + /********************************************************** + /* Type serialization methods + /********************************************************** + */ + + /** + * Method called to write initial part of type information for given + * value, when it will be output as scalar JSON value (not as JSON + * Object or Array). + * This means that the context after call can not be that of JSON Object; + * it may be Array or root context. + * + * @param value Value that will be serialized, for which type information is + * to be written + * @param jgen Generator to use for writing type information + */ + public abstract void writeTypePrefixForScalar(Object value, JsonGenerator jgen) throws IOException; + + /** + * Method called to write initial part of type information for given + * value, when it will be output as JSON Object value (not as JSON + * Array or scalar). + * This means that context after call must be JSON Object, meaning that + * caller can then proceed to output field entries. + * + * @param value Value that will be serialized, for which type information is + * to be written + * @param jgen Generator to use for writing type information + */ + public abstract void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException; + + /** + * Method called to write initial part of type information for given + * value, when it will be output as JSON Array value (not as JSON + * Object or scalar). + * This means that context after call must be JSON Array, that is, there + * must be an open START_ARRAY to write contents in. + * + * @param value Value that will be serialized, for which type information is + * to be written + * @param jgen Generator to use for writing type information + */ + public abstract void writeTypePrefixForArray(Object value, JsonGenerator jgen) throws IOException; + + /** + * Method called after value has been serialized, to close any scopes opened + * by earlier matching call to {@link #writeTypePrefixForScalar}. + * Actual action to take may depend on various factors, but has to match with + * action {@link #writeTypePrefixForScalar} did (close array or object; or do nothing). + */ + public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) throws IOException; + + /** + * Method called after value has been serialized, to close any scopes opened + * by earlier matching call to {@link #writeTypePrefixForObject}. + * It needs to write closing END_OBJECT marker, and any other decoration + * that needs to be matched. + */ + public abstract void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException; + + /** + * Method called after value has been serialized, to close any scopes opened + * by earlier matching call to {@link #writeTypeSuffixForScalar}. + * It needs to write closing END_ARRAY marker, and any other decoration + * that needs to be matched. + */ + public abstract void writeTypeSuffixForArray(Object value, JsonGenerator jgen) throws IOException; + + /** + * Alternative version of the prefix-for-scalar method, which is given + * actual type to use (instead of using exact type of the value); typically + * a super type of actual value type + */ + public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class type) throws IOException { + writeTypePrefixForScalar(value, jgen); + } + + /** + * Alternative version of the prefix-for-object method, which is given + * actual type to use (instead of using exact type of the value); typically + * a super type of actual value type + */ + public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class type) throws IOException { + writeTypePrefixForObject(value, jgen); + } + + /** + * Alternative version of the prefix-for-array method, which is given + * actual type to use (instead of using exact type of the value); typically + * a super type of actual value type + */ + public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class type) throws IOException { + writeTypePrefixForArray(value, jgen); + } + + /* + /********************************************************** + /* Type serialization methods with type id override + /********************************************************** + */ + + /** + * Method called to write initial part of type information for given + * value, when it will be output as scalar JSON value (not as JSON + * Object or Array), + * using specified custom type id instead of calling {@link TypeIdResolver}. + * This means that the context after call can not be that of JSON Object; + * it may be Array or root context. + * + * @param value Value that will be serialized, for which type information is + * to be written + * @param jgen Generator to use for writing type information + * @param typeId Exact type id to use + */ + public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException, JsonProcessingException; + + /** + * Method called to write initial part of type information for given + * value, when it will be output as JSON Object value (not as JSON + * Array or scalar), + * using specified custom type id instead of calling {@link TypeIdResolver}. + * This means that context after call must be JSON Object, meaning that + * caller can then proceed to output field entries. + * + * @param value Value that will be serialized, for which type information is + * to be written + * @param jgen Generator to use for writing type information + * @param typeId Exact type id to use + */ + public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException; + + public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException; + + public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException; + + public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException; + + public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,148 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.JsonParserSequence; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Type deserializer used with {@link As#WRAPPER_ARRAY} + * inclusion mechanism. Simple since JSON structure used is always + * the same, regardless of structure used for actual value: wrapping + * is done using a 2-element JSON Array where type id is the first + * element, and actual object data as second element. + */ +public class AsArrayTypeDeserializer + extends TypeDeserializerBase + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + public AsArrayTypeDeserializer(JavaType bt, TypeIdResolver idRes, + String typePropertyName, boolean typeIdVisible, Class defaultImpl) + { + super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl); + } + + public AsArrayTypeDeserializer(AsArrayTypeDeserializer src, BeanProperty property) { + super(src, property); + } + + @Override + public TypeDeserializer forProperty(BeanProperty prop) { + // usually if it's null: + return (prop == _property) ? this : new AsArrayTypeDeserializer(this, prop); + } + + @Override + public As getTypeInclusion() { return As.WRAPPER_ARRAY; } + + /** + * Method called when actual object is serialized as JSON Array. + */ + @Override + public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + /** + * Method called when actual object is serialized as JSON Object + */ + @Override + public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + @Override + public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + @Override + public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + /* + /*************************************************************** + /* Internal methods + /*************************************************************** + */ + + /** + * Method that handles type information wrapper, locates actual + * subtype deserializer to use, and calls it to do actual + * deserialization. + */ + @SuppressWarnings("resource") + protected Object _deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // 02-Aug-2013, tatu: May need to use native type ids + if (p.canReadTypeId()) { + Object typeId = p.getTypeId(); + if (typeId != null) { + return _deserializeWithNativeTypeId(p, ctxt, typeId); + } + } + boolean hadStartArray = p.isExpectedStartArrayToken(); + String typeId = _locateTypeId(p, ctxt); + JsonDeserializer deser = _findDeserializer(ctxt, typeId); + // Minor complication: we may need to merge type id in? + if (_typeIdVisible + // 06-Oct-2014, tatu: To fix [databind#408], must distinguish between + // internal and external properties + // TODO: but does it need to be injected in external case? Why not? + && !_usesExternalId() + && p.getCurrentToken() == JsonToken.START_OBJECT) { + // but what if there's nowhere to add it in? Error? Or skip? For now, skip. + TokenBuffer tb = new TokenBuffer(null, false); + tb.writeStartObject(); // recreate START_OBJECT + tb.writeFieldName(_typePropertyName); + tb.writeString(typeId); + p = JsonParserSequence.createFlattened(tb.asParser(p), p); + p.nextToken(); + } + Object value = deser.deserialize(p, ctxt); + // And then need the closing END_ARRAY + if (hadStartArray && p.nextToken() != JsonToken.END_ARRAY) { + throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY, + "expected closing END_ARRAY after type information and deserialized value"); + } + return value; + } + + protected String _locateTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException + { + if (!jp.isExpectedStartArrayToken()) { + // Need to allow even more customized handling, if something unexpected seen... + // but should there be a way to limit this to likely success cases? + if (_defaultImpl != null) { + return _idResolver.idFromBaseType(); + } + throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "need JSON Array to contain As.WRAPPER_ARRAY type information for class "+baseTypeName()); + } + // And then type id as a String + JsonToken t = jp.nextToken(); + if (t == JsonToken.VALUE_STRING) { + String result = jp.getText(); + jp.nextToken(); + return result; + } + if (_defaultImpl != null) { + return _idResolver.idFromBaseType(); + } + throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")"); + } + + /** + * @since 2.5 + */ + protected boolean _usesExternalId() { + return false; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,216 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Type serializer that will embed type information in an array, + * as the first element, and actual value as the second element. + */ +public class AsArrayTypeSerializer extends TypeSerializerBase +{ + public AsArrayTypeSerializer(TypeIdResolver idRes, BeanProperty property) { + super(idRes, property); + } + + @Override + public AsArrayTypeSerializer forProperty(BeanProperty prop) { + return (_property == prop) ? this : new AsArrayTypeSerializer(this._idResolver, prop); + } + + @Override + public As getTypeInclusion() { return As.WRAPPER_ARRAY; } + + /* + /********************************************************** + /* Writing prefixes + /********************************************************** + */ + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException { + final String typeId = idFromValue(value); + // NOTE: can not always avoid writing type id, even if null + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + jgen.writeStartObject(); + } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class type) throws IOException { + final String typeId = idFromValueAndType(value, type); + // NOTE: can not always avoid writing type id, even if null + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + jgen.writeStartObject(); + } + + @Override + public void writeTypePrefixForArray(Object value, JsonGenerator jgen) throws IOException { + final String typeId = idFromValue(value); + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + jgen.writeStartArray(); + } + + @Override + public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class type) throws IOException { + final String typeId = idFromValueAndType(value, type); + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + jgen.writeStartArray(); + } + + @Override + public void writeTypePrefixForScalar(Object value, JsonGenerator jgen) throws IOException { + final String typeId = idFromValue(value); + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + // only need the wrapper array + jgen.writeStartArray(); + jgen.writeString(typeId); + } + } + + @Override + public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class type) throws IOException { + final String typeId = idFromValueAndType(value, type); + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + // only need the wrapper array + jgen.writeStartArray(); + jgen.writeString(typeId); + } + } + + /* + /********************************************************** + /* Writing suffixes + /********************************************************** + */ + + @Override + public void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException { + jgen.writeEndObject(); + if (!jgen.canWriteTypeId()) { + jgen.writeEndArray(); + } + } + + @Override + public void writeTypeSuffixForArray(Object value, JsonGenerator jgen) throws IOException { + // first array caller needs to close, then wrapper array + jgen.writeEndArray(); + if (!jgen.canWriteTypeId()) { + jgen.writeEndArray(); + } + } + + @Override + public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) throws IOException { + if (!jgen.canWriteTypeId()) { + // just the wrapper array to close + jgen.writeEndArray(); + } + } + + /* + /********************************************************** + /* Writing with custom type id + /********************************************************** + */ + + @Override + public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException { + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + jgen.writeStartObject(); + } + + @Override + public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException { + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + jgen.writeStartArray(); + } + + @Override + public void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException { + if (jgen.canWriteTypeId()) { + if (typeId != null) { + jgen.writeTypeId(typeId); + } + } else { + jgen.writeStartArray(); + jgen.writeString(typeId); + } + } + + @Override + public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException { + if (!jgen.canWriteTypeId()) { + writeTypeSuffixForObject(value, jgen); // standard impl works fine + } + } + + @Override + public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException { + if (!jgen.canWriteTypeId()) { + writeTypeSuffixForArray(value, jgen); // standard impl works fine + } + } + + @Override + public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException { + if (!jgen.canWriteTypeId()) { + writeTypeSuffixForScalar(value, jgen); // standard impl works fine + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExistingPropertyTypeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,62 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Type serializer used with {@link As#EXISTING_PROPERTY} inclusion mechanism. + * Expects type information to be a well-defined property on all sub-classes. + * + * @author fleeman (modeled after code by tatus) + */ +public class AsExistingPropertyTypeSerializer + extends AsPropertyTypeSerializer +{ + + public AsExistingPropertyTypeSerializer(TypeIdResolver idRes, BeanProperty property, String propName) + { + super(idRes, property, propName); + } + + @Override + public AsExistingPropertyTypeSerializer forProperty(BeanProperty prop) { + return (_property == prop) ? this : new AsExistingPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName); + } + + @Override + public As getTypeInclusion() { return As.EXISTING_PROPERTY; } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException + { + final String typeId = idFromValue(value); + if ((typeId != null) && jgen.canWriteTypeId()) { + jgen.writeTypeId(typeId); + } + jgen.writeStartObject(); + } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class type) throws IOException + { + final String typeId = idFromValueAndType(value, type); + if ((typeId != null) && jgen.canWriteTypeId()) { + jgen.writeTypeId(typeId); + } + jgen.writeStartObject(); + } + + @Override + public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException + { + if ((typeId != null) && jgen.canWriteTypeId()) { + jgen.writeTypeId(typeId); + } + jgen.writeStartObject(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Type deserializer used with {@link As#EXTERNAL_PROPERTY} inclusion mechanism. + * Actual implementation may look bit strange since it depends on comprehensive + * pre-processing done by {@link com.fasterxml.jackson.databind.deser.BeanDeserializer} + * to basically transform external type id into structure that looks more like + * "wrapper-array" style inclusion. This intermediate form is chosen to allow + * supporting all possible JSON structures. + */ +public class AsExternalTypeDeserializer extends AsArrayTypeDeserializer +{ + private static final long serialVersionUID = 1L; + + public AsExternalTypeDeserializer(JavaType bt, TypeIdResolver idRes, + String typePropertyName, boolean typeIdVisible, Class defaultImpl) + { + super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl); + } + + public AsExternalTypeDeserializer(AsExternalTypeDeserializer src, BeanProperty property) { + super(src, property); + } + + @Override + public TypeDeserializer forProperty(BeanProperty prop) { + if (prop == _property) { // usually if it's null + return this; + } + return new AsExternalTypeDeserializer(this, prop); + } + + @Override + public As getTypeInclusion() { return As.EXTERNAL_PROPERTY; } + + // yes, very important distinction... + @Override + protected boolean _usesExternalId() { + return true; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,173 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Type serializer that preferably embeds type information as an "external" + * type property; embedded in enclosing JSON object. + * Note that this serializer should only be used when value is being output + * at JSON Object context; otherwise it can not work reliably, and will have + * to revert operation similar to {@link AsPropertyTypeSerializer}. + *

+ * Note that implementation of serialization is bit cumbersome as we must + * serialized external type id AFTER object; this because callback only + * occurs after field name has been written. + *

+ * Also note that this type of type id inclusion will NOT try to make use + * of native Type Ids, even if those exist. + */ +public class AsExternalTypeSerializer extends TypeSerializerBase +{ + protected final String _typePropertyName; + + public AsExternalTypeSerializer(TypeIdResolver idRes, BeanProperty property, String propName) { + super(idRes, property); + _typePropertyName = propName; + } + + @Override + public AsExternalTypeSerializer forProperty(BeanProperty prop) { + return (_property == prop) ? this : new AsExternalTypeSerializer(_idResolver, prop, _typePropertyName); + } + + @Override + public String getPropertyName() { return _typePropertyName; } + + @Override + public As getTypeInclusion() { return As.EXTERNAL_PROPERTY; } + + /* + /********************************************************** + /* Writing prefixes + /********************************************************** + */ + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException { + _writeObjectPrefix(value, gen); + } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class type) throws IOException { + _writeObjectPrefix(value, gen); + } + + @Override + public void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException { + _writeArrayPrefix(value, gen); + } + + @Override + public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class type) throws IOException { + _writeArrayPrefix(value, gen); + } + + @Override + public void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException { + _writeScalarPrefix(value, gen); + } + + @Override + public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class type) throws IOException { + _writeScalarPrefix(value, gen); + } + + /* + /********************************************************** + /* Writing suffixes + /********************************************************** + */ + + @Override + public void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException { + _writeObjectSuffix(value, gen, idFromValue(value)); + } + + @Override + public void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException { + _writeArraySuffix(value, gen, idFromValue(value)); + } + + @Override + public void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException { + _writeScalarSuffix(value, gen, idFromValue(value)); + } + + /* + /********************************************************** + /* Writing with custom type id + /********************************************************** + */ + + @Override + public void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException { + _writeScalarPrefix(value, gen); + } + + @Override + public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException { + _writeObjectPrefix(value, gen); + } + + @Override + public void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException { + _writeArrayPrefix(value, gen); + } + + @Override + public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException { + _writeScalarSuffix(value, gen, typeId); + } + + @Override + public void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException { + _writeObjectSuffix(value, gen, typeId); + } + + @Override + public void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException { + _writeArraySuffix(value, gen, typeId); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + // nothing to wrap it with: + protected final void _writeScalarPrefix(Object value, JsonGenerator gen) throws IOException { } + + protected final void _writeObjectPrefix(Object value, JsonGenerator gen) throws IOException { + gen.writeStartObject(); + } + + protected final void _writeArrayPrefix(Object value, JsonGenerator gen) throws IOException { + gen.writeStartArray(); + } + + protected final void _writeScalarSuffix(Object value, JsonGenerator gen, String typeId) throws IOException { + if (typeId != null) { + gen.writeStringField(_typePropertyName, typeId); + } + } + + protected final void _writeObjectSuffix(Object value, JsonGenerator gen, String typeId) throws IOException { + gen.writeEndObject(); + if (typeId != null) { + gen.writeStringField(_typePropertyName, typeId); + } + } + + protected final void _writeArraySuffix(Object value, JsonGenerator gen, String typeId) throws IOException { + gen.writeEndArray(); + if (typeId != null) { + gen.writeStringField(_typePropertyName, typeId); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,169 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.JsonParserSequence; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Type deserializer used with {@link As#PROPERTY} + * inclusion mechanism. + * Uses regular form (additional key/value entry before actual data) + * when typed object is expressed as JSON Object; otherwise behaves similar to how + * {@link As#WRAPPER_ARRAY} works. + * Latter is used if JSON representation is polymorphic + */ +public class AsPropertyTypeDeserializer extends AsArrayTypeDeserializer +{ + private static final long serialVersionUID = 1L; + + protected final As _inclusion; + + public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes, + String typePropertyName, boolean typeIdVisible, Class defaultImpl) + { + this(bt, idRes, typePropertyName, typeIdVisible, defaultImpl, As.PROPERTY); + } + + public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes, + String typePropertyName, boolean typeIdVisible, Class defaultImpl, + As inclusion) + { + super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl); + _inclusion = inclusion; + } + + public AsPropertyTypeDeserializer(AsPropertyTypeDeserializer src, BeanProperty property) { + super(src, property); + _inclusion = src._inclusion; + } + + @Override + public TypeDeserializer forProperty(BeanProperty prop) { + return (prop == _property) ? this : new AsPropertyTypeDeserializer(this, prop); + } + + @Override + public As getTypeInclusion() { return _inclusion; } + + /** + * This is the trickiest thing to handle, since property we are looking + * for may be anywhere... + */ + @Override + @SuppressWarnings("resource") + public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt) throws IOException + { + // 02-Aug-2013, tatu: May need to use native type ids + if (p.canReadTypeId()) { + Object typeId = p.getTypeId(); + if (typeId != null) { + return _deserializeWithNativeTypeId(p, ctxt, typeId); + } + } + + // but first, sanity check to ensure we have START_OBJECT or FIELD_NAME + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.START_OBJECT) { + t = p.nextToken(); + } else if (/*t == JsonToken.START_ARRAY ||*/ t != JsonToken.FIELD_NAME) { + /* This is most likely due to the fact that not all Java types are + * serialized as JSON Objects; so if "as-property" inclusion is requested, + * serialization of things like Lists must be instead handled as if + * "as-wrapper-array" was requested. + * But this can also be due to some custom handling: so, if "defaultImpl" + * is defined, it will be asked to handle this case. + */ + return _deserializeTypedUsingDefaultImpl(p, ctxt, null); + } + // Ok, let's try to find the property. But first, need token buffer... + TokenBuffer tb = null; + + for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { + String name = p.getCurrentName(); + p.nextToken(); // to point to the value + if (name.equals(_typePropertyName)) { // gotcha! + return _deserializeTypedForId(p, ctxt, tb); + } + if (tb == null) { + tb = new TokenBuffer(p, ctxt); + } + tb.writeFieldName(name); + tb.copyCurrentStructure(p); + } + return _deserializeTypedUsingDefaultImpl(p, ctxt, tb); + } + + @SuppressWarnings("resource") + protected Object _deserializeTypedForId(JsonParser p, DeserializationContext ctxt, TokenBuffer tb) throws IOException + { + String typeId = p.getText(); + JsonDeserializer deser = _findDeserializer(ctxt, typeId); + if (_typeIdVisible) { // need to merge id back in JSON input? + if (tb == null) { + tb = new TokenBuffer(p, ctxt); + } + tb.writeFieldName(p.getCurrentName()); + tb.writeString(typeId); + } + if (tb != null) { // need to put back skipped properties? + p = JsonParserSequence.createFlattened(tb.asParser(p), p); + } + // Must point to the next value; tb had no current, jp pointed to VALUE_STRING: + p.nextToken(); // to skip past String value + // deserializer should take care of closing END_OBJECT as well + return deser.deserialize(p, ctxt); + } + + // off-lined to keep main method lean and mean... + @SuppressWarnings("resource") + protected Object _deserializeTypedUsingDefaultImpl(JsonParser p, DeserializationContext ctxt, TokenBuffer tb) throws IOException + { + // As per [JACKSON-614], may have default implementation to use + JsonDeserializer deser = _findDefaultImplDeserializer(ctxt); + if (deser != null) { + if (tb != null) { + tb.writeEndObject(); + p = tb.asParser(p); + // must move to point to the first token: + p.nextToken(); + } + return deser.deserialize(p, ctxt); + } + // or, perhaps we just bumped into a "natural" value (boolean/int/double/String)? + Object result = TypeDeserializer.deserializeIfNatural(p, ctxt, _baseType); + if (result != null) { + return result; + } + // or, something for which "as-property" won't work, changed into "wrapper-array" type: + if (p.getCurrentToken() == JsonToken.START_ARRAY) { + return super.deserializeTypedFromAny(p, ctxt); + } + throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME, + "missing property '"+_typePropertyName+"' that is to contain type id (for class "+baseTypeName()+")"); + } + + /* Also need to re-route "unknown" version. Need to think + * this through bit more in future, but for now this does address issue and has + * no negative side effects (at least within existing unit test suite). + */ + @Override + public Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt) throws IOException { + /* Sometimes, however, we get an array wrapper; specifically + * when an array or list has been serialized with type information. + */ + if (p.getCurrentToken() == JsonToken.START_ARRAY) { + return super.deserializeTypedFromArray(p, ctxt); + } + return deserializeTypedFromObject(p, ctxt); + } + + // These are fine from base class: + //public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt) + //public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt) +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,111 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Type serializer that preferably embeds type information as an additional + * JSON Object property, if possible (when resulting serialization would + * use JSON Object). If this is not possible (for JSON Arrays, scalars), + * uses a JSON Array wrapper (similar to how + * {@link As#WRAPPER_ARRAY} always works) as a fallback. + */ +public class AsPropertyTypeSerializer + extends AsArrayTypeSerializer +{ + protected final String _typePropertyName; + + public AsPropertyTypeSerializer(TypeIdResolver idRes, BeanProperty property, String propName) + { + super(idRes, property); + _typePropertyName = propName; + } + + @Override + public AsPropertyTypeSerializer forProperty(BeanProperty prop) { + return (_property == prop) ? this : new AsPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName); + } + + @Override + public String getPropertyName() { return _typePropertyName; } + + @Override + public As getTypeInclusion() { return As.PROPERTY; } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException + { + final String typeId = idFromValue(value); + if (typeId == null) { + jgen.writeStartObject(); + } else if (jgen.canWriteTypeId()) { + jgen.writeTypeId(typeId); + jgen.writeStartObject(); + } else { + jgen.writeStartObject(); + jgen.writeStringField(_typePropertyName, typeId); + } + } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class type) throws IOException + { + final String typeId = idFromValueAndType(value, type); + if (typeId == null) { + jgen.writeStartObject(); + } else if (jgen.canWriteTypeId()) { + jgen.writeTypeId(typeId); + jgen.writeStartObject(); + } else { + jgen.writeStartObject(); + jgen.writeStringField(_typePropertyName, typeId); + } + } + + //public void writeTypePrefixForArray(Object value, JsonGenerator jgen) + //public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class type) + //public void writeTypePrefixForScalar(Object value, JsonGenerator jgen) + //public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class type) + + @Override + public void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException { + // always need to close, regardless of whether its native type id or not + jgen.writeEndObject(); + } + + //public void writeTypeSuffixForArray(Object value, JsonGenerator jgen) + //public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) + + + /* + /********************************************************** + /* Writing with custom type id + /********************************************************** + */ + + // Only need to override Object-variants + + @Override + public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException + { + if (typeId == null) { + jgen.writeStartObject(); + } else if (jgen.canWriteTypeId()) { + jgen.writeTypeId(typeId); + jgen.writeStartObject(); + } else { + jgen.writeStartObject(); + jgen.writeStringField(_typePropertyName, typeId); + } + } + + @Override + public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException { + jgen.writeEndObject(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,123 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.JsonParserSequence; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Type deserializer used with {@link As#WRAPPER_OBJECT} + * inclusion mechanism. Simple since JSON structure used is always + * the same, regardless of structure used for actual value: wrapping + * is done using a single-element JSON Object where type id is the key, + * and actual object data as the value. + */ +public class AsWrapperTypeDeserializer + extends TypeDeserializerBase + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + public AsWrapperTypeDeserializer(JavaType bt, TypeIdResolver idRes, + String typePropertyName, boolean typeIdVisible, Class defaultImpl) + { + super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl); + } + + protected AsWrapperTypeDeserializer(AsWrapperTypeDeserializer src, BeanProperty property) { + super(src, property); + } + + @Override + public TypeDeserializer forProperty(BeanProperty prop) { + return (prop == _property) ? this : new AsWrapperTypeDeserializer(this, prop); + } + + @Override + public As getTypeInclusion() { return As.WRAPPER_OBJECT; } + + /** + * Deserializing type id enclosed using WRAPPER_OBJECT style is straightforward + */ + @Override + public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + @Override + public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + @Override + public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + @Override + public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserialize(jp, ctxt); + } + + /* + /*************************************************************** + /* Internal methods + /*************************************************************** + */ + + /** + * Method that handles type information wrapper, locates actual + * subtype deserializer to use, and calls it to do actual + * deserialization. + */ + @SuppressWarnings("resource") + protected Object _deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + // 02-Aug-2013, tatu: May need to use native type ids + if (p.canReadTypeId()) { + Object typeId = p.getTypeId(); + if (typeId != null) { + return _deserializeWithNativeTypeId(p, ctxt, typeId); + } + } + // first, sanity checks + JsonToken t = p.getCurrentToken(); + if (t == JsonToken.START_OBJECT) { + // should always get field name, but just in case... + if (p.nextToken() != JsonToken.FIELD_NAME) { + throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME, + "need JSON String that contains type id (for subtype of "+baseTypeName()+")"); + } + } else if (t != JsonToken.FIELD_NAME) { + throw ctxt.wrongTokenException(p, JsonToken.START_OBJECT, + "need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName()); + } + final String typeId = p.getText(); + JsonDeserializer deser = _findDeserializer(ctxt, typeId); + p.nextToken(); + + // Minor complication: we may need to merge type id in? + if (_typeIdVisible && p.getCurrentToken() == JsonToken.START_OBJECT) { + // but what if there's nowhere to add it in? Error? Or skip? For now, skip. + TokenBuffer tb = new TokenBuffer(null, false); + tb.writeStartObject(); // recreate START_OBJECT + tb.writeFieldName(_typePropertyName); + tb.writeString(typeId); + p = JsonParserSequence.createFlattened(tb.asParser(p), p); + p.nextToken(); + } + + Object value = deser.deserialize(p, ctxt); + // And then need the closing END_OBJECT + if (p.nextToken() != JsonToken.END_OBJECT) { + throw ctxt.wrongTokenException(p, JsonToken.END_OBJECT, + "expected closing END_OBJECT after type information and deserialized value"); + } + return value; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,245 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; + +/** + * Type wrapper that tries to use an extra JSON Object, with a single + * entry that has type name as key, to serialize type information. + * If this is not possible (value is serialize as array or primitive), + * will use {@link As#WRAPPER_ARRAY} mechanism as fallback: that is, + * just use a wrapping array with type information as the first element + * and value as second. + */ +public class AsWrapperTypeSerializer extends TypeSerializerBase +{ + public AsWrapperTypeSerializer(TypeIdResolver idRes, BeanProperty property) { + super(idRes, property); + } + + @Override + public AsWrapperTypeSerializer forProperty(BeanProperty prop) { + return (_property == prop) ? this : new AsWrapperTypeSerializer(this._idResolver, prop); + } + + @Override + public As getTypeInclusion() { return As.WRAPPER_OBJECT; } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException + { + String typeId = idFromValue(value); + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + g.writeStartObject(); + } else { + // wrapper + g.writeStartObject(); + // and then JSON Object start caller wants + + // 28-Jan-2015, tatu: No really good answer here; can not really change + // structure, so change null to empty String... + g.writeObjectFieldStart(_validTypeId(typeId)); + } + } + + @Override + public void writeTypePrefixForObject(Object value, JsonGenerator g, Class type) throws IOException + { + String typeId = idFromValueAndType(value, type); + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + g.writeStartObject(); + } else { + // wrapper + g.writeStartObject(); + // and then JSON Object start caller wants + + // 28-Jan-2015, tatu: No really good answer here; can not really change + // structure, so change null to empty String... + g.writeObjectFieldStart(_validTypeId(typeId)); + } + } + + @Override + public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException + { + String typeId = idFromValue(value); + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + g.writeStartArray(); + } else { + // can still wrap ok + g.writeStartObject(); + g.writeArrayFieldStart(_validTypeId(typeId)); + } + } + + @Override + public void writeTypePrefixForArray(Object value, JsonGenerator g, Class type) throws IOException + { + final String typeId = idFromValueAndType(value, type); + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + g.writeStartArray(); + } else { + // can still wrap ok + g.writeStartObject(); + // and then JSON Array start caller wants + g.writeArrayFieldStart(_validTypeId(typeId)); + } + } + + @Override + public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException { + final String typeId = idFromValue(value); + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + } else { + // can still wrap ok + g.writeStartObject(); + g.writeFieldName(_validTypeId(typeId)); + } + } + + @Override + public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class type) throws IOException + { + final String typeId = idFromValueAndType(value, type); + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + } else { + // can still wrap ok + g.writeStartObject(); + g.writeFieldName(_validTypeId(typeId)); + } + } + + @Override + public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException + { + // first close JSON Object caller used + g.writeEndObject(); + if (!g.canWriteTypeId()) { + // and then wrapper + g.writeEndObject(); + } + } + + @Override + public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException + { + // first close array caller needed + g.writeEndArray(); + if (!g.canWriteTypeId()) { + // then wrapper object + g.writeEndObject(); + } + } + + @Override + public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException { + if (!g.canWriteTypeId()) { + // just need to close the wrapper object + g.writeEndObject(); + } + } + + /* + /********************************************************** + /* Writing with custom type id + /********************************************************** + */ + + @Override + public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException { + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + g.writeStartObject(); + } else { + g.writeStartObject(); + g.writeObjectFieldStart(_validTypeId(typeId)); + } + } + + @Override + public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException { + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + g.writeStartArray(); + } else { + g.writeStartObject(); + g.writeArrayFieldStart(_validTypeId(typeId)); + } + } + + @Override + public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException { + if (g.canWriteTypeId()) { + if (typeId != null) { + g.writeTypeId(typeId); + } + } else { + g.writeStartObject(); + g.writeFieldName(_validTypeId(typeId)); + } + } + + @Override + public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException { + if (!g.canWriteTypeId()) { + writeTypeSuffixForObject(value, g); // standard impl works fine + } + } + + @Override + public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException { + if (!g.canWriteTypeId()) { + writeTypeSuffixForArray(value, g); // standard impl works fine + } + } + + @Override + public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException { + if (!g.canWriteTypeId()) { + writeTypeSuffixForScalar(value, g); // standard impl works fine + } + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + /** + * Helper method used to ensure that intended type id is output as something that is valid: + * currently only used to ensure that `null` output is converted to an empty String. + * + * @since 2.6 + */ + protected String _validTypeId(String typeId) { + return (typeId == null) ? "" : typeId; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,145 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.DatabindContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver} implementation + * that converts between fully-qualified + * Java class names and (JSON) Strings. + */ +public class ClassNameIdResolver + extends TypeIdResolverBase +{ + public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) { + super(baseType, typeFactory); + } + + @Override + public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.CLASS; } + + public void registerSubtype(Class type, String name) { + // not used with class name - based resolvers + } + + @Override + public String idFromValue(Object value) { + return _idFrom(value, value.getClass()); + } + + @Override + public String idFromValueAndType(Object value, Class type) { + return _idFrom(value, type); + } + + @Deprecated // since 2.3 + @Override + public JavaType typeFromId(String id) { + return _typeFromId(id, _typeFactory); + } + + @Override + public JavaType typeFromId(DatabindContext context, String id) { + return _typeFromId(id, context.getTypeFactory()); + } + + protected JavaType _typeFromId(String id, TypeFactory typeFactory) + { + /* 30-Jan-2010, tatu: Most ids are basic class names; so let's first + * check if any generics info is added; and only then ask factory + * to do translation when necessary + */ + if (id.indexOf('<') > 0) { + JavaType t = typeFactory.constructFromCanonical(id); + // note: may want to try combining with specialization (esp for EnumMap)? + return t; + } + try { + Class cls = typeFactory.findClass(id); + return typeFactory.constructSpecializedType(_baseType, cls); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): no such class found"); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): "+e.getMessage(), e); + } + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected final String _idFrom(Object value, Class cls) + { + // Need to ensure that "enum subtypes" work too + if (Enum.class.isAssignableFrom(cls)) { + if (!cls.isEnum()) { // means that it's sub-class of base enum, so: + cls = cls.getSuperclass(); + } + } + String str = cls.getName(); + if (str.startsWith("java.util")) { + // 25-Jan-2009, tatu: There are some internal classes that we can not access as is. + // We need better mechanism; for now this has to do... + + // Enum sets and maps are problematic since we MUST know type of + // contained enums, to be able to deserialize. + // In addition, EnumSet is not a concrete type either + if (value instanceof EnumSet) { // Regular- and JumboEnumSet... + Class enumClass = ClassUtil.findEnumType((EnumSet) value); + // not optimal: but EnumSet is not a customizable type so this is sort of ok + str = _typeFactory.constructCollectionType(EnumSet.class, enumClass).toCanonical(); + } else if (value instanceof EnumMap) { + Class enumClass = ClassUtil.findEnumType((EnumMap) value); + Class valueClass = Object.class; + // not optimal: but EnumMap is not a customizable type so this is sort of ok + str = _typeFactory.constructMapType(EnumMap.class, enumClass, valueClass).toCanonical(); + } else { + String end = str.substring(9); + if ((end.startsWith(".Arrays$") || end.startsWith(".Collections$")) + && str.indexOf("List") >= 0) { + /* 17-Feb-2010, tatus: Another such case: result of + * Arrays.asList() is named like so in Sun JDK... + * Let's just plain old ArrayList in its place + * NOTE: chances are there are plenty of similar cases + * for other wrappers... (immutable, singleton, synced etc) + */ + str = "java.util.ArrayList"; + } + } + } else if (str.indexOf('$') >= 0) { + /* Other special handling may be needed for inner classes, [JACKSON-584]. + * The best way to handle would be to find 'hidden' constructor; pass parent + * value etc (which is actually done for non-anonymous static classes!), + * but that is just not possible due to various things. So, we will instead + * try to generalize type into something we will be more likely to be able + * construct. + */ + Class outer = ClassUtil.getOuterClass(cls); + if (outer != null) { + /* one more check: let's actually not worry if the declared + * static type is non-static as well; if so, deserializer does + * have a chance at figuring it all out. + */ + Class staticType = _baseType.getRawClass(); + if (ClassUtil.getOuterClass(staticType) == null) { + // Is this always correct? Seems like it should be... + cls = _baseType.getRawClass(); + str = cls.getName(); + } + } + } + return str; + } + + @Override + public String getDescForKnownTypeIds() { + return "class name used as type id"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class MinimalClassNameIdResolver + extends ClassNameIdResolver +{ + /** + * Package name of the base class, to be used for determining common + * prefix that can be omitted from included type id. + * Does not include the trailing dot. + */ + protected final String _basePackageName; + + /** + * Same as {@link #_basePackageName}, but includes trailing dot. + */ + protected final String _basePackagePrefix; + + protected MinimalClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) + { + super(baseType, typeFactory); + String base = baseType.getRawClass().getName(); + int ix = base.lastIndexOf('.'); + if (ix < 0) { // can this ever occur? + _basePackageName = ""; + _basePackagePrefix = "."; + } else { + _basePackagePrefix = base.substring(0, ix+1); + _basePackageName = base.substring(0, ix); + } + } + + @Override + public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.MINIMAL_CLASS; } + + @Override + public String idFromValue(Object value) + { + String n = value.getClass().getName(); + if (n.startsWith(_basePackagePrefix)) { + // note: we will leave the leading dot in there + return n.substring(_basePackagePrefix.length()-1); + } + return n; + } + + @Override + protected JavaType _typeFromId(String id, TypeFactory typeFactory) + { + if (id.startsWith(".")) { + StringBuilder sb = new StringBuilder(id.length() + _basePackageName.length()); + if (_basePackageName.length() == 0) { + // no package; must remove leading '.' from id + sb.append(id.substring(1)); + } else { + // otherwise just concatenate package, with leading-dot-partial name + sb.append(_basePackageName).append(id); + } + id = sb.toString(); + } + return super._typeFromId(id, typeFactory); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,300 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.util.*; + +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; + +/** + * Standard {@link SubtypeResolver} implementation. + */ +public class StdSubtypeResolver + extends SubtypeResolver + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected LinkedHashSet _registeredSubtypes; + + public StdSubtypeResolver() { } + + /* + /********************************************************** + /* Subtype registration + /********************************************************** + */ + + @Override + public void registerSubtypes(NamedType... types) { + if (_registeredSubtypes == null) { + _registeredSubtypes = new LinkedHashSet(); + } + for (NamedType type : types) { + _registeredSubtypes.add(type); + } + } + + @Override + public void registerSubtypes(Class... classes) { + NamedType[] types = new NamedType[classes.length]; + for (int i = 0, len = classes.length; i < len; ++i) { + types[i] = new NamedType(classes[i]); + } + registerSubtypes(types); + } + + /* + /********************************************************** + /* Resolution by class (serialization) + /********************************************************** + */ + + @Override + public Collection collectAndResolveSubtypesByClass(MapperConfig config, + AnnotatedMember property, JavaType baseType) + { + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + // for backwards compatibility, must allow null here: + Class rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass(); + + HashMap collected = new HashMap(); + // start with registered subtypes (which have precedence) + if (_registeredSubtypes != null) { + for (NamedType subtype : _registeredSubtypes) { + // is it a subtype of root type? + if (rawBase.isAssignableFrom(subtype.getType())) { // yes + AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); + _collectAndResolve(curr, subtype, config, ai, collected); + } + } + } + + // then annotated types for property itself + Collection st = ai.findSubtypes(property); + if (st != null) { + for (NamedType nt : st) { + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(nt.getType(), config); + _collectAndResolve(ac, nt, config, ai, collected); + } + } + + NamedType rootType = new NamedType(rawBase, null); + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, config); + + // and finally subtypes via annotations from base type (recursively) + _collectAndResolve(ac, rootType, config, ai, collected); + + return new ArrayList(collected.values()); + } + + @Override + public Collection collectAndResolveSubtypesByClass(MapperConfig config, + AnnotatedClass type) + { + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + HashMap subtypes = new HashMap(); + // [JACKSON-257] then consider registered subtypes (which have precedence over annotations) + if (_registeredSubtypes != null) { + Class rawBase = type.getRawType(); + for (NamedType subtype : _registeredSubtypes) { + // is it a subtype of root type? + if (rawBase.isAssignableFrom(subtype.getType())) { // yes + AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); + _collectAndResolve(curr, subtype, config, ai, subtypes); + } + } + } + // and then check subtypes via annotations from base type (recursively) + NamedType rootType = new NamedType(type.getRawType(), null); + _collectAndResolve(type, rootType, config, ai, subtypes); + return new ArrayList(subtypes.values()); + } + + /* + /********************************************************** + /* Resolution by class (deserialization) + /********************************************************** + */ + + @Override + public Collection collectAndResolveSubtypesByTypeId(MapperConfig config, + AnnotatedMember property, JavaType baseType) + { + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + Class rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass(); + + // Need to keep track of classes that have been handled already + Set> typesHandled = new HashSet>(); + Map byName = new LinkedHashMap(); + + // start with lowest-precedence, which is from type hierarchy + NamedType rootType = new NamedType(rawBase, null); + AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, config); + _collectAndResolveByTypeId(ac, rootType, config, typesHandled, byName); + + // then with definitions from property + Collection st = ai.findSubtypes(property); + if (st != null) { + for (NamedType nt : st) { + ac = AnnotatedClass.constructWithoutSuperTypes(nt.getType(), config); + _collectAndResolveByTypeId(ac, nt, config, typesHandled, byName); + } + } + + // and finally explicit type registrations (highest precedence) + if (_registeredSubtypes != null) { + for (NamedType subtype : _registeredSubtypes) { + // is it a subtype of root type? + if (rawBase.isAssignableFrom(subtype.getType())) { // yes + AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); + _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName); + } + } + } + return _combineNamedAndUnnamed(typesHandled, byName); + } + + @Override + public Collection collectAndResolveSubtypesByTypeId(MapperConfig config, + AnnotatedClass type) + { + Set> typesHandled = new HashSet>(); + Map byName = new LinkedHashMap(); + + NamedType rootType = new NamedType(type.getRawType(), null); + _collectAndResolveByTypeId(type, rootType, config, typesHandled, byName); + + if (_registeredSubtypes != null) { + Class rawBase = type.getRawType(); + for (NamedType subtype : _registeredSubtypes) { + // is it a subtype of root type? + if (rawBase.isAssignableFrom(subtype.getType())) { // yes + AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); + _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName); + } + } + } + return _combineNamedAndUnnamed(typesHandled, byName); + } + + /* + /********************************************************** + /* Deprecated method overrides + /********************************************************** + */ + + @Override + @Deprecated + public Collection collectAndResolveSubtypes(AnnotatedMember property, + MapperConfig config, AnnotationIntrospector ai, JavaType baseType) + { + return collectAndResolveSubtypesByClass(config, property, baseType); + } + + @Override + @Deprecated + public Collection collectAndResolveSubtypes(AnnotatedClass type, + MapperConfig config, AnnotationIntrospector ai) + { + return collectAndResolveSubtypesByClass(config, type); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * Method called to find subtypes for a specific type (class), using + * type (class) as the unique key (in case of conflicts). + */ + protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedType, + MapperConfig config, AnnotationIntrospector ai, + HashMap collectedSubtypes) + { + if (!namedType.hasName()) { + String name = ai.findTypeName(annotatedType); + if (name != null) { + namedType = new NamedType(namedType.getType(), name); + } + } + + // First things first: is base type itself included? + if (collectedSubtypes.containsKey(namedType)) { + // if so, no recursion; however, may need to update name? + if (namedType.hasName()) { + NamedType prev = collectedSubtypes.get(namedType); + if (!prev.hasName()) { + collectedSubtypes.put(namedType, namedType); + } + } + return; + } + // if it wasn't, add and check subtypes recursively + collectedSubtypes.put(namedType, namedType); + Collection st = ai.findSubtypes(annotatedType); + if (st != null && !st.isEmpty()) { + for (NamedType subtype : st) { + AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); + _collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes); + } + } + } + + /** + * Method called to find subtypes for a specific type (class), using + * type id as the unique key (in case of conflicts). + */ + protected void _collectAndResolveByTypeId(AnnotatedClass annotatedType, NamedType namedType, + MapperConfig config, + Set> typesHandled, Map byName) + { + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + if (!namedType.hasName()) { + String name = ai.findTypeName(annotatedType); + if (name != null) { + namedType = new NamedType(namedType.getType(), name); + } + } + if (namedType.hasName()) { + byName.put(namedType.getName(), namedType); + } + + // only check subtypes if this type hadn't yet been handled + if (typesHandled.add(namedType.getType())) { + Collection st = ai.findSubtypes(annotatedType); + if (st != null && !st.isEmpty()) { + for (NamedType subtype : st) { + AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); + _collectAndResolveByTypeId(subtypeClass, subtype, config, typesHandled, byName); + } + } + } + } + + /** + * Helper method used for merging explicitly named types and handled classes + * without explicit names. + */ + protected Collection _combineNamedAndUnnamed(Set> typesHandled, + Map byName) + { + ArrayList result = new ArrayList(byName.values()); + + // Ok, so... we will figure out which classes have no explicitly assigned name, + // by removing Classes from Set. And for remaining classes, add an anonymous + // marker + for (NamedType t : byName.values()) { + typesHandled.remove(t.getType()); + } + for (Class cls : typesHandled) { + result.add(new NamedType(cls)); + } + return result; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,206 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.util.Collection; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Default {@link TypeResolverBuilder} implementation. + */ +public class StdTypeResolverBuilder + implements TypeResolverBuilder +{ + // Configuration settings: + + protected JsonTypeInfo.Id _idType; + + protected JsonTypeInfo.As _includeAs; + + protected String _typeProperty; + + /** + * Whether type id should be exposed to deserializers or not + */ + protected boolean _typeIdVisible = false; + + /** + * Default class to use in case type information is not available + * or is broken. + */ + protected Class _defaultImpl; + + // Objects + + protected TypeIdResolver _customIdResolver; + + /* + /********************************************************** + /* Construction, initialization, actual building + /********************************************************** + */ + + public StdTypeResolverBuilder() { } + + public static StdTypeResolverBuilder noTypeInfoBuilder() { + return new StdTypeResolverBuilder().init(JsonTypeInfo.Id.NONE, null); + } + + @Override + public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes) + { + // sanity checks + if (idType == null) { + throw new IllegalArgumentException("idType can not be null"); + } + _idType = idType; + _customIdResolver = idRes; + // Let's also initialize property name as per idType default + _typeProperty = idType.getDefaultPropertyName(); + return this; + } + + @Override + public TypeSerializer buildTypeSerializer(SerializationConfig config, + JavaType baseType, Collection subtypes) + { + if (_idType == JsonTypeInfo.Id.NONE) { return null; } + TypeIdResolver idRes = idResolver(config, baseType, subtypes, true, false); + switch (_includeAs) { + case WRAPPER_ARRAY: + return new AsArrayTypeSerializer(idRes, null); + case PROPERTY: + return new AsPropertyTypeSerializer(idRes, null, _typeProperty); + case WRAPPER_OBJECT: + return new AsWrapperTypeSerializer(idRes, null); + case EXTERNAL_PROPERTY: + return new AsExternalTypeSerializer(idRes, null, _typeProperty); + case EXISTING_PROPERTY: + // as per [#528] + return new AsExistingPropertyTypeSerializer(idRes, null, _typeProperty); + } + throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs); + } + + // as per [#368] + // removed when fix [#528] + //private IllegalArgumentException _noExisting() { + // return new IllegalArgumentException("Inclusion type "+_includeAs+" not yet supported"); + //} + + @Override + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, + JavaType baseType, Collection subtypes) + { + if (_idType == JsonTypeInfo.Id.NONE) { return null; } + + TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true); + + // First, method for converting type info to type id: + switch (_includeAs) { + case WRAPPER_ARRAY: + return new AsArrayTypeDeserializer(baseType, idRes, + _typeProperty, _typeIdVisible, _defaultImpl); + case PROPERTY: + case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY + return new AsPropertyTypeDeserializer(baseType, idRes, + _typeProperty, _typeIdVisible, _defaultImpl, _includeAs); + case WRAPPER_OBJECT: + return new AsWrapperTypeDeserializer(baseType, idRes, + _typeProperty, _typeIdVisible, _defaultImpl); + case EXTERNAL_PROPERTY: + return new AsExternalTypeDeserializer(baseType, idRes, + _typeProperty, _typeIdVisible, _defaultImpl); + } + throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs); + } + + /* + /********************************************************** + /* Construction, configuration + /********************************************************** + */ + + @Override + public StdTypeResolverBuilder inclusion(JsonTypeInfo.As includeAs) { + if (includeAs == null) { + throw new IllegalArgumentException("includeAs can not be null"); + } + _includeAs = includeAs; + return this; + } + + /** + * Method for constructing an instance with specified type property name + * (property name to use for type id when using "as-property" inclusion). + */ + @Override + public StdTypeResolverBuilder typeProperty(String typeIdPropName) { + // ok to have null/empty; will restore to use defaults + if (typeIdPropName == null || typeIdPropName.length() == 0) { + typeIdPropName = _idType.getDefaultPropertyName(); + } + _typeProperty = typeIdPropName; + return this; + } + + @Override + public StdTypeResolverBuilder defaultImpl(Class defaultImpl) { + _defaultImpl = defaultImpl; + return this; + } + + @Override + public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) { + _typeIdVisible = isVisible; + return this; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override public Class getDefaultImpl() { return _defaultImpl; } + + public String getTypeProperty() { return _typeProperty; } + public boolean isTypeIdVisible() { return _typeIdVisible; } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * Helper method that will either return configured custom + * type id resolver, or construct a standard resolver + * given configuration. + */ + protected TypeIdResolver idResolver(MapperConfig config, + JavaType baseType, Collection subtypes, boolean forSer, boolean forDeser) + { + // Custom id resolver? + if (_customIdResolver != null) { return _customIdResolver; } + if (_idType == null) throw new IllegalStateException("Can not build, 'init()' not yet called"); + switch (_idType) { + case CLASS: + return new ClassNameIdResolver(baseType, config.getTypeFactory()); + case MINIMAL_CLASS: + return new MinimalClassNameIdResolver(baseType, config.getTypeFactory()); + case NAME: + return TypeNameIdResolver.construct(config, baseType, subtypes, forSer, forDeser); + case NONE: // hmmh. should never get this far with 'none' + return null; + case CUSTOM: // need custom resolver... + } + throw new IllegalStateException("Do not know how to construct standard type id resolver for idType: "+_idType); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,283 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Base class for all standard Jackson {@link TypeDeserializer}s. + */ +public abstract class TypeDeserializerBase + extends TypeDeserializer + implements java.io.Serializable +{ + private static final long serialVersionUID = 1; + + protected final TypeIdResolver _idResolver; + + protected final JavaType _baseType; + + /** + * Property that contains value for which type information + * is included; null if value is a root value. + * Note that this value is not assigned during construction + * but only when {@link #forProperty} is called to create + * a copy. + */ + protected final BeanProperty _property; + + /** + * Type to use as the default implementation, if type id is + * missing or can not be resolved. + */ + protected final JavaType _defaultImpl; + + /** + * Name of type property used; needed for non-property versions too, + * in cases where type id is to be exposed as part of JSON. + */ + protected final String _typePropertyName; + + protected final boolean _typeIdVisible; + + /** + * For efficient operation we will lazily build mappings from type ids + * to actual deserializers, once needed. + */ + protected final Map> _deserializers; + + protected JsonDeserializer _defaultImplDeserializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected TypeDeserializerBase(JavaType baseType, TypeIdResolver idRes, + String typePropertyName, boolean typeIdVisible, Class defaultImpl) + { + _baseType = baseType; + _idResolver = idRes; + // 22-Dec-2015, tatu: as per [databind#1055], avoid NPE + _typePropertyName = (typePropertyName == null) ? "" : typePropertyName; + _typeIdVisible = typeIdVisible; + // defaults are fine, although shouldn't need much concurrency + _deserializers = new ConcurrentHashMap>(16, 0.75f, 2); + if (defaultImpl == null) { + _defaultImpl = null; + } else { + /* 16-Oct-2011, tatu: should call this via TypeFactory; this is + * not entirely safe... however, since Collections/Maps are + * seldom (if ever) base types, may be ok. + */ + // 01-Nov-2015, tatu: Actually this is still exactly wrong. Should fix. + // 15-Jan-2016, tatu: ... as witnessed by [databind#1083], patched, but + // fundamentally this call can't be made to work for all cases + _defaultImpl = baseType.forcedNarrowBy(defaultImpl); + } + _property = null; + } + + protected TypeDeserializerBase(TypeDeserializerBase src, BeanProperty property) + { + _baseType = src._baseType; + _idResolver = src._idResolver; + _typePropertyName = src._typePropertyName; + _typeIdVisible = src._typeIdVisible; + _deserializers = src._deserializers; + _defaultImpl = src._defaultImpl; + _defaultImplDeserializer = src._defaultImplDeserializer; + _property = property; + } + + @Override + public abstract TypeDeserializer forProperty(BeanProperty prop); + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public abstract JsonTypeInfo.As getTypeInclusion(); + + public String baseTypeName() { return _baseType.getRawClass().getName(); } + + @Override + public final String getPropertyName() { return _typePropertyName; } + + @Override + public TypeIdResolver getTypeIdResolver() { return _idResolver; } + + @Override + public Class getDefaultImpl() { + return (_defaultImpl == null) ? null : _defaultImpl.getRawClass(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append('[').append(getClass().getName()); + sb.append("; base-type:").append(_baseType); + sb.append("; id-resolver: ").append(_idResolver); + sb.append(']'); + return sb.toString(); + } + + /* + /********************************************************** + /* Helper methods for sub-classes + /********************************************************** + */ + + protected final JsonDeserializer _findDeserializer(DeserializationContext ctxt, + String typeId) throws IOException + { + JsonDeserializer deser = _deserializers.get(typeId); + if (deser == null) { + /* As per [Databind#305], need to provide contextual info. But for + * backwards compatibility, let's start by only supporting this + * for base class, not via interface. Later on we can add this + * to the interface, assuming deprecation at base class helps. + */ + JavaType type = _idResolver.typeFromId(ctxt, typeId); + if (type == null) { + // As per [JACKSON-614], use the default impl if no type id available: + deser = _findDefaultImplDeserializer(ctxt); + if (deser == null) { + deser = _handleUnknownTypeId(ctxt, typeId, _idResolver, _baseType); + } + } else { + /* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters, + * we actually now need to explicitly narrow from base type (which may have parameterization) + * using raw type. + * + * One complication, though; can not change 'type class' (simple type to container); otherwise + * we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual + * type in process (getting SimpleType of Map.class which will not work as expected) + */ + if ((_baseType != null) + && _baseType.getClass() == type.getClass()) { + /* 09-Aug-2015, tatu: Not sure if the second part of the check makes sense; + * but it appears to check that JavaType impl class is the same which is + * important for some reason? + * Disabling the check will break 2 Enum-related tests. + */ + type = ctxt.getTypeFactory().constructSpecializedType(_baseType, type.getRawClass()); + } + deser = ctxt.findContextualValueDeserializer(type, _property); + } + _deserializers.put(typeId, deser); + } + return deser; + } + + protected final JsonDeserializer _findDefaultImplDeserializer(DeserializationContext ctxt) throws IOException + { + /* 06-Feb-2013, tatu: As per [databind#148], consider default implementation value of + * {@link java.lang.Void} to mean "serialize as null"; as well as DeserializationFeature + * to do swift mapping to null + */ + if (_defaultImpl == null) { + if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) { + return NullifyingDeserializer.instance; + } + return null; + } + Class raw = _defaultImpl.getRawClass(); + if (ClassUtil.isBogusClass(raw)) { + return NullifyingDeserializer.instance; + } + + synchronized (_defaultImpl) { + if (_defaultImplDeserializer == null) { + _defaultImplDeserializer = ctxt.findContextualValueDeserializer( + _defaultImpl, _property); + } + return _defaultImplDeserializer; + } + } + + /** + * Helper method called when {@link JsonParser} indicates that it can use + * so-called native type ids. Assumption from there is that only native + * type ids are to be used. + * + * @since 2.3 + */ + @Deprecated + protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException { + return _deserializeWithNativeTypeId(jp, ctxt, jp.getTypeId()); + } + + /** + * Helper method called when {@link JsonParser} indicates that it can use + * so-called native type ids, and such type id has been found. + * + * @since 2.4 + */ + protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt, Object typeId) + throws IOException + { + JsonDeserializer deser; + if (typeId == null) { + /* 04-May-2014, tatu: Should error be obligatory, or should there be another method + * for "try to deserialize with native tpye id"? + */ + deser = _findDefaultImplDeserializer(ctxt); + if (deser == null) { + throw ctxt.mappingException("No (native) type id found when one was expected for polymorphic type handling"); + } + } else { + String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId); + deser = _findDeserializer(ctxt, typeIdStr); + } + return deser.deserialize(jp, ctxt); + } + + /** + * Helper method called when given type id can not be resolved into + * concrete deserializer either directly (using given {@link TypeIdResolver}), + * or using default type. + * Default implementation simply throws a {@link com.fasterxml.jackson.databind.JsonMappingException} to + * indicate the problem; sub-classes may choose + * + * @return If it is possible to resolve type id into a {@link JsonDeserializer} + * should return that deserializer; otherwise throw an exception to indicate + * the problem. + * + * @since 2.5 + */ + protected JsonDeserializer _handleUnknownTypeId(DeserializationContext ctxt, String typeId, + TypeIdResolver idResolver, JavaType baseType) + throws IOException + { + String extraDesc; + if (idResolver instanceof TypeIdResolverBase) { + extraDesc = ((TypeIdResolverBase) idResolver).getDescForKnownTypeIds(); + if (extraDesc == null) { + extraDesc = "known type ids are not statically known"; + } else { + extraDesc = "known type ids = " + extraDesc; + } + } else { + extraDesc = null; + } + throw ctxt.unknownTypeException(_baseType, typeId, extraDesc); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import com.fasterxml.jackson.databind.DatabindContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Partial base implementation of {@link TypeIdResolver}: all custom implementations + * are strongly recommended to extend this class, instead of directly + * implementing {@link TypeIdResolver}. + * Note that ALL sub-class need to re-implement + * {@link #typeFromId(DatabindContext, String)} method; otherwise implementation + * will not work. + *

+ * Note that instances created to be constructed from annotations + * ({@link com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver}) + * are always created using no-arguments constructor; protected constructor + * is only used sub-classes. + */ +public abstract class TypeIdResolverBase + implements TypeIdResolver +{ + protected final TypeFactory _typeFactory; + + /** + * Common base type for all polymorphic instances handled. + */ + protected final JavaType _baseType; + + protected TypeIdResolverBase() { + this(null, null); + } + + protected TypeIdResolverBase(JavaType baseType, TypeFactory typeFactory) { + _baseType = baseType; + _typeFactory = typeFactory; + } + + // Standard type id resolvers do not need this: only useful for custom ones. + @Override + public void init(JavaType bt) { } + + @Override + public String idFromBaseType() { + /* By default we will just defer to regular handling, handing out the + * base type; and since there is no value, must just pass null here + * assuming that implementations can deal with it. + * Alternative would be to pass a bogus Object, but that does not seem right. + */ + return idFromValueAndType(null, _baseType.getRawClass()); + } + + /** + * @deprecated Since 2.3, override {@link #typeFromId(DatabindContext, String)} instead + * to get access to contextual information + */ + @Deprecated + @Override + public JavaType typeFromId(String id) { + return typeFromId(null, id); + } + + /** + * New method, replacement for {@link #typeFromId(String)}, which is given + * context for accessing information, including configuration and + * {@link TypeFactory}. + * + * @return Type for given id + * + * @since 2.3 + */ + @Override + public JavaType typeFromId(DatabindContext context, String id) { + // 22-Dec-2015, tatu: Must be overridden by sub-classes, so let's throw + // an exception if not + throw new IllegalStateException("Sub-class "+getClass().getName()+" MUST implement " + +"`typeFromId(DatabindContext,String)"); + } + + /** + * Helper method used to get a simple description of all known type ids, + * for use in error messages. + */ + @Override + public String getDescForKnownTypeIds() { + return null; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,176 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DatabindContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.jsontype.NamedType; + +public class TypeNameIdResolver extends TypeIdResolverBase +{ + protected final MapperConfig _config; + + /** + * Mappings from class name to type id, used for serialization + */ + protected final Map _typeToId; + + /** + * Mappings from type id to JavaType, used for deserialization + */ + protected final Map _idToType; + + protected TypeNameIdResolver(MapperConfig config, JavaType baseType, + Map typeToId, Map idToType) + { + super(baseType, config.getTypeFactory()); + _config = config; + _typeToId = typeToId; + _idToType = idToType; + } + + public static TypeNameIdResolver construct(MapperConfig config, JavaType baseType, + Collection subtypes, boolean forSer, boolean forDeser) + { + // sanity check + if (forSer == forDeser) throw new IllegalArgumentException(); + Map typeToId = null; + Map idToType = null; + + if (forSer) { + typeToId = new HashMap(); + } + if (forDeser) { + idToType = new HashMap(); + // 14-Apr-2016, tatu: Apparently needed for special case of `defaultImpl`; + // see [databind#1198] for details. + typeToId = new TreeMap(); + } + if (subtypes != null) { + for (NamedType t : subtypes) { + /* no name? Need to figure out default; for now, let's just + * use non-qualified class name + */ + Class cls = t.getType(); + String id = t.hasName() ? t.getName() : _defaultTypeId(cls); + if (forSer) { + typeToId.put(cls.getName(), id); + } + if (forDeser) { + // One more problem; sometimes we have same name for multiple types; + // if so, use most specific + JavaType prev = idToType.get(id); + if (prev != null) { // Can only override if more specific + if (cls.isAssignableFrom(prev.getRawClass())) { // nope, more generic (or same) + continue; + } + } + idToType.put(id, config.constructType(cls)); + } + } + } + return new TypeNameIdResolver(config, baseType, typeToId, idToType); + } + + @Override + public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.NAME; } + + @Override + public String idFromValue(Object value) + { + return idFromClass(value.getClass()); + } + + protected String idFromClass(Class clazz) + { + if (clazz == null) { + return null; + } + Class cls = _typeFactory.constructType(clazz).getRawClass(); + final String key = cls.getName(); + String name; + + synchronized (_typeToId) { + name = _typeToId.get(key); + if (name == null) { + // 24-Feb-2011, tatu: As per [JACKSON-498], may need to dynamically look up name + // can either throw an exception, or use default name... + if (_config.isAnnotationProcessingEnabled()) { + BeanDescription beanDesc = _config.introspectClassAnnotations(cls); + name = _config.getAnnotationIntrospector().findTypeName(beanDesc.getClassInfo()); + } + if (name == null) { + // And if still not found, let's choose default? + name = _defaultTypeId(cls); + } + _typeToId.put(key, name); + } + } + return name; + } + + @Override + public String idFromValueAndType(Object value, Class type) { + /* 18-Jan-2013, tatu: We may be called with null value occasionally + * it seems; nothing much we can figure out that way. + */ + if (value == null) { + return idFromClass(type); + } + return idFromValue(value); + } + + @Deprecated + @Override + public JavaType typeFromId(String id) { + return _typeFromId(id); + } + + @Override + public JavaType typeFromId(DatabindContext context, String id) { + return _typeFromId(id); + } + + protected JavaType _typeFromId(String id) { + /* Now: if no type is found, should we try to locate it by + * some other means? (specifically, if in same package as base type, + * could just try Class.forName) + * For now let's not add any such workarounds; can add if need be + */ + return _idToType.get(id); + } + + @Override + public String getDescForKnownTypeIds() { + return new TreeSet(_idToType.keySet()).toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('[').append(getClass().getName()); + sb.append("; id-to-type=").append(_idToType); + sb.append(']'); + return sb.toString(); + } + + /* + /********************************************************* + /* Helper methods + /********************************************************* + */ + + /** + * If no name was explicitly given for a class, we will just + * use non-qualified class name + */ + protected static String _defaultTypeId(Class cls) + { + String n = cls.getName(); + int ix = n.lastIndexOf('.'); + return (ix < 0) ? n : n.substring(ix+1); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,59 @@ +package com.fasterxml.jackson.databind.jsontype.impl; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +public abstract class TypeSerializerBase extends TypeSerializer +{ + protected final TypeIdResolver _idResolver; + + protected final BeanProperty _property; + + protected TypeSerializerBase(TypeIdResolver idRes, BeanProperty property) + { + _idResolver = idRes; + _property = property; + } + + @Override + public abstract JsonTypeInfo.As getTypeInclusion(); + + @Override + public String getPropertyName() { return null; } + + @Override + public TypeIdResolver getTypeIdResolver() { return _idResolver; } + + /* + /********************************************************** + /* Helper methods for subclasses + /********************************************************** + */ + + protected String idFromValue(Object value) { + String id = _idResolver.idFromValue(value); + if (id == null) { + handleMissingId(value); + } + return id; + } + + protected String idFromValueAndType(Object value, Class type) { + String id = _idResolver.idFromValueAndType(value, type); + if (id == null) { + handleMissingId(value); + } + return id; + } + + // As per [databind#633], maybe better just not do anything... + protected void handleMissingId(Object value) { + /* + String typeDesc = (value == null) ? "NULL" : value.getClass().getName(); + throw new IllegalArgumentException("Can not resolve type id for " + +typeDesc+" (using "+_idResolver.getClass().getName()+")"); + */ + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/impl/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,7 @@ +/** + * Package that contains standard implementations for + * {@link com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder} + * and + * {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver}. + */ +package com.fasterxml.jackson.databind.jsontype.impl; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/jsontype/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,8 @@ +/** + * Package that contains interfaces that define how to implement + * functionality for dynamically resolving type during deserialization. + * This is needed for complete handling of polymorphic types, where + * actual type can not be determined statically (declared type is + * a supertype of actual polymorphic serialized types). + */ +package com.fasterxml.jackson.databind.jsontype; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,95 @@ +package com.fasterxml.jackson.databind.module; + +import java.lang.reflect.Modifier; +import java.util.*; + +import com.fasterxml.jackson.databind.AbstractTypeResolver; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * Simple {@link AbstractTypeResolver} implementation, which is + * based on static mapping from abstract super types into + * sub types (concrete or abstract), but retaining generic + * parameterization. + * Can be used for things like specifying which implementation of + * {@link java.util.Collection} to use: + *

+ *  SimpleAbstractTypeResolver resolver = new SimpleAbstractTypeResolver();
+ *  // To make all properties declared as Collection, List, to LinkedList
+ *  resolver.addMapping(Collection.class, LinkedList.class);
+ *  resolver.addMapping(List.class, LinkedList.class);
+ *
+ * Can also be used as an alternative to per-class annotations when defining + * concrete implementations; however, only works with abstract types (since + * this is only called for abstract types) + */ +public class SimpleAbstractTypeResolver + extends AbstractTypeResolver + implements java.io.Serializable +{ + private static final long serialVersionUID = 8635483102371490919L; + + /** + * Mappings from super types to subtypes + */ + protected final HashMap> _mappings = new HashMap>(); + + /** + * Method for adding a mapping from super type to specific subtype. + * Arguments will be checked by method, to ensure that superType + * is abstract (since resolver is never called for concrete classes); + * as well as to ensure that there is supertype/subtype relationship + * (to ensure there won't be cycles during resolution). + * + * @param superType Abstract type to resolve + * @param subType Sub-class of superType, to map superTo to + * + * @return This resolver, to allow chaining of initializations + */ + public SimpleAbstractTypeResolver addMapping(Class superType, Class subType) + { + // Sanity checks, just in case someone tries to force typing... + if (superType == subType) { + throw new IllegalArgumentException("Can not add mapping from class to itself"); + } + if (!superType.isAssignableFrom(subType)) { + throw new IllegalArgumentException("Can not add mapping from class "+superType.getName() + +" to "+subType.getName()+", as latter is not a subtype of former"); + } + if (!Modifier.isAbstract(superType.getModifiers())) { + throw new IllegalArgumentException("Can not add mapping from class "+superType.getName() + +" since it is not abstract"); + } + _mappings.put(new ClassKey(superType), subType); + return this; + } + + @Override + public JavaType findTypeMapping(DeserializationConfig config, JavaType type) + { + // this is the main mapping base, so let's + Class src = type.getRawClass(); + Class dst = _mappings.get(new ClassKey(src)); + if (dst == null) { + return null; + } + // 09-Aug-2015, tatu: Instead of direct call via JavaType, better use TypeFactory + return config.getTypeFactory().constructSpecializedType(type, dst); + } + + @Override + public JavaType resolveAbstractType(DeserializationConfig config, JavaType type){ + // never materialize anything, so: + return null; + } + + @Override + public JavaType resolveAbstractType(DeserializationConfig config, + BeanDescription typeDesc) { + // never materialize anything, so: + return null; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,175 @@ +package com.fasterxml.jackson.databind.module; + +import java.util.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.Deserializers; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.type.*; + +/** + * Simple implementation {@link Deserializers} which allows registration of + * deserializers based on raw (type erased class). + * It can work well for basic bean and scalar type deserializers, but is not + * a good fit for handling generic types (like {@link Map}s and {@link Collection}s + * or array types). + *

+ * Unlike {@link SimpleSerializers}, this class does not currently support generic mappings; + * all mappings must be to exact declared deserialization type. + */ +public class SimpleDeserializers + implements Deserializers, java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected HashMap> _classMappings = null; + + /** + * Flag to help find "generic" enum deserializer, if one has been registered. + * + * @since 2.3 + */ + protected boolean _hasEnumDeserializer = false; + + /* + /********************************************************** + /* Life-cycle, construction and configuring + /********************************************************** + */ + + public SimpleDeserializers() { } + + /** + * @since 2.1 + */ + public SimpleDeserializers(Map,JsonDeserializer> desers) { + addDeserializers(desers); + } + + public void addDeserializer(Class forClass, JsonDeserializer deser) + { + ClassKey key = new ClassKey(forClass); + if (_classMappings == null) { + _classMappings = new HashMap>(); + } + _classMappings.put(key, deser); + // [Issue#227]: generic Enum deserializer? + if (forClass == Enum.class) { + _hasEnumDeserializer = true; + } + } + + /** + * @since 2.1 + */ + @SuppressWarnings("unchecked") + public void addDeserializers(Map,JsonDeserializer> desers) + { + for (Map.Entry,JsonDeserializer> entry : desers.entrySet()) { + Class cls = entry.getKey(); + // what a mess... nominal generics safety... + JsonDeserializer deser = (JsonDeserializer) entry.getValue(); + addDeserializer((Class) cls, deser); + } + } + + /* + /********************************************************** + /* Serializers implementation + /********************************************************** + */ + + @Override + public JsonDeserializer findArrayDeserializer(ArrayType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass())); + } + + @Override + public JsonDeserializer findBeanDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass())); + } + + @Override + public JsonDeserializer findCollectionDeserializer(CollectionType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass())); + } + + @Override + public JsonDeserializer findCollectionLikeDeserializer(CollectionLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass())); + } + + @Override + public JsonDeserializer findEnumDeserializer(Class type, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + if (_classMappings == null) { + return null; + } + JsonDeserializer deser = _classMappings.get(new ClassKey(type)); + if (deser == null) { + if (_hasEnumDeserializer && type.isEnum()) { + deser = _classMappings.get(new ClassKey(Enum.class)); + } + } + return deser; + } + + @Override + public JsonDeserializer findTreeNodeDeserializer(Class nodeType, + DeserializationConfig config, BeanDescription beanDesc) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(nodeType)); + } + + @Override + public JsonDeserializer findReferenceDeserializer(ReferenceType refType, + DeserializationConfig config, BeanDescription beanDesc, + TypeDeserializer contentTypeDeserializer, JsonDeserializer contentDeserializer) + throws JsonMappingException { + // 21-Oct-2015, tatu: Unlikely this will really get used (reference types need more + // work, simple registration probably not sufficient). But whatever. + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(refType.getRawClass())); + } + + @Override + public JsonDeserializer findMapDeserializer(MapType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass())); + } + + @Override + public JsonDeserializer findMapLikeDeserializer(MapLikeType type, + DeserializationConfig config, BeanDescription beanDesc, + KeyDeserializer keyDeserializer, + TypeDeserializer elementTypeDeserializer, + JsonDeserializer elementDeserializer) + throws JsonMappingException + { + return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass())); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.databind.module; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.KeyDeserializers; +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * Simple implementation {@link KeyDeserializers} which allows registration of + * deserializers based on raw (type erased class). + * It can work well for basic bean and scalar type deserializers, but is not + * a good fit for handling generic types (like {@link Map}s and {@link Collection}s + * or array types). + *

+ * Unlike {@link SimpleSerializers}, this class does not currently support generic mappings; + * all mappings must be to exact declared deserialization type. + */ +public class SimpleKeyDeserializers + implements KeyDeserializers, java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1L; + + protected HashMap _classMappings = null; + + /* + /********************************************************** + /* Life-cycle, construction and configuring + /********************************************************** + */ + + public SimpleKeyDeserializers() { } + + public SimpleKeyDeserializers addDeserializer(Class forClass, KeyDeserializer deser) + { + if (_classMappings == null) { + _classMappings = new HashMap(); + } + _classMappings.put(new ClassKey(forClass), deser); + return this; + } + + /* + /********************************************************** + /* Serializers implementation + /********************************************************** + */ + + @Override + public KeyDeserializer findKeyDeserializer(JavaType type, + DeserializationConfig config, BeanDescription beanDesc) + { + if (_classMappings == null) { + return null; + } + return _classMappings.get(new ClassKey(type.getRawClass())); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleModule.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleModule.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleModule.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,444 @@ +package com.fasterxml.jackson.databind.module; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; + +/** + * Vanilla {@link Module} implementation that allows registration + * of serializers and deserializers, bean serializer + * and deserializer modifiers, registration of subtypes and mix-ins + * as well as some other commonly + * needed aspects (addition of custom {@link AbstractTypeResolver}s, + * {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s). + *

+ * NOTE: although it is not expected that sub-types should need to + * override {@link #setupModule(SetupContext)} method, if they choose + * to do so they MUST call super.setupModule(context); + * to ensure that registration works as expected. + */ +public class SimpleModule + extends Module + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; // 2.5.0 + + protected final String _name; + protected final Version _version; + + protected SimpleSerializers _serializers = null; + protected SimpleDeserializers _deserializers = null; + + protected SimpleSerializers _keySerializers = null; + protected SimpleKeyDeserializers _keyDeserializers = null; + + /** + * Lazily-constructed resolver used for storing mappings from + * abstract classes to more specific implementing classes + * (which may be abstract or concrete) + */ + protected SimpleAbstractTypeResolver _abstractTypes = null; + + /** + * Lazily-constructed resolver used for storing mappings from + * abstract classes to more specific implementing classes + * (which may be abstract or concrete) + */ + protected SimpleValueInstantiators _valueInstantiators = null; + + /** + * @since 2.2 + */ + protected BeanDeserializerModifier _deserializerModifier = null; + + /** + * @since 2.2 + */ + protected BeanSerializerModifier _serializerModifier = null; + + /** + * Lazily-constructed map that contains mix-in definitions, indexed + * by target class, value being mix-in to apply. + */ + protected HashMap, Class> _mixins = null; + + /** + * Set of subtypes to register, if any. + */ + protected LinkedHashSet _subtypes = null; + + /** + * @since 2.3 + */ + protected PropertyNamingStrategy _namingStrategy = null; + + /* + /********************************************************** + /* Life-cycle: creation + /********************************************************** + */ + + /** + * Constructors that should only be used for non-reusable + * convenience modules used by app code: "real" modules should + * use actual name and version number information. + */ + public SimpleModule() { + // can't chain when making reference to 'this' + // note: generate different name for direct instantiation, sub-classing + _name = (getClass() == SimpleModule.class) ? + "SimpleModule-"+System.identityHashCode(this) + : getClass().getName(); + _version = Version.unknownVersion(); + } + + /** + * Convenience constructor that will default version to + * {@link Version#unknownVersion()}. + */ + public SimpleModule(String name) { + this(name, Version.unknownVersion()); + } + + /** + * Convenience constructor that will use specified Version, + * including name from {@link Version#getArtifactId()} + */ + public SimpleModule(Version version) { + _name = version.getArtifactId(); + _version = version; + } + + /** + * Constructor to use for actual reusable modules. + * ObjectMapper may use name as identifier to notice attempts + * for multiple registrations of the same module (although it + * does not have to). + * + * @param name Unique name of the module + * @param version Version of the module + */ + public SimpleModule(String name, Version version) { + _name = name; + _version = version; + } + + /** + * @since 2.1 + */ + public SimpleModule(String name, Version version, + Map,JsonDeserializer> deserializers) { + this(name, version, deserializers, null); + } + + /** + * @since 2.1 + */ + public SimpleModule(String name, Version version, + List> serializers) { + this(name, version, null, serializers); + } + + /** + * @since 2.1 + */ + public SimpleModule(String name, Version version, + Map,JsonDeserializer> deserializers, + List> serializers) + { + _name = name; + _version = version; + if (deserializers != null) { + _deserializers = new SimpleDeserializers(deserializers); + } + if (serializers != null) { + _serializers = new SimpleSerializers(serializers); + } + } + + /** + * Since instances are likely to be custom, implementation returns + * null if (but only if!) this class is directly instantiated; + * but class name (default impl) for sub-classes. + */ + @Override + public Object getTypeId() { + if (getClass() == SimpleModule.class) { + return null; + } + return super.getTypeId(); + } + + /* + /********************************************************** + /* Simple setters to allow overriding + /********************************************************** + */ + + /** + * Resets all currently configured serializers. + */ + public void setSerializers(SimpleSerializers s) { + _serializers = s; + } + + /** + * Resets all currently configured deserializers. + */ + public void setDeserializers(SimpleDeserializers d) { + _deserializers = d; + } + + /** + * Resets all currently configured key serializers. + */ + public void setKeySerializers(SimpleSerializers ks) { + _keySerializers = ks; + } + + /** + * Resets all currently configured key deserializers. + */ + public void setKeyDeserializers(SimpleKeyDeserializers kd) { + _keyDeserializers = kd; + } + + /** + * Resets currently configured abstract type mappings + */ + public void setAbstractTypes(SimpleAbstractTypeResolver atr) { + _abstractTypes = atr; + } + + /** + * Resets all currently configured value instantiators + */ + public void setValueInstantiators(SimpleValueInstantiators svi) { + _valueInstantiators = svi; + } + + /** + * @since 2.2 + */ + public SimpleModule setDeserializerModifier(BeanDeserializerModifier mod) { + _deserializerModifier = mod; + return this; + } + + /** + * @since 2.2 + */ + public SimpleModule setSerializerModifier(BeanSerializerModifier mod) { + _serializerModifier = mod; + return this; + } + + /** + * @since 2.3 + */ + protected SimpleModule setNamingStrategy(PropertyNamingStrategy naming) { + _namingStrategy = naming; + return this; + } + + /* + /********************************************************** + /* Configuration methods + /********************************************************** + */ + + public SimpleModule addSerializer(JsonSerializer ser) + { + if (_serializers == null) { + _serializers = new SimpleSerializers(); + } + _serializers.addSerializer(ser); + return this; + } + + public SimpleModule addSerializer(Class type, JsonSerializer ser) + { + if (_serializers == null) { + _serializers = new SimpleSerializers(); + } + _serializers.addSerializer(type, ser); + return this; + } + + public SimpleModule addKeySerializer(Class type, JsonSerializer ser) + { + if (_keySerializers == null) { + _keySerializers = new SimpleSerializers(); + } + _keySerializers.addSerializer(type, ser); + return this; + } + + public SimpleModule addDeserializer(Class type, JsonDeserializer deser) + { + if (_deserializers == null) { + _deserializers = new SimpleDeserializers(); + } + _deserializers.addDeserializer(type, deser); + return this; + } + + public SimpleModule addKeyDeserializer(Class type, KeyDeserializer deser) + { + if (_keyDeserializers == null) { + _keyDeserializers = new SimpleKeyDeserializers(); + } + _keyDeserializers.addDeserializer(type, deser); + return this; + } + + /** + * Lazily-constructed resolver used for storing mappings from + * abstract classes to more specific implementing classes + * (which may be abstract or concrete) + */ + public SimpleModule addAbstractTypeMapping(Class superType, + Class subType) + { + if (_abstractTypes == null) { + _abstractTypes = new SimpleAbstractTypeResolver(); + } + // note: addMapping() will verify arguments + _abstractTypes = _abstractTypes.addMapping(superType, subType); + return this; + } + + /** + * Method for registering {@link ValueInstantiator} to use when deserializing + * instances of type beanType. + *

+ * Instantiator is + * registered when module is registered for ObjectMapper. + */ + public SimpleModule addValueInstantiator(Class beanType, ValueInstantiator inst) + { + if (_valueInstantiators == null) { + _valueInstantiators = new SimpleValueInstantiators(); + } + _valueInstantiators = _valueInstantiators.addValueInstantiator(beanType, inst); + return this; + } + + /** + * Method for adding set of subtypes to be registered with + * {@link ObjectMapper} + * this is an alternative to using annotations in super type to indicate subtypes. + */ + public SimpleModule registerSubtypes(Class ... subtypes) + { + if (_subtypes == null) { + _subtypes = new LinkedHashSet(Math.max(16, subtypes.length)); + } + for (Class subtype : subtypes) { + _subtypes.add(new NamedType(subtype)); + } + return this; + } + + /** + * Method for adding set of subtypes (along with type name to use) to be registered with + * {@link ObjectMapper} + * this is an alternative to using annotations in super type to indicate subtypes. + */ + public SimpleModule registerSubtypes(NamedType ... subtypes) + { + if (_subtypes == null) { + _subtypes = new LinkedHashSet(Math.max(16, subtypes.length)); + } + for (NamedType subtype : subtypes) { + _subtypes.add(subtype); + } + return this; + } + + /** + * Method for specifying that annotations define by mixinClass + * should be "mixed in" with annotations that targetType + * has (as if they were directly included on it!). + *

+ * Mix-in annotations are + * registered when module is registered for ObjectMapper. + */ + public SimpleModule setMixInAnnotation(Class targetType, Class mixinClass) + { + if (_mixins == null) { + _mixins = new HashMap, Class>(); + } + _mixins.put(targetType, mixinClass); + return this; + } + + /* + /********************************************************** + /* Module impl + /********************************************************** + */ + + @Override + public String getModuleName() { + return _name; + } + + /** + * Standard implementation handles registration of all configured + * customizations: it is important that sub-classes call this + * implementation (usually before additional custom logic) + * if they choose to override it; otherwise customizations + * will not be registered. + */ + @Override + public void setupModule(SetupContext context) + { + if (_serializers != null) { + context.addSerializers(_serializers); + } + if (_deserializers != null) { + context.addDeserializers(_deserializers); + } + if (_keySerializers != null) { + context.addKeySerializers(_keySerializers); + } + if (_keyDeserializers != null) { + context.addKeyDeserializers(_keyDeserializers); + } + if (_abstractTypes != null) { + context.addAbstractTypeResolver(_abstractTypes); + } + if (_valueInstantiators != null) { + context.addValueInstantiators(_valueInstantiators); + } + if (_deserializerModifier != null) { + context.addBeanDeserializerModifier(_deserializerModifier); + } + if (_serializerModifier != null) { + context.addBeanSerializerModifier(_serializerModifier); + } + if (_subtypes != null && _subtypes.size() > 0) { + context.registerSubtypes(_subtypes.toArray(new NamedType[_subtypes.size()])); + } + if (_namingStrategy != null) { + context.setNamingStrategy(_namingStrategy); + } + if (_mixins != null) { + for (Map.Entry,Class> entry : _mixins.entrySet()) { + context.setMixInAnnotations(entry.getKey(), entry.getValue()); + } + } + } + + @Override + public Version version() { return _version; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleSerializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleSerializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleSerializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,246 @@ +package com.fasterxml.jackson.databind.module; + +import java.util.*; + + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.type.ArrayType; +import com.fasterxml.jackson.databind.type.ClassKey; +import com.fasterxml.jackson.databind.type.CollectionLikeType; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.databind.type.MapLikeType; +import com.fasterxml.jackson.databind.type.MapType; + +/** + * Simple implementation {@link Serializers} which allows registration of + * serializers based on raw (type erased class). + * It can work well for basic bean and scalar type serializers, but is not + * a good fit for handling generic types (like {@link Map}s and {@link Collection}s). + *

+ * Type registrations are assumed to be general; meaning that registration of serializer + * for a super type will also be used for handling subtypes, unless an exact match + * is found first. As an example, handler for {@link CharSequence} would also be used + * serializing {@link StringBuilder} instances, unless a direct mapping was found. + */ +public class SimpleSerializers + extends Serializers.Base + implements java.io.Serializable +{ + private static final long serialVersionUID = 8531646511998456779L; + + /** + * Class-based mappings that are used both for exact and + * sub-class matches. + */ + protected HashMap> _classMappings = null; + + /** + * Interface-based matches. + */ + protected HashMap> _interfaceMappings = null; + + /** + * Flag to help find "generic" enum serializer, if one has been registered. + * + * @since 2.3 + */ + protected boolean _hasEnumSerializer = false; + + /* + /********************************************************** + /* Life-cycle, construction and configuring + /********************************************************** + */ + + public SimpleSerializers() { } + + /** + * @since 2.1 + */ + public SimpleSerializers(List> sers) { + addSerializers(sers); + } + + /** + * Method for adding given serializer for type that {@link JsonSerializer#handledType} + * specifies (which MUST return a non-null class; and can NOT be {@link Object}, as a + * sanity check). + * For serializers that do not declare handled type, use the variant that takes + * two arguments. + * + * @param ser + */ + public void addSerializer(JsonSerializer ser) + { + // Interface to match? + Class cls = ser.handledType(); + if (cls == null || cls == Object.class) { + throw new IllegalArgumentException("JsonSerializer of type "+ser.getClass().getName() + +" does not define valid handledType() -- must either register with method that takes type argument " + +" or make serializer extend 'com.fasterxml.jackson.databind.ser.std.StdSerializer'"); + } + _addSerializer(cls, ser); + } + + public void addSerializer(Class type, JsonSerializer ser) + { + _addSerializer(type, ser); + } + + /** + * @since 2.1 + */ + public void addSerializers(List> sers) { + for (JsonSerializer ser : sers) { + addSerializer(ser); + } + } + + /* + /********************************************************** + /* Serializers implementation + /********************************************************** + */ + + @Override + public JsonSerializer findSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc) + { + Class cls = type.getRawClass(); + ClassKey key = new ClassKey(cls); + JsonSerializer ser = null; + + // First: direct match? + if (cls.isInterface()) { + if (_interfaceMappings != null) { + ser = _interfaceMappings.get(key); + if (ser != null) { + return ser; + } + } + } else { + if (_classMappings != null) { + ser = _classMappings.get(key); + if (ser != null) { + return ser; + } + + // [Issue#227]: Handle registration of plain `Enum` serializer + if (_hasEnumSerializer && type.isEnumType()) { + key.reset(Enum.class); + ser = _classMappings.get(key); + if (ser != null) { + return ser; + } + } + + // If not direct match, maybe super-class match? + for (Class curr = cls; (curr != null); curr = curr.getSuperclass()) { + key.reset(curr); + ser = _classMappings.get(key); + if (ser != null) { + return ser; + } + } + } + } + // No direct match? How about super-interfaces? + if (_interfaceMappings != null) { + ser = _findInterfaceMapping(cls, key); + if (ser != null) { + return ser; + } + // still no matches? Maybe interfaces of super classes + if (!cls.isInterface()) { + while ((cls = cls.getSuperclass()) != null) { + ser = _findInterfaceMapping(cls, key); + if (ser != null) { + return ser; + } + } + } + } + return null; + } + + @Override + public JsonSerializer findArraySerializer(SerializationConfig config, + ArrayType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return findSerializer(config, type, beanDesc); + } + + @Override + public JsonSerializer findCollectionSerializer(SerializationConfig config, + CollectionType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return findSerializer(config, type, beanDesc); + } + + @Override + public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, + CollectionLikeType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return findSerializer(config, type, beanDesc); + } + + @Override + public JsonSerializer findMapSerializer(SerializationConfig config, + MapType type, BeanDescription beanDesc, + JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return findSerializer(config, type, beanDesc); + } + + @Override + public JsonSerializer findMapLikeSerializer(SerializationConfig config, + MapLikeType type, BeanDescription beanDesc, + JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return findSerializer(config, type, beanDesc); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected JsonSerializer _findInterfaceMapping(Class cls, ClassKey key) + { + for (Class iface : cls.getInterfaces()) { + key.reset(iface); + JsonSerializer ser = _interfaceMappings.get(key); + if (ser != null) { + return ser; + } + ser = _findInterfaceMapping(iface, key); + if (ser != null) { + return ser; + } + } + return null; + } + + protected void _addSerializer(Class cls, JsonSerializer ser) + { + ClassKey key = new ClassKey(cls); + // Interface or class type? + if (cls.isInterface()) { + if (_interfaceMappings == null) { + _interfaceMappings = new HashMap>(); + } + _interfaceMappings.put(key, ser); + } else { // nope, class: + if (_classMappings == null) { + _classMappings = new HashMap>(); + } + _classMappings.put(key, ser); + if (cls == Enum.class) { + _hasEnumSerializer = true; + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleValueInstantiators.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleValueInstantiators.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/SimpleValueInstantiators.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.databind.module; + +import java.util.HashMap; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.deser.ValueInstantiator; +import com.fasterxml.jackson.databind.deser.ValueInstantiators; +import com.fasterxml.jackson.databind.type.ClassKey; + +public class SimpleValueInstantiators + extends ValueInstantiators.Base + implements java.io.Serializable +{ + private static final long serialVersionUID = -8929386427526115130L; + + /** + * Mappings from raw (type-erased, i.e. non-generic) types + * to matching {@link ValueInstantiator} instances. + */ + protected HashMap _classMappings; + + /* + /********************************************************** + /* Life-cycle, construction and configuring + /********************************************************** + */ + + public SimpleValueInstantiators() + { + _classMappings = new HashMap(); + } + + public SimpleValueInstantiators addValueInstantiator(Class forType, + ValueInstantiator inst) + { + _classMappings.put(new ClassKey(forType), inst); + return this; + } + + @Override + public ValueInstantiator findValueInstantiator(DeserializationConfig config, + BeanDescription beanDesc, ValueInstantiator defaultInstantiator) + { + ValueInstantiator inst = _classMappings.get(new ClassKey(beanDesc.getBeanClass())); + return (inst == null) ? defaultInstantiator : inst; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/module/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,14 @@ +/** + * Package that contains classes and interfaces to help implement + * custom extension {@link com.fasterxml.jackson.databind.Module}s + * (which are registered using + * {@link com.fasterxml.jackson.databind.ObjectMapper#registerModule}. + *

+ * Note that classes in the package only support registering + * handlers for non-generic types (types without type + * parameterization) -- hence "simple" -- which works for + * many cases, but not all. So if you will need to register + * handlers for generic types, you will usually need to either + * sub-class handlers, or implement/extend base types directly. + */ +package com.fasterxml.jackson.databind.module; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ArrayNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ArrayNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ArrayNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,839 @@ +package com.fasterxml.jackson.databind.node; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.util.RawValue; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * Node class that represents Arrays mapped from JSON content. + *

+ * Note: class was final temporarily for Jackson 2.2. + */ +public class ArrayNode + extends ContainerNode +{ + private final List _children; + + public ArrayNode(JsonNodeFactory nf) { + super(nf); + _children = new ArrayList(); + } + + /** + * @since 2.7 + */ + public ArrayNode(JsonNodeFactory nf, List children) { + super(nf); + _children = children; + } + + @Override + protected JsonNode _at(JsonPointer ptr) { + return get(ptr.getMatchingIndex()); + } + + // note: co-variant to allow caller-side type safety + @SuppressWarnings("unchecked") + @Override + public ArrayNode deepCopy() + { + ArrayNode ret = new ArrayNode(_nodeFactory); + + for (JsonNode element: _children) + ret._children.add(element.deepCopy()); + + return ret; + } + + /* + /********************************************************** + /* Overrides for JsonSerializable.Base + /********************************************************** + */ + + @Override + public boolean isEmpty(SerializerProvider serializers) { + return _children.isEmpty(); + } + + /* + /********************************************************** + /* Implementation of core JsonNode API + /********************************************************** + */ + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.ARRAY; + } + + @Override public JsonToken asToken() { return JsonToken.START_ARRAY; } + + @Override + public int size() { + return _children.size(); + } + + @Override + public Iterator elements() { + return _children.iterator(); + } + + @Override + public JsonNode get(int index) { + if (index >= 0 && index < _children.size()) { + return _children.get(index); + } + return null; + } + + @Override + public JsonNode get(String fieldName) { return null; } + + @Override + public JsonNode path(String fieldName) { return MissingNode.getInstance(); } + + @Override + public JsonNode path(int index) { + if (index >= 0 && index < _children.size()) { + return _children.get(index); + } + return MissingNode.getInstance(); + } + + @Override + public boolean equals(Comparator comparator, JsonNode o) + { + if (!(o instanceof ArrayNode)) { + return false; + } + ArrayNode other = (ArrayNode) o; + final int len = _children.size(); + if (other.size() != len) { + return false; + } + List l1 = _children; + List l2 = other._children; + for (int i = 0; i < len; ++i) { + if (!l1.get(i).equals(comparator, l2.get(i))) { + return false; + } + } + return true; + } + + /* + /********************************************************** + /* Public API, serialization + /********************************************************** + */ + + @Override + public void serialize(JsonGenerator f, SerializerProvider provider) throws IOException + { + final List c = _children; + final int size = c.size(); + f.writeStartArray(size); + for (int i = 0; i < size; ++i) { // we'll typically have array list + // For now, assuming it's either BaseJsonNode, JsonSerializable + JsonNode n = c.get(i); + if (n instanceof BaseJsonNode) { + ((BaseJsonNode) n).serialize(f, provider); + } else { + ((JsonSerializable) n).serialize(f, provider); + } + } + f.writeEndArray(); + } + + @Override + public void serializeWithType(JsonGenerator jg, SerializerProvider provider, TypeSerializer typeSer) + throws IOException + { + typeSer.writeTypePrefixForArray(this, jg); + for (JsonNode n : _children) { + ((BaseJsonNode)n).serialize(jg, provider); + } + typeSer.writeTypeSuffixForArray(this, jg); + } + + /* + /********************************************************** + /* Public API, finding value nodes + /********************************************************** + */ + + @Override + public JsonNode findValue(String fieldName) + { + for (JsonNode node : _children) { + JsonNode value = node.findValue(fieldName); + if (value != null) { + return value; + } + } + return null; + } + + @Override + public List findValues(String fieldName, List foundSoFar) + { + for (JsonNode node : _children) { + foundSoFar = node.findValues(fieldName, foundSoFar); + } + return foundSoFar; + } + + @Override + public List findValuesAsText(String fieldName, List foundSoFar) + { + for (JsonNode node : _children) { + foundSoFar = node.findValuesAsText(fieldName, foundSoFar); + } + return foundSoFar; + } + + @Override + public ObjectNode findParent(String fieldName) + { + for (JsonNode node : _children) { + JsonNode parent = node.findParent(fieldName); + if (parent != null) { + return (ObjectNode) parent; + } + } + return null; + } + + @Override + public List findParents(String fieldName, List foundSoFar) + { + for (JsonNode node : _children) { + foundSoFar = node.findParents(fieldName, foundSoFar); + } + return foundSoFar; + } + + /* + /********************************************************** + /* Extended ObjectNode API, accessors + /********************************************************** + */ + + /** + * Method that will set specified field, replacing old value, + * if any. + * + * @param value to set field to; if null, will be converted + * to a {@link NullNode} first (to remove field entry, call + * {@link #remove} instead) + * + * @return Old value of the field, if any; null if there was no + * old value. + */ + public JsonNode set(int index, JsonNode value) + { + if (value == null) { // let's not store 'raw' nulls but nodes + value = nullNode(); + } + if (index < 0 || index >= _children.size()) { + throw new IndexOutOfBoundsException("Illegal index "+ index +", array size "+size()); + } + return _children.set(index, value); + } + + /** + * Method for adding specified node at the end of this array. + * + * @return This node, to allow chaining + */ + public ArrayNode add(JsonNode value) + { + if (value == null) { // let's not store 'raw' nulls but nodes + value = nullNode(); + } + _add(value); + return this; + } + + /** + * Method for adding all child nodes of given Array, appending to + * child nodes this array contains + * + * @param other Array to add contents from + * + * @return This node (to allow chaining) + */ + public ArrayNode addAll(ArrayNode other) + { + _children.addAll(other._children); + return this; + } + + /** + * Method for adding given nodes as child nodes of this array node. + * + * @param nodes Nodes to add + * + * @return This node (to allow chaining) + */ + public ArrayNode addAll(Collection nodes) + { + _children.addAll(nodes); + return this; + } + + /** + * Method for inserting specified child node as an element + * of this Array. If index is 0 or less, it will be inserted as + * the first element; if >= size(), appended at the end, and otherwise + * inserted before existing element in specified index. + * No exceptions are thrown for any index. + * + * @return This node (to allow chaining) + */ + public ArrayNode insert(int index, JsonNode value) + { + if (value == null) { + value = nullNode(); + } + _insert(index, value); + return this; + } + + /** + * Method for removing an entry from this ArrayNode. + * Will return value of the entry at specified index, if entry existed; + * null if not. + * + * @return Node removed, if any; null if none + */ + public JsonNode remove(int index) + { + if (index >= 0 && index < _children.size()) { + return _children.remove(index); + } + return null; + } + + /** + * Method for removing all elements of this array, leaving the + * array empty. + * + * @return This node (to allow chaining) + */ + @Override + public ArrayNode removeAll() + { + _children.clear(); + return this; + } + + /* + /********************************************************** + /* Extended ObjectNode API, mutators, generic; addXxx()/insertXxx() + /********************************************************** + */ + + /** + * Method that will construct an ArrayNode and add it as a + * field of this ObjectNode, replacing old value, if any. + * + * @return Newly constructed ArrayNode + */ + public ArrayNode addArray() + { + ArrayNode n = arrayNode(); + _add(n); + return n; + } + + /** + * Method that will construct an ObjectNode and add it at the end + * of this array node. + * + * @return Newly constructed ObjectNode + */ + public ObjectNode addObject() + { + ObjectNode n = objectNode(); + _add(n); + return n; + } + + /** + * Method that will construct a POJONode and add it at the end + * of this array node. + * + * @return This array node, to allow chaining + */ + public ArrayNode addPOJO(Object value) + { + if (value == null) { + addNull(); + } else { + _add(pojoNode(value)); + } + return this; + } + + /** + * @return This array node, to allow chaining + * + * @since 2.6 + */ + public ArrayNode addRawValue(RawValue raw) { + if (raw == null) { + addNull(); + } else { + _add(rawValueNode(raw)); + } + return this; + } + + /** + * Method that will add a null value at the end of this array node. + * + * @return This array node, to allow chaining + */ + public ArrayNode addNull() + { + _add(nullNode()); + return this; + } + + /** + * Method for adding specified number at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(int v) { + _add(numberNode(v)); + return this; + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(Integer value) { + if (value == null) { + return addNull(); + } + return _add(numberNode(value.intValue())); + } + + /** + * Method for adding specified number at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(long v) { return _add(numberNode(v)); } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(Long value) { + if (value == null) { + return addNull(); + } + return _add(numberNode(value.longValue())); + } + + /** + * Method for adding specified number at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(float v) { + return _add(numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(Float value) { + if (value == null) { + return addNull(); + } + return _add(numberNode(value.floatValue())); + } + + /** + * Method for adding specified number at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(double v) { + return _add(numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(Double value) { + if (value == null) { + return addNull(); + } + return _add(numberNode(value.doubleValue())); + } + + /** + * Method for adding specified number at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(BigDecimal v) { + if (v == null) { + return addNull(); + } + return _add(numberNode(v)); + } + + /** + * Method for adding specified String value at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(String v) { + if (v == null) { + return addNull(); + } + return _add(textNode(v)); + } + + /** + * Method for adding specified boolean value at the end of this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(boolean v) { + return _add(booleanNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode add(Boolean value) { + if (value == null) { + return addNull(); + } + return _add(booleanNode(value.booleanValue())); + } + + /** + * Method for adding specified binary value at the end of this array + * (note: when serializing as JSON, will be output Base64 encoded) + * + * @return This array node, to allow chaining + */ + public ArrayNode add(byte[] v) { + if (v == null) { + return addNull(); + } + return _add(binaryNode(v)); + } + + /** + * Method for creating an array node, inserting it at the + * specified point in the array, + * and returning the newly created array + * (note: NOT 'this' array) + */ + public ArrayNode insertArray(int index) + { + ArrayNode n = arrayNode(); + _insert(index, n); + return n; + } + + /** + * Method for creating an {@link ObjectNode}, appending it at the end + * of this array, and returning the newly created node + * (note: NOT 'this' array) + * + * @return Newly constructed ObjectNode + */ + public ObjectNode insertObject(int index) + { + ObjectNode n = objectNode(); + _insert(index, n); + return n; + } + + /** + * Method that will construct a POJONode and + * insert it at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insertPOJO(int index, Object value) + { + if (value == null) { + return insertNull(index); + } + return _insert(index, pojoNode(value)); + } + + /** + * Method that will insert a null value + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insertNull(int index) + { + _insert(index, nullNode()); + return this; + } + + /** + * Method that will insert specified numeric value + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, int v) { + _insert(index, numberNode(v)); + return this; + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, Integer value) { + if (value == null) { + insertNull(index); + } else { + _insert(index, numberNode(value.intValue())); + } + return this; + } + + /** + * Method that will insert specified numeric value + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, long v) { + return _insert(index, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, Long value) { + if (value == null) { + return insertNull(index); + } + return _insert(index, numberNode(value.longValue())); + } + + /** + * Method that will insert specified numeric value + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, float v) { + return _insert(index, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, Float value) { + if (value == null) { + return insertNull(index); + } + return _insert(index, numberNode(value.floatValue())); + } + + /** + * Method that will insert specified numeric value + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, double v) { + return _insert(index, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, Double value) { + if (value == null) { + return insertNull(index); + } + return _insert(index, numberNode(value.doubleValue())); + } + + /** + * Method that will insert specified numeric value + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, BigDecimal v) { + if (v == null) { + return insertNull(index); + } + return _insert(index, numberNode(v)); + } + + /** + * Method that will insert specified String + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, String v) { + if (v == null) { + return insertNull(index); + } + return _insert(index, textNode(v)); + } + + /** + * Method that will insert specified String + * at specified position in this array. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, boolean v) { + return _insert(index, booleanNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, Boolean value) { + if (value == null) { + return insertNull(index); + } + return _insert(index, booleanNode(value.booleanValue())); + } + + /** + * Method that will insert specified binary value + * at specified position in this array + * (note: when written as JSON, will be Base64 encoded) + * + * @return This array node, to allow chaining + */ + public ArrayNode insert(int index, byte[] v) { + if (v == null) { + return insertNull(index); + } + return _insert(index, binaryNode(v)); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof ArrayNode) { + return _children.equals(((ArrayNode) o)._children); + } + return false; + } + + /** + * @since 2.3 + */ + protected boolean _childrenEqual(ArrayNode other) { + return _children.equals(other._children); + } + + @Override + public int hashCode() { + return _children.hashCode(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(16 + (size() << 4)); + sb.append('['); + for (int i = 0, len = _children.size(); i < len; ++i) { + if (i > 0) { + sb.append(','); + } + sb.append(_children.get(i).toString()); + } + sb.append(']'); + return sb.toString(); + } + + /* + /********************************************************** + /* Internal methods (overridable) + /********************************************************** + */ + + protected ArrayNode _add(JsonNode node) { + _children.add(node); + return this; + } + + protected ArrayNode _insert(int index, JsonNode node) + { + if (index < 0) { + _children.add(0, node); + } else if (index >= _children.size()) { + _children.add(node); + } else { + _children.add(index, node); + } + return this; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BaseJsonNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BaseJsonNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BaseJsonNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,101 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Abstract base class common to all standard {@link JsonNode} + * implementations. + * The main addition here is that we declare that sub-classes must + * implement {@link JsonSerializable}. + * This simplifies object mapping aspects a bit, as no external serializers are needed. + */ +public abstract class BaseJsonNode + extends JsonNode + implements JsonSerializable +{ + protected BaseJsonNode() { } + + /* + /********************************************************** + /* Basic definitions for non-container types + /********************************************************** + */ + + @Override + public final JsonNode findPath(String fieldName) + { + JsonNode value = findValue(fieldName); + if (value == null) { + return MissingNode.getInstance(); + } + return value; + } + + // Also, force (re)definition (2.7) + @Override public abstract int hashCode(); + + /* + /********************************************************** + /* Support for traversal-as-stream + /********************************************************** + */ + + @Override + public JsonParser traverse() { + return new TreeTraversingParser(this); + } + + @Override + public JsonParser traverse(ObjectCodec codec) { + return new TreeTraversingParser(this, codec); + } + + /** + * 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) + */ + @Override + public abstract JsonToken asToken(); + + /** + * Returns code that identifies type of underlying numeric + * value, if (and only if) node is a number node. + */ + @Override + public JsonParser.NumberType numberType() { + // most types non-numeric, so: + return null; + } + + /* + /********************************************************** + /* JsonSerializable + /********************************************************** + */ + + /** + * Method called to serialize node instances using given generator. + */ + @Override + public abstract void serialize(JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException; + + /** + * Type information is needed, even if JsonNode instances are "plain" JSON, + * since they may be mixed with other types. + */ + @Override + public abstract void serializeWithType(JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonProcessingException; +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BigIntegerNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BigIntegerNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BigIntegerNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,122 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * Numeric node that contains simple 64-bit integer values. + */ +public class BigIntegerNode + extends NumericNode +{ + private final static BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE); + private final static BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE); + private final static BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); + private final static BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + + final protected BigInteger _value; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public BigIntegerNode(BigInteger v) { _value = v; } + + public static BigIntegerNode valueOf(BigInteger v) { return new BigIntegerNode(v); } + + /* + /********************************************************** + /* Overrridden JsonNode methods + /********************************************************** + */ + + @Override + public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.BIG_INTEGER; } + + @Override + public boolean isIntegralNumber() { return true; } + + @Override + public boolean isBigInteger() { return true; } + + @Override public boolean canConvertToInt() { + return (_value.compareTo(MIN_INTEGER) >= 0) && (_value.compareTo(MAX_INTEGER) <= 0); + } + @Override public boolean canConvertToLong() { + return (_value.compareTo(MIN_LONG) >= 0) && (_value.compareTo(MAX_LONG) <= 0); + } + + @Override + public Number numberValue() { + return _value; + } + + @Override + public short shortValue() { return _value.shortValue(); } + + @Override + public int intValue() { return _value.intValue(); } + + @Override + public long longValue() { return _value.longValue(); } + + @Override + public BigInteger bigIntegerValue() { return _value; } + + @Override + public float floatValue() { return _value.floatValue(); } + + @Override + public double doubleValue() { return _value.doubleValue(); } + + @Override + public BigDecimal decimalValue() { return new BigDecimal(_value); } + + /* + /********************************************************** + /* General type coercions + /********************************************************** + */ + + @Override + public String asText() { + return _value.toString(); + } + + @Override + public boolean asBoolean(boolean defaultValue) { + return !BigInteger.ZERO.equals(_value); + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (!(o instanceof BigIntegerNode)) { + return false; + } + return ((BigIntegerNode) o)._value.equals(_value); + } + + @Override + public int hashCode() { + return _value.hashCode(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BinaryNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BinaryNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BinaryNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,123 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.util.Arrays; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * Value node that contains Base64 encoded binary value, which will be + * output and stored as Json String value. + */ +public class BinaryNode + extends ValueNode +{ + final static BinaryNode EMPTY_BINARY_NODE = new BinaryNode(new byte[0]); + + protected final byte[] _data; + + public BinaryNode(byte[] data) + { + _data = data; + } + + public BinaryNode(byte[] data, int offset, int length) + { + if (offset == 0 && length == data.length) { + _data = data; + } else { + _data = new byte[length]; + System.arraycopy(data, offset, _data, 0, length); + } + } + + public static BinaryNode valueOf(byte[] data) + { + if (data == null) { + return null; + } + if (data.length == 0) { + return EMPTY_BINARY_NODE; + } + return new BinaryNode(data); + } + + public static BinaryNode valueOf(byte[] data, int offset, int length) + { + if (data == null) { + return null; + } + if (length == 0) { + return EMPTY_BINARY_NODE; + } + return new BinaryNode(data, offset, length); + } + + @Override + public JsonNodeType getNodeType() + { + return JsonNodeType.BINARY; + } + + @Override + public JsonToken asToken() { + /* No distinct type; could use one for textual values, + * but given that it's not in text form at this point, + * embedded-object is closest + */ + return JsonToken.VALUE_EMBEDDED_OBJECT; + } + + /** + *

+ * Note: caller is not to modify returned array in any way, since + * it is not a copy but reference to the underlying byte array. + */ + @Override + public byte[] binaryValue() { return _data; } + + /** + * Hmmh. This is not quite as efficient as using {@link #serialize}, + * but will work correctly. + */ + @Override + public String asText() { + return Base64Variants.getDefaultVariant().encode(_data, false); + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeBinary(provider.getConfig().getBase64Variant(), + _data, 0, _data.length); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (!(o instanceof BinaryNode)) { + return false; + } + return Arrays.equals(((BinaryNode) o)._data, _data); + } + + @Override + public int hashCode() { + return (_data == null) ? -1 : _data.length; + } + + /** + * Different from other values, since contents need to be surrounded + * by (double) quotes. + */ + @Override + public String toString() + { + return Base64Variants.getDefaultVariant().encode(_data, true); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BooleanNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BooleanNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/BooleanNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,97 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * This concrete value class is used to contain boolean (true / false) + * values. Only two instances are ever created, to minimize memory + * usage. + */ +public class BooleanNode + extends ValueNode +{ + // // Just need two instances... + + public final static BooleanNode TRUE = new BooleanNode(true); + public final static BooleanNode FALSE = new BooleanNode(false); + + private final boolean _value; + + private BooleanNode(boolean v) { _value = v; } + + public static BooleanNode getTrue() { return TRUE; } + public static BooleanNode getFalse() { return FALSE; } + + public static BooleanNode valueOf(boolean b) { return b ? TRUE : FALSE; } + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.BOOLEAN; + } + + @Override public JsonToken asToken() { + return _value ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE; + } + + @Override + public boolean booleanValue() { + return _value; + } + + @Override + public String asText() { + return _value ? "true" : "false"; + } + + @Override + public boolean asBoolean() { + return _value; + } + + @Override + public boolean asBoolean(boolean defaultValue) { + return _value; + } + + @Override + public int asInt(int defaultValue) { + return _value ? 1 : 0; + } + @Override + public long asLong(long defaultValue) { + return _value ? 1L : 0L; + } + @Override + public double asDouble(double defaultValue) { + return _value ? 1.0 : 0.0; + } + + @Override + public final void serialize(JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeBoolean(_value); + } + + @Override + public int hashCode() { + return _value ? 3 : 1; + } + + @Override + public boolean equals(Object o) + { + /* 11-Mar-2013, tatu: Apparently ClassLoaders can manage to load + * different instances, rendering identity comparisons broken. + * So let's use value instead. + */ + if (o == this) return true; + if (o == null) return false; + if (!(o instanceof BooleanNode)) { + return false; + } + return (_value == ((BooleanNode) o)._value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ContainerNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ContainerNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ContainerNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,143 @@ +package com.fasterxml.jackson.databind.node; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.util.RawValue; + +/** + * This intermediate base class is used for all container nodes, + * specifically, array and object nodes. + */ +public abstract class ContainerNode> + extends BaseJsonNode + implements JsonNodeCreator +{ + /** + * We will keep a reference to the Object (usually TreeMapper) + * that can construct instances of nodes to add to this container + * node. + */ + protected final JsonNodeFactory _nodeFactory; + + protected ContainerNode(JsonNodeFactory nc) { + _nodeFactory = nc; + } + + // all containers are mutable: can't define: +// @Override public abstract T deepCopy(); + + @Override + public abstract JsonToken asToken(); + + @Override + public String asText() { return ""; } + + /* + /********************************************************** + /* Methods reset as abstract to force real implementation + /********************************************************** + */ + + @Override + public abstract int size(); + + @Override + public abstract JsonNode get(int index); + + @Override + public abstract JsonNode get(String fieldName); + + /* + /********************************************************** + /* JsonNodeCreator implementation, just dispatch to + /* the real creator + /********************************************************** + */ + + /** + * Factory method that constructs and returns an empty {@link ArrayNode} + * Construction is done using registered {@link JsonNodeFactory}. + */ + @Override + public final ArrayNode arrayNode() { return _nodeFactory.arrayNode(); } + + /** + * Factory method that constructs and returns an empty {@link ObjectNode} + * Construction is done using registered {@link JsonNodeFactory}. + */ + @Override + public final ObjectNode objectNode() { return _nodeFactory.objectNode(); } + + @Override + public final NullNode nullNode() { return _nodeFactory.nullNode(); } + + @Override + public final BooleanNode booleanNode(boolean v) { return _nodeFactory.booleanNode(v); } + + @Override + public final NumericNode numberNode(byte v) { return _nodeFactory.numberNode(v); } + @Override + public final NumericNode numberNode(short v) { return _nodeFactory.numberNode(v); } + @Override + public final NumericNode numberNode(int v) { return _nodeFactory.numberNode(v); } + @Override + public final NumericNode numberNode(long v) { + return _nodeFactory.numberNode(v); + } + + // was missing from 2.2 and before + @Override + public final NumericNode numberNode(BigInteger v) { return _nodeFactory.numberNode(v); } + + @Override + public final NumericNode numberNode(float v) { return _nodeFactory.numberNode(v); } + @Override + public final NumericNode numberNode(double v) { return _nodeFactory.numberNode(v); } + @Override + public final NumericNode numberNode(BigDecimal v) { return (_nodeFactory.numberNode(v)); } + + // // Wrapper types, missing from 2.2 and before + @Override + public final ValueNode numberNode(Byte v) { return _nodeFactory.numberNode(v); } + @Override + public final ValueNode numberNode(Short v) { return _nodeFactory.numberNode(v); } + @Override + public final ValueNode numberNode(Integer v) { return _nodeFactory.numberNode(v); } + @Override + public final ValueNode numberNode(Long v) { return _nodeFactory.numberNode(v); } + + @Override + public final ValueNode numberNode(Float v) { return _nodeFactory.numberNode(v); } + @Override + public final ValueNode numberNode(Double v) { return _nodeFactory.numberNode(v); } + + @Override + public final TextNode textNode(String text) { return _nodeFactory.textNode(text); } + + @Override + public final BinaryNode binaryNode(byte[] data) { return _nodeFactory.binaryNode(data); } + @Override + public final BinaryNode binaryNode(byte[] data, int offset, int length) { return _nodeFactory.binaryNode(data, offset, length); } + + @Override + public final ValueNode pojoNode(Object pojo) { return _nodeFactory.pojoNode(pojo); } + + @Override + public final ValueNode rawValueNode(RawValue value) { return _nodeFactory.rawValueNode(value); } + + /* + /********************************************************** + /* Common mutators + /********************************************************** + */ + + /** + * Method for removing all children container has (if any) + * + * @return Container node itself (to allow method call chaining) + */ + public abstract T removeAll(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/DecimalNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/DecimalNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/DecimalNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,126 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; + +/** + * Numeric node that contains values that do not fit in simple + * integer (int, long) or floating point (double) values. + */ +public class DecimalNode + extends NumericNode +{ + public static final DecimalNode ZERO = new DecimalNode(BigDecimal.ZERO); + + private final static BigDecimal MIN_INTEGER = BigDecimal.valueOf(Integer.MIN_VALUE); + private final static BigDecimal MAX_INTEGER = BigDecimal.valueOf(Integer.MAX_VALUE); + private final static BigDecimal MIN_LONG = BigDecimal.valueOf(Long.MIN_VALUE); + private final static BigDecimal MAX_LONG = BigDecimal.valueOf(Long.MAX_VALUE); + + final protected BigDecimal _value; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public DecimalNode(BigDecimal v) { _value = v; } + + public static DecimalNode valueOf(BigDecimal d) { return new DecimalNode(d); } + + /* + /********************************************************** + /* BaseJsonNode extended API + /********************************************************** + */ + + @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_FLOAT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.BIG_DECIMAL; } + + /* + /********************************************************** + /* Overrridden JsonNode methods + /********************************************************** + */ + + @Override + public boolean isFloatingPointNumber() { return true; } + + @Override + public boolean isBigDecimal() { return true; } + + @Override public boolean canConvertToInt() { + return (_value.compareTo(MIN_INTEGER) >= 0) && (_value.compareTo(MAX_INTEGER) <= 0); + } + @Override public boolean canConvertToLong() { + return (_value.compareTo(MIN_LONG) >= 0) && (_value.compareTo(MAX_LONG) <= 0); + } + + @Override + public Number numberValue() { return _value; } + + @Override + public short shortValue() { return _value.shortValue(); } + + @Override + public int intValue() { return _value.intValue(); } + + @Override + public long longValue() { return _value.longValue(); } + + + @Override + public BigInteger bigIntegerValue() { return _value.toBigInteger(); } + + @Override + public float floatValue() { return _value.floatValue(); } + + @Override + public double doubleValue() { return _value.doubleValue(); } + + @Override + public BigDecimal decimalValue() { return _value; } + + @Override + public String asText() { + return _value.toString(); + } + + @Override + public final void serialize(JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException + { + // 07-Jul-2013, tatu: Should be handled by propagating setting to JsonGenerator + // so this should not be needed: + /* + if (provider.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN)) { + if (!(jgen instanceof TokenBuffer)) { // [Issue#232] + jgen.writeNumber(((BigDecimal) _value).toPlainString()); + return; + } + } + */ + jgen.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof DecimalNode) { + return ((DecimalNode) o)._value.compareTo(_value) == 0; + } + return false; + } + + @Override + public int hashCode() { return Double.valueOf(doubleValue()).hashCode(); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/DoubleNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/DoubleNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/DoubleNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,123 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.NumberOutput; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * Numeric node that contains 64-bit ("double precision") + * floating point values simple 32-bit integer values. + */ +public class DoubleNode + extends NumericNode +{ + protected final double _value; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public DoubleNode(double v) { _value = v; } + + public static DoubleNode valueOf(double v) { return new DoubleNode(v); } + + /* + /********************************************************** + /* BaseJsonNode extended API + /********************************************************** + */ + + @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_FLOAT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.DOUBLE; } + + /* + /********************************************************** + /* Overrridden JsonNode methods + /********************************************************** + */ + + @Override + public boolean isFloatingPointNumber() { return true; } + + @Override + public boolean isDouble() { return true; } + + @Override public boolean canConvertToInt() { + return (_value >= Integer.MIN_VALUE && _value <= Integer.MAX_VALUE); + } + @Override public boolean canConvertToLong() { + return (_value >= Long.MIN_VALUE && _value <= Long.MAX_VALUE); + } + + @Override + public Number numberValue() { + return Double.valueOf(_value); + } + + @Override + public short shortValue() { return (short) _value; } + + @Override + public int intValue() { return (int) _value; } + + @Override + public long longValue() { return (long) _value; } + + @Override + public float floatValue() { return (float) _value; } + + @Override + public double doubleValue() { return _value; } + + @Override + public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); } + + @Override + public BigInteger bigIntegerValue() { + return decimalValue().toBigInteger(); + } + + @Override + public String asText() { + return NumberOutput.toString(_value); + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof DoubleNode) { + // We must account for NaNs: NaN does not equal NaN, therefore we have + // to use Double.compare(). + final double otherValue = ((DoubleNode) o)._value; + return Double.compare(_value, otherValue) == 0; + } + return false; + } + + @Override + public int hashCode() + { + // same as hashCode Double.class uses + long l = Double.doubleToLongBits(_value); + return ((int) l) ^ (int) (l >> 32); + + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/FloatNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/FloatNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/FloatNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,121 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * JsonNode implementation for efficiently containing 32-bit + * `float` values. + * + * @since 2.2 + */ +public class FloatNode extends NumericNode +{ + protected final float _value; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public FloatNode(float v) { _value = v; } + + public static FloatNode valueOf(float v) { return new FloatNode(v); } + + /* + /********************************************************** + /* BaseJsonNode extended API + /********************************************************** + */ + + @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_FLOAT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.FLOAT; } + + /* + /********************************************************** + /* Overrridden JsonNode methods + /********************************************************** + */ + + @Override + public boolean isFloatingPointNumber() { return true; } + + @Override + public boolean isFloat() { return true; } + + @Override public boolean canConvertToInt() { + return (_value >= Integer.MIN_VALUE && _value <= Integer.MAX_VALUE); + } + + @Override public boolean canConvertToLong() { + return (_value >= Long.MIN_VALUE && _value <= Long.MAX_VALUE); + } + + @Override + public Number numberValue() { + return Float.valueOf(_value); + } + + @Override + public short shortValue() { return (short) _value; } + + @Override + public int intValue() { return (int) _value; } + + @Override + public long longValue() { return (long) _value; } + + @Override + public float floatValue() { return (float) _value; } + + @Override + public double doubleValue() { return _value; } + + @Override + public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); } + + @Override + public BigInteger bigIntegerValue() { + return decimalValue().toBigInteger(); + } + + @Override + public String asText() { + // As per [jackson-databind#707] +// return NumberOutput.toString(_value); + // TODO: in 2.7, call `NumberOutput.toString (added in 2.6); not yet for backwards compat + return Float.toString(_value); + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) throws IOException + { + jg.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof FloatNode) { + // We must account for NaNs: NaN does not equal NaN, therefore we have + // to use Double.compare(). + final float otherValue = ((FloatNode) o)._value; + return Float.compare(_value, otherValue) == 0; + } + return false; + } + + @Override + public int hashCode() { + return Float.floatToIntBits(_value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/IntNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/IntNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/IntNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,133 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.NumberOutput; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * Numeric node that contains simple 32-bit integer values. + */ +public class IntNode + extends NumericNode +{ + // // // Let's cache small set of common value + + final static int MIN_CANONICAL = -1; + final static int MAX_CANONICAL = 10; + + private final static IntNode[] CANONICALS; + static { + int count = MAX_CANONICAL - MIN_CANONICAL + 1; + CANONICALS = new IntNode[count]; + for (int i = 0; i < count; ++i) { + CANONICALS[i] = new IntNode(MIN_CANONICAL + i); + } + } + + /** + * Integer value this node contains + */ + protected final int _value; + + /* + ************************************************ + * Construction + ************************************************ + */ + + public IntNode(int v) { _value = v; } + + public static IntNode valueOf(int i) { + if (i > MAX_CANONICAL || i < MIN_CANONICAL) return new IntNode(i); + return CANONICALS[i - MIN_CANONICAL]; + } + + /* + /********************************************************** + /* BaseJsonNode extended API + /********************************************************** + */ + + @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.INT; } + + /* + /********************************************************** + /* Overrridden JsonNode methods + /********************************************************** + */ + + @Override + public boolean isIntegralNumber() { return true; } + + @Override + public boolean isInt() { return true; } + + @Override public boolean canConvertToInt() { return true; } + @Override public boolean canConvertToLong() { return true; } + + @Override + public Number numberValue() { + return Integer.valueOf(_value); + } + + @Override + public short shortValue() { return (short) _value; } + + @Override + public int intValue() { return _value; } + + @Override + public long longValue() { return (long) _value; } + + @Override + public float floatValue() { return (float) _value; } + + @Override + public double doubleValue() { return (double) _value; } + + + @Override + public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); } + + @Override + public BigInteger bigIntegerValue() { return BigInteger.valueOf(_value); } + + @Override + public String asText() { + return NumberOutput.toString(_value); + } + + @Override + public boolean asBoolean(boolean defaultValue) { + return _value != 0; + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof IntNode) { + return ((IntNode) o)._value == _value; + } + return false; + } + + @Override + public int hashCode() { return _value; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeCreator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeCreator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeCreator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind.node; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.databind.util.RawValue; + +/** + * Interface that defines common "creator" functionality implemented + * both by {@link JsonNodeFactory} and {@link ContainerNode} (that is, + * JSON Object and Array nodes). + * + * @since 2.3 + */ +public interface JsonNodeCreator +{ + // Enumerated/singleton types + + public ValueNode booleanNode(boolean v); + public ValueNode nullNode(); + + // Numeric types + + public ValueNode numberNode(byte v); + public ValueNode numberNode(Byte value); + public ValueNode numberNode(short v); + public ValueNode numberNode(Short value); + public ValueNode numberNode(int v); + public ValueNode numberNode(Integer value); + public ValueNode numberNode(long v); + public ValueNode numberNode(Long value); + public ValueNode numberNode(BigInteger v); + public ValueNode numberNode(float v); + public ValueNode numberNode(Float value); + public ValueNode numberNode(double v); + public ValueNode numberNode(Double value); + public ValueNode numberNode(BigDecimal v); + + // Textual nodes + + public ValueNode textNode(String text); + + // Other value (non-structured) nodes + + public ValueNode binaryNode(byte[] data); + public ValueNode binaryNode(byte[] data, int offset, int length); + public ValueNode pojoNode(Object pojo); + + /** + * Factory method to use for adding "raw values"; pre-encoded values + * that are included exactly as-is when node is serialized. + * This may be used, for example, to include fully serialized JSON + * sub-trees. + * Note that the concept may not work with all backends, and since + * no translation of any kinds is done it will not work when converting + * between data formats. + * + * @since 2.6 + */ + public ValueNode rawValueNode(RawValue value); + + // Structured nodes: + // (bit unkosher, due to forward references... but has to do for now) + + public ArrayNode arrayNode(); + public ObjectNode objectNode(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,345 @@ +package com.fasterxml.jackson.databind.node; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.databind.util.RawValue; + +/** + * Base class that specifies methods for getting access to + * Node instances (newly constructed, or shared, depending + * on type), as well as basic implementation of the methods. + * Designed to be sub-classed if extended functionality (additions + * to behavior of node types, mostly) is needed. + */ +public class JsonNodeFactory + implements java.io.Serializable // since 2.1 + ,JsonNodeCreator // since 2.3 +{ + // with 2.2 + private static final long serialVersionUID = 1L; + + private final boolean _cfgBigDecimalExact; + + private static final JsonNodeFactory decimalsNormalized + = new JsonNodeFactory(false); + private static final JsonNodeFactory decimalsAsIs + = new JsonNodeFactory(true); + + /** + * Default singleton instance that construct "standard" node instances: + * given that this class is stateless, a globally shared singleton + * can be used. + */ + public final static JsonNodeFactory instance = decimalsNormalized; + + /** + * Main constructor + * + *

The only argument to this constructor is a boolean telling whether + * {@link DecimalNode} instances must be built with exact representations of + * {@link BigDecimal} instances.

+ * + *

This has quite an influence since, for instance, a BigDecimal (and, + * therefore, a DecimalNode) constructed from input string {@code "1.0"} and + * another constructed with input string {@code "1.00"} will not be + * equal, since their scale differs (1 in the first case, 2 in the second + * case).

+ * + *

Note that setting the argument to {@code true} does not + * guarantee a strict inequality between JSON representations: input texts + * {@code "0.1"} and {@code "1e-1"}, for instance, yield two equivalent + * BigDecimal instances since they have the same scale (1).

+ * + *

The no-arg constructor (and the default {@link #instance}) calls this + * constructor with {@code false} as an argument.

+ * + * @param bigDecimalExact see description + * + * @see BigDecimal + */ + public JsonNodeFactory(boolean bigDecimalExact) + { + _cfgBigDecimalExact = bigDecimalExact; + } + + /** + * Default constructor + * + *

This calls {@link #JsonNodeFactory(boolean)} with {@code false} + * as an argument.

+ */ + protected JsonNodeFactory() + { + this(false); + } + + /** + * Return a factory instance with the desired behavior for BigDecimals + *

See {@link #JsonNodeFactory(boolean)} for a full description.

+ * + * @param bigDecimalExact see description + * @return a factory instance + */ + public static JsonNodeFactory withExactBigDecimals(boolean bigDecimalExact) + { + return bigDecimalExact ? decimalsAsIs : decimalsNormalized; + } + + /* + /********************************************************** + /* Factory methods for literal values + /********************************************************** + */ + + /** + * Factory method for getting an instance of JSON boolean value + * (either literal 'true' or 'false') + */ + @Override + public BooleanNode booleanNode(boolean v) { + return v ? BooleanNode.getTrue() : BooleanNode.getFalse(); + } + + /** + * Factory method for getting an instance of JSON null node (which + * represents literal null value) + */ + @Override + public NullNode nullNode() { return NullNode.getInstance(); } + + /* + /********************************************************** + /* Factory methods for numeric values + /********************************************************** + */ + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given 8-bit value + */ + @Override + public NumericNode numberNode(byte v) { return IntNode.valueOf(v); } + + /** + * Alternate factory method that will handle wrapper value, which may + * be null. + * Due to possibility of null, returning type is not guaranteed to be + * {@link NumericNode}, but just {@link ValueNode}. + */ + @Override + public ValueNode numberNode(Byte value) { + return (value == null) ? nullNode() : IntNode.valueOf(value.intValue()); + } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given 16-bit integer value + */ + @Override + public NumericNode numberNode(short v) { return ShortNode.valueOf(v); } + + /** + * Alternate factory method that will handle wrapper value, which may + * be null. + * Due to possibility of null, returning type is not guaranteed to be + * {@link NumericNode}, but just {@link ValueNode}. + */ + @Override + public ValueNode numberNode(Short value) { + return (value == null) ? nullNode() : ShortNode.valueOf(value); + } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given 32-bit integer value + */ + @Override + public NumericNode numberNode(int v) { return IntNode.valueOf(v); } + + /** + * Alternate factory method that will handle wrapper value, which may + * be null. + * Due to possibility of null, returning type is not guaranteed to be + * {@link NumericNode}, but just {@link ValueNode}. + */ + @Override + public ValueNode numberNode(Integer value) { + return (value == null) ? nullNode() : IntNode.valueOf(value.intValue()); + } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given 64-bit integer value + */ + @Override + public NumericNode numberNode(long v) { + return LongNode.valueOf(v); + } + + /** + * Alternate factory method that will handle wrapper value, which may be null. + * Due to possibility of null, returning type is not guaranteed to be + * {@link NumericNode}, but just {@link ValueNode}. + */ + @Override + public ValueNode numberNode(Long value) { + if (value == null) { + return nullNode(); + } + return LongNode.valueOf(value.longValue()); + } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given unlimited range integer value + */ + @Override + public NumericNode numberNode(BigInteger v) { return BigIntegerNode.valueOf(v); } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given 32-bit floating point value + */ + @Override + public NumericNode numberNode(float v) { return FloatNode.valueOf((float) v); } + + /** + * Alternate factory method that will handle wrapper value, which may + * be null. + * Due to possibility of null, returning type is not guaranteed to be + * {@link NumericNode}, but just {@link ValueNode}. + */ + @Override + public ValueNode numberNode(Float value) { + return (value == null) ? nullNode() : FloatNode.valueOf(value.floatValue()); + } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given 64-bit floating point value + */ + @Override + public NumericNode numberNode(double v) { return DoubleNode.valueOf(v); } + + /** + * Alternate factory method that will handle wrapper value, which may + * be null. + * Due to possibility of null, returning type is not guaranteed to be + * {@link NumericNode}, but just {@link ValueNode}. + */ + @Override + public ValueNode numberNode(Double value) { + return (value == null) ? nullNode() : DoubleNode.valueOf(value.doubleValue()); + } + + /** + * Factory method for getting an instance of JSON numeric value + * that expresses given unlimited precision floating point value + * + *

In the event that the factory has been built to normalize decimal + * values, the BigDecimal argument will be stripped off its trailing zeroes, + * using {@link BigDecimal#stripTrailingZeros()}.

+ * + * @see #JsonNodeFactory(boolean) + */ + @Override + public NumericNode numberNode(BigDecimal v) + { + /* + * If the user wants the exact representation of this big decimal, + * return the value directly + */ + if (_cfgBigDecimalExact) + return DecimalNode.valueOf(v); + + /* + * If the user has asked to strip trailing zeroes, however, there is + * this bug to account for: + * + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6480539 + * + * In short: zeroes are never stripped out of 0! We therefore _have_ + * to compare with BigDecimal.ZERO... + */ + return v.compareTo(BigDecimal.ZERO) == 0 ? DecimalNode.ZERO + : DecimalNode.valueOf(v.stripTrailingZeros()); + } + + /* + /********************************************************** + /* Factory methods for textual values + /********************************************************** + */ + + /** + * Factory method for constructing a node that represents JSON + * String value + */ + @Override + public TextNode textNode(String text) { return TextNode.valueOf(text); } + + /** + * Factory method for constructing a node that represents given + * binary data, and will get serialized as equivalent base64-encoded + * String value + */ + @Override + public BinaryNode binaryNode(byte[] data) { return BinaryNode.valueOf(data); } + + /** + * Factory method for constructing a node that represents given + * binary data, and will get serialized as equivalent base64-encoded + * String value + */ + @Override + public BinaryNode binaryNode(byte[] data, int offset, int length) { + return BinaryNode.valueOf(data, offset, length); + } + + /* + /********************************************************** + /* Factory method for structured values + /********************************************************** + */ + + /** + * Factory method for constructing an empty JSON Array node + */ + @Override + public ArrayNode arrayNode() { return new ArrayNode(this); } + + /** + * Factory method for constructing an empty JSON Object ("struct") node + */ + @Override + public ObjectNode objectNode() { return new ObjectNode(this); } + + /** + * Factory method for constructing a wrapper for POJO + * ("Plain Old Java Object") objects; these will get serialized + * using data binding, usually as JSON Objects, but in some + * cases as JSON Strings or other node types. + */ + @Override + public ValueNode pojoNode(Object pojo) { return new POJONode(pojo); } + + @Override + public ValueNode rawValueNode(RawValue value) { + return new POJONode(value); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected boolean _inIntRange(long l) + { + int i = (int) l; + long l2 = (long) i; + return (l2 == l); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/JsonNodeType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind.node; + +/** + * Enumeration of JSON types. + * Covers all JSON types defined by RFC 4627 (array, boolean, + * null, number, object and string) but also Jackson-specific types: binary, + * missing and POJO; although does not distinguish between more granular + * types. + * + * @see BinaryNode + * @see MissingNode + * @see POJONode + * + * @since 2.2 + */ +public enum JsonNodeType +{ + ARRAY, + BINARY, + BOOLEAN, + MISSING, + NULL, + NUMBER, + OBJECT, + POJO, + STRING +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/LongNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/LongNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/LongNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,111 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.NumberOutput; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * Numeric node that contains simple 64-bit integer values. + */ +public class LongNode + extends NumericNode +{ + protected final long _value; + + /* + ************************************************ + * Construction + ************************************************ + */ + + public LongNode(long v) { _value = v; } + + public static LongNode valueOf(long l) { return new LongNode(l); } + + /* + ************************************************ + * Overrridden JsonNode methods + ************************************************ + */ + + @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.LONG; } + + + @Override + public boolean isIntegralNumber() { return true; } + + @Override + public boolean isLong() { return true; } + + @Override public boolean canConvertToInt() { + return (_value >= Integer.MIN_VALUE && _value <= Integer.MAX_VALUE); + } + @Override public boolean canConvertToLong() { return true; } + + @Override + public Number numberValue() { + return Long.valueOf(_value); + } + + @Override + public short shortValue() { return (short) _value; } + + @Override + public int intValue() { return (int) _value; } + + @Override + public long longValue() { return _value; } + + @Override + public float floatValue() { return _value; } + + @Override + public double doubleValue() { return _value; } + + @Override + public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); } + + @Override + public BigInteger bigIntegerValue() { return BigInteger.valueOf(_value); } + + @Override + public String asText() { + return NumberOutput.toString(_value); + } + + @Override + public boolean asBoolean(boolean defaultValue) { + return _value != 0; + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof LongNode) { + return ((LongNode) o)._value == _value; + } + return false; + } + + @Override + public int hashCode() { + return ((int) _value) ^ (int) (_value >> 32); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/MissingNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/MissingNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/MissingNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,102 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * This singleton node class is generated to denote "missing nodes" + * along paths that do not exist. For example, if a path via + * element of an array is requested for an element outside range + * of elements in the array; or for a non-array value, result + * will be reference to this node. + *

+ * In most respects this placeholder node will act as {@link NullNode}; + * for example, for purposes of value conversions, value is considered + * to be null and represented as value zero when used for numeric + * conversions. + */ +public final class MissingNode + extends ValueNode +{ + private final static MissingNode instance = new MissingNode(); + + private MissingNode() { } + + // Immutable: no need to copy + @SuppressWarnings("unchecked") + @Override + public T deepCopy() { return (T) this; } + + public static MissingNode getInstance() { return instance; } + + @Override + public JsonNodeType getNodeType() + { + return JsonNodeType.MISSING; + } + + @Override public JsonToken asToken() { return JsonToken.NOT_AVAILABLE; } + + @Override public String asText() { return ""; } + + @Override public String asText(String defaultValue) { return defaultValue; } + + // // Note: not a numeric node, hence default 'asXxx()' are fine: + + /* + public int asInt(int defaultValue); + public long asLong(long defaultValue); + public double asDouble(double defaultValue); + public boolean asBoolean(boolean defaultValue); + */ + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + /* Nothing to output... should we signal an error tho? + * Chances are, this is an erroneous call. For now, let's + * not do that; serialize as explicit null. Why? Because we + * can not just omit a value as JSON Object field name may have + * been written out. + */ + jg.writeNull(); + } + + @Override + public void serializeWithType(JsonGenerator g, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + g.writeNull(); + } + + @Override + public boolean equals(Object o) + { + /* Hmmh. Since there's just a singleton instance, this + * fails in all cases but with identity comparison. + * However: if this placeholder value was to be considered + * similar to SQL NULL, it shouldn't even equal itself? + * That might cause problems when dealing with collections + * like Sets... so for now, let's let identity comparison + * return true. + */ + return (o == this); + } + + @Override + public String toString() { + // toString() should never return null + return ""; + } + + @Override + public int hashCode() { + return JsonNodeType.MISSING.ordinal(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NodeCursor.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NodeCursor.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NodeCursor.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,248 @@ +package com.fasterxml.jackson.databind.node; + +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Helper class used by {@link TreeTraversingParser} to keep track + * of current location within traversed JSON tree. + */ +abstract class NodeCursor + extends JsonStreamContext +{ + /** + * Parent cursor of this cursor, if any; null for root + * cursors. + */ + protected final NodeCursor _parent; + + /** + * Current field name + */ + protected String _currentName; + + /** + * @since 2.5 + */ + protected java.lang.Object _currentValue; + + public NodeCursor(int contextType, NodeCursor p) + { + super(); + _type = contextType; + _index = -1; + _parent = p; + } + + /* + /********************************************************** + /* JsonStreamContext impl + /********************************************************** + */ + + // note: co-variant return type + @Override + public final NodeCursor getParent() { return _parent; } + + @Override + public final String getCurrentName() { + return _currentName; + } + + /** + * @since 2.0 + */ + public void overrideCurrentName(String name) { + _currentName = name; + } + + @Override + public java.lang.Object getCurrentValue() { + return _currentValue; + } + + @Override + public void setCurrentValue(java.lang.Object v) { + _currentValue = v; + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + public abstract JsonToken nextToken(); + public abstract JsonToken nextValue(); + public abstract JsonToken endToken(); + + public abstract JsonNode currentNode(); + public abstract boolean currentHasChildren(); + + /** + * Method called to create a new context for iterating all + * contents of the current structured value (JSON array or object) + */ + public final NodeCursor iterateChildren() { + JsonNode n = currentNode(); + if (n == null) throw new IllegalStateException("No current node"); + if (n.isArray()) { // false since we have already returned START_ARRAY + return new ArrayCursor(n, this); + } + if (n.isObject()) { + return new ObjectCursor(n, this); + } + throw new IllegalStateException("Current node of type "+n.getClass().getName()); + } + + /* + /********************************************************** + /* Concrete implementations + /********************************************************** + */ + + /** + * Context matching root-level value nodes (i.e. anything other + * than JSON Object and Array). + * Note that context is NOT created for leaf values. + */ + protected final static class RootCursor + extends NodeCursor + { + protected JsonNode _node; + + protected boolean _done = false; + + public RootCursor(JsonNode n, NodeCursor p) { + super(JsonStreamContext.TYPE_ROOT, p); + _node = n; + } + + @Override + public void overrideCurrentName(String name) { + + } + + @Override + public JsonToken nextToken() { + if (!_done) { + _done = true; + return _node.asToken(); + } + _node = null; + return null; + } + + @Override + public JsonToken nextValue() { return nextToken(); } + @Override + public JsonToken endToken() { return null; } + @Override + public JsonNode currentNode() { return _node; } + @Override + public boolean currentHasChildren() { return false; } + } + + /** + * Cursor used for traversing non-empty JSON Array nodes + */ + protected final static class ArrayCursor + extends NodeCursor + { + protected Iterator _contents; + + protected JsonNode _currentNode; + + public ArrayCursor(JsonNode n, NodeCursor p) { + super(JsonStreamContext.TYPE_ARRAY, p); + _contents = n.elements(); + } + + @Override + public JsonToken nextToken() + { + if (!_contents.hasNext()) { + _currentNode = null; + return null; + } + _currentNode = _contents.next(); + return _currentNode.asToken(); + } + + @Override + public JsonToken nextValue() { return nextToken(); } + @Override + public JsonToken endToken() { return JsonToken.END_ARRAY; } + + @Override + public JsonNode currentNode() { return _currentNode; } + @Override + public boolean currentHasChildren() { + // note: ONLY to be called for container nodes + return ((ContainerNode) currentNode()).size() > 0; + } + } + + /** + * Cursor used for traversing non-empty JSON Object nodes + */ + protected final static class ObjectCursor + extends NodeCursor + { + protected Iterator> _contents; + protected Map.Entry _current; + + protected boolean _needEntry; + + public ObjectCursor(JsonNode n, NodeCursor p) + { + super(JsonStreamContext.TYPE_OBJECT, p); + _contents = ((ObjectNode) n).fields(); + _needEntry = true; + } + + @Override + public JsonToken nextToken() + { + // Need a new entry? + if (_needEntry) { + if (!_contents.hasNext()) { + _currentName = null; + _current = null; + return null; + } + _needEntry = false; + _current = _contents.next(); + _currentName = (_current == null) ? null : _current.getKey(); + return JsonToken.FIELD_NAME; + } + _needEntry = true; + return _current.getValue().asToken(); + } + + @Override + public JsonToken nextValue() + { + JsonToken t = nextToken(); + if (t == JsonToken.FIELD_NAME) { + t = nextToken(); + } + return t; + } + + @Override + public JsonToken endToken() { return JsonToken.END_OBJECT; } + + @Override + public JsonNode currentNode() { + return (_current == null) ? null : _current.getValue(); + } + @Override + public boolean currentHasChildren() { + // note: ONLY to be called for container nodes + return ((ContainerNode) currentNode()).size() > 0; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NullNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NullNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NullNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,59 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * This singleton value class is used to contain explicit JSON null + * value. + */ +public final class NullNode + extends ValueNode +{ + // // Just need a fly-weight singleton + + public final static NullNode instance = new NullNode(); + + private NullNode() { } + + public static NullNode getInstance() { return instance; } + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.NULL; + } + + @Override public JsonToken asToken() { return JsonToken.VALUE_NULL; } + + @Override public String asText(String defaultValue) { return defaultValue; } + @Override public String asText() { return "null"; } + + // as with MissingNode, not considered number node; hence defaults are returned if provided + + /* + public int asInt(int defaultValue); + public long asLong(long defaultValue); + public double asDouble(double defaultValue); + public boolean asBoolean(boolean defaultValue); + */ + + @Override + public final void serialize(JsonGenerator g, SerializerProvider provider) + throws IOException + { + provider.defaultSerializeNull(g); + } + + @Override + public boolean equals(Object o) { + return (o == this); + } + + @Override + public int hashCode() { + return JsonNodeType.NULL.ordinal(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NumericNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NumericNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/NumericNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,72 @@ +package com.fasterxml.jackson.databind.node; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.JsonParser; + +/** + * Intermediate value node used for numeric nodes. + */ +public abstract class NumericNode + extends ValueNode +{ + protected NumericNode() { } + + @Override + public final JsonNodeType getNodeType() + { + return JsonNodeType.NUMBER; + } + + // // // Let's re-abstract so sub-classes handle them + + @Override + public abstract JsonParser.NumberType numberType(); + + @Override public abstract Number numberValue(); + @Override public abstract int intValue(); + @Override public abstract long longValue(); + @Override public abstract double doubleValue(); + @Override public abstract BigDecimal decimalValue(); + @Override public abstract BigInteger bigIntegerValue(); + + @Override public abstract boolean canConvertToInt(); + @Override public abstract boolean canConvertToLong(); + + /* + /********************************************************** + /* General type coercions + /********************************************************** + */ + + @Override + public abstract String asText(); + + @Override + public final int asInt() { + return intValue(); + } + @Override + public final int asInt(int defaultValue) { + return intValue(); + } + + @Override + public final long asLong() { + return longValue(); + } + @Override + public final long asLong(long defaultValue) { + return longValue(); + } + + @Override + public final double asDouble() { + return doubleValue(); + } + @Override + public final double asDouble(double defaultValue) { + return doubleValue(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ObjectNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ObjectNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ObjectNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,841 @@ +package com.fasterxml.jackson.databind.node; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.util.RawValue; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; + +/** + * Node that maps to JSON Object structures in JSON content. + *

+ * Note: class was final temporarily for Jackson 2.2. + */ +public class ObjectNode + extends ContainerNode +{ + // Note: LinkedHashMap for backwards compatibility + protected final Map _children; + + public ObjectNode(JsonNodeFactory nc) { + super(nc); + _children = new LinkedHashMap(); + } + + /** + * @since 2.4 + */ + public ObjectNode(JsonNodeFactory nc, Map kids) { + super(nc); + _children = kids; + } + + @Override + protected JsonNode _at(JsonPointer ptr) { + return get(ptr.getMatchingProperty()); + } + + /* Question: should this delegate to `JsonNodeFactory`? It does not absolutely + * have to, as long as sub-types override the method but... + */ + // note: co-variant for type safety + @SuppressWarnings("unchecked") + @Override + public ObjectNode deepCopy() + { + ObjectNode ret = new ObjectNode(_nodeFactory); + + for (Map.Entry entry: _children.entrySet()) + ret._children.put(entry.getKey(), entry.getValue().deepCopy()); + + return ret; + } + + /* + /********************************************************** + /* Overrides for JsonSerializable.Base + /********************************************************** + */ + + @Override + public boolean isEmpty(SerializerProvider serializers) { + return _children.isEmpty(); + } + + /* + /********************************************************** + /* Implementation of core JsonNode API + /********************************************************** + */ + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.OBJECT; + } + + @Override public JsonToken asToken() { return JsonToken.START_OBJECT; } + + @Override + public int size() { + return _children.size(); + } + + @Override + public Iterator elements() { + return _children.values().iterator(); + } + + @Override + public JsonNode get(int index) { return null; } + + @Override + public JsonNode get(String fieldName) { + return _children.get(fieldName); + } + + @Override + public Iterator fieldNames() { + return _children.keySet().iterator(); + } + + @Override + public JsonNode path(int index) { + return MissingNode.getInstance(); + } + + @Override + public JsonNode path(String fieldName) + { + JsonNode n = _children.get(fieldName); + if (n != null) { + return n; + } + return MissingNode.getInstance(); + } + + /** + * Method to use for accessing all fields (with both names + * and values) of this JSON Object. + */ + @Override + public Iterator> fields() { + return _children.entrySet().iterator(); + } + + @Override + public ObjectNode with(String propertyName) { + JsonNode n = _children.get(propertyName); + if (n != null) { + if (n instanceof ObjectNode) { + return (ObjectNode) n; + } + throw new UnsupportedOperationException("Property '" + propertyName + + "' has value that is not of type ObjectNode (but " + n + .getClass().getName() + ")"); + } + ObjectNode result = objectNode(); + _children.put(propertyName, result); + return result; + } + + @Override + public ArrayNode withArray(String propertyName) + { + JsonNode n = _children.get(propertyName); + if (n != null) { + if (n instanceof ArrayNode) { + return (ArrayNode) n; + } + throw new UnsupportedOperationException("Property '" + propertyName + + "' has value that is not of type ArrayNode (but " + n + .getClass().getName() + ")"); + } + ArrayNode result = arrayNode(); + _children.put(propertyName, result); + return result; + } + + @Override + public boolean equals(Comparator comparator, JsonNode o) + { + if (!(o instanceof ObjectNode)) { + return false; + } + ObjectNode other = (ObjectNode) o; + Map m1 = _children; + Map m2 = other._children; + + final int len = m1.size(); + if (m2.size() != len) { + return false; + } + + for (Map.Entry entry : m1.entrySet()) { + JsonNode v2 = m2.get(entry.getKey()); + if ((v2 == null) || !entry.getValue().equals(comparator, v2)) { + return false; + } + } + return true; + } + + /* + /********************************************************** + /* Public API, finding value nodes + /********************************************************** + */ + + @Override + public JsonNode findValue(String fieldName) + { + for (Map.Entry entry : _children.entrySet()) { + if (fieldName.equals(entry.getKey())) { + return entry.getValue(); + } + JsonNode value = entry.getValue().findValue(fieldName); + if (value != null) { + return value; + } + } + return null; + } + + @Override + public List findValues(String fieldName, List foundSoFar) + { + for (Map.Entry entry : _children.entrySet()) { + if (fieldName.equals(entry.getKey())) { + if (foundSoFar == null) { + foundSoFar = new ArrayList(); + } + foundSoFar.add(entry.getValue()); + } else { // only add children if parent not added + foundSoFar = entry.getValue().findValues(fieldName, foundSoFar); + } + } + return foundSoFar; + } + + @Override + public List findValuesAsText(String fieldName, List foundSoFar) + { + for (Map.Entry entry : _children.entrySet()) { + if (fieldName.equals(entry.getKey())) { + if (foundSoFar == null) { + foundSoFar = new ArrayList(); + } + foundSoFar.add(entry.getValue().asText()); + } else { // only add children if parent not added + foundSoFar = entry.getValue().findValuesAsText(fieldName, + foundSoFar); + } + } + return foundSoFar; + } + + @Override + public ObjectNode findParent(String fieldName) + { + for (Map.Entry entry : _children.entrySet()) { + if (fieldName.equals(entry.getKey())) { + return this; + } + JsonNode value = entry.getValue().findParent(fieldName); + if (value != null) { + return (ObjectNode) value; + } + } + return null; + } + + @Override + public List findParents(String fieldName, List foundSoFar) + { + for (Map.Entry entry : _children.entrySet()) { + if (fieldName.equals(entry.getKey())) { + if (foundSoFar == null) { + foundSoFar = new ArrayList(); + } + foundSoFar.add(this); + } else { // only add children if parent not added + foundSoFar = entry.getValue() + .findParents(fieldName, foundSoFar); + } + } + return foundSoFar; + } + + /* + /********************************************************** + /* Public API, serialization + /********************************************************** + */ + + /** + * Method that can be called to serialize this node and + * all of its descendants using specified JSON generator. + */ + @Override + public void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeStartObject(); + for (Map.Entry en : _children.entrySet()) { + jg.writeFieldName(en.getKey()); + /* 17-Feb-2009, tatu: Can we trust that all nodes will always + * extend BaseJsonNode? Or if not, at least implement + * JsonSerializable? Let's start with former, change if + * we must. + */ + ((BaseJsonNode) en.getValue()).serialize(jg, provider); + } + jg.writeEndObject(); + } + + @Override + public void serializeWithType(JsonGenerator jg, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + typeSer.writeTypePrefixForObject(this, jg); + for (Map.Entry en : _children.entrySet()) { + jg.writeFieldName(en.getKey()); + ((BaseJsonNode) en.getValue()).serialize(jg, provider); + } + typeSer.writeTypeSuffixForObject(this, jg); + } + + /* + /********************************************************** + /* Extended ObjectNode API, mutators, since 2.1 + /********************************************************** + */ + + /** + * Method that will set specified field, replacing old value, if any. + * Note that this is identical to {@link #replace(String, JsonNode)}, + * except for return value. + *

+ * NOTE: added to replace those uses of {@link #put(String, JsonNode)} + * where chaining with 'this' is desired. + * + * @param value to set field to; if null, will be converted + * to a {@link NullNode} first (to remove field entry, call + * {@link #remove} instead) + * + * @return This node after adding/replacing property value (to allow chaining) + * + * @since 2.1 + */ + public JsonNode set(String fieldName, JsonNode value) + { + if (value == null) { + value = nullNode(); + } + _children.put(fieldName, value); + return this; + } + + /** + * Method for adding given properties to this object node, overriding + * any existing values for those properties. + * + * @param properties Properties to add + * + * @return This node after adding/replacing property values (to allow chaining) + * + * @since 2.1 + */ + public JsonNode setAll(Map properties) + { + for (Map.Entry en : properties.entrySet()) { + JsonNode n = en.getValue(); + if (n == null) { + n = nullNode(); + } + _children.put(en.getKey(), n); + } + return this; + } + + /** + * Method for adding all properties of the given Object, overriding + * any existing values for those properties. + * + * @param other Object of which properties to add to this object + * + * @return This node after addition (to allow chaining) + * + * @since 2.1 + */ + public JsonNode setAll(ObjectNode other) + { + _children.putAll(other._children); + return this; + } + + /** + * Method for replacing value of specific property with passed + * value, and returning value (or null if none). + * + * @param fieldName Property of which value to replace + * @param value Value to set property to, replacing old value if any + * + * @return Old value of the property; null if there was no such property + * with value + * + * @since 2.1 + */ + public JsonNode replace(String fieldName, JsonNode value) + { + if (value == null) { // let's not store 'raw' nulls but nodes + value = nullNode(); + } + return _children.put(fieldName, value); + } + + /** + * Method for removing field entry from this ObjectNode, and + * returning instance after removal. + * + * @return This node after removing entry (if any) + * + * @since 2.1 + */ + public JsonNode without(String fieldName) + { + _children.remove(fieldName); + return this; + } + + /** + * Method for removing specified field properties out of + * this ObjectNode. + * + * @param fieldNames Names of fields to remove + * + * @return This node after removing entries + * + * @since 2.1 + */ + public ObjectNode without(Collection fieldNames) + { + _children.keySet().removeAll(fieldNames); + return this; + } + + /* + /********************************************************** + /* Extended ObjectNode API, mutators, generic + /********************************************************** + */ + + /** + * Method that will set specified field, replacing old value, if any. + * + * @param value to set field to; if null, will be converted + * to a {@link NullNode} first (to remove field entry, call + * {@link #remove} instead) + * + * @return Old value of the field, if any; null if there was no + * old value. + * + * @deprecated Since 2.4 use either {@link #set(String,JsonNode)} or {@link #replace(String,JsonNode)}, + */ + @Deprecated + public JsonNode put(String fieldName, JsonNode value) + { + if (value == null) { // let's not store 'raw' nulls but nodes + value = nullNode(); + } + return _children.put(fieldName, value); + } + + /** + * Method for removing field entry from this ObjectNode. + * Will return value of the field, if such field existed; + * null if not. + * + * @return Value of specified field, if it existed; null if not + */ + public JsonNode remove(String fieldName) { + return _children.remove(fieldName); + } + + /** + * Method for removing specified field properties out of + * this ObjectNode. + * + * @param fieldNames Names of fields to remove + * + * @return This node after removing entries + */ + public ObjectNode remove(Collection fieldNames) + { + _children.keySet().removeAll(fieldNames); + return this; + } + + /** + * Method for removing all field properties, such that this + * ObjectNode will contain no properties after call. + * + * @return This node after removing all entries + */ + @Override + public ObjectNode removeAll() + { + _children.clear(); + return this; + } + + /** + * Method for adding given properties to this object node, overriding + * any existing values for those properties. + * + * @param properties Properties to add + * + * @return This node after adding/replacing property values (to allow chaining) + * + * @deprecated Since 2.4 use {@link #setAll(Map)}, + */ + @Deprecated + public JsonNode putAll(Map properties) { + return setAll(properties); + } + + /** + * Method for adding all properties of the given Object, overriding + * any existing values for those properties. + * + * @param other Object of which properties to add to this object + * + * @return This node (to allow chaining) + * + * @deprecated Since 2.4 use {@link #setAll(ObjectNode)}, + */ + @Deprecated + public JsonNode putAll(ObjectNode other) { + return setAll(other); + } + + /** + * Method for removing all field properties out of this ObjectNode + * except for ones specified in argument. + * + * @param fieldNames Fields to retain in this ObjectNode + * + * @return This node (to allow call chaining) + */ + public ObjectNode retain(Collection fieldNames) + { + _children.keySet().retainAll(fieldNames); + return this; + } + + /** + * Method for removing all field properties out of this ObjectNode + * except for ones specified in argument. + * + * @param fieldNames Fields to retain in this ObjectNode + * + * @return This node (to allow call chaining) + */ + public ObjectNode retain(String... fieldNames) { + return retain(Arrays.asList(fieldNames)); + } + + /* + /********************************************************** + /* Extended ObjectNode API, mutators, typed + /********************************************************** + */ + + /** + * Method that will construct an ArrayNode and add it as a + * field of this ObjectNode, replacing old value, if any. + *

+ * NOTE: Unlike all put(...) methods, return value + * is NOT this ObjectNode, but the + * newly created ArrayNode instance. + * + * @return Newly constructed ArrayNode (NOT the old value, + * which could be of any type) + */ + public ArrayNode putArray(String fieldName) + { + ArrayNode n = arrayNode(); + _put(fieldName, n); + return n; + } + + /** + * Method that will construct an ObjectNode and add it as a + * field of this ObjectNode, replacing old value, if any. + *

+ * NOTE: Unlike all put(...) methods, return value + * is NOT this ObjectNode, but the + * newly created ObjectNode instance. + * + * @return Newly constructed ObjectNode (NOT the old value, + * which could be of any type) + */ + public ObjectNode putObject(String fieldName) + { + ObjectNode n = objectNode(); + _put(fieldName, n); + return n; + } + + /** + * @return This node (to allow chaining) + */ + public ObjectNode putPOJO(String fieldName, Object pojo) { + return _put(fieldName, pojoNode(pojo)); + } + + /** + * @since 2.6 + */ + public ObjectNode putRawValue(String fieldName, RawValue raw) { + return _put(fieldName, rawValueNode(raw)); + } + + /** + * @return This node (to allow chaining) + */ + public ObjectNode putNull(String fieldName) + { + _children.put(fieldName, nullNode()); + return this; + } + + /** + * Method for setting value of a field to specified numeric value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, short v) { + return _put(fieldName, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, Short v) { + return _put(fieldName, (v == null) ? nullNode() + : numberNode(v.shortValue())); + } + + /** + * Method for setting value of a field to specified numeric value. + * The underlying {@link JsonNode} that will be added is constructed + * using {@link JsonNodeFactory#numberNode(int)}, and may be + * "smaller" (like {@link ShortNode}) in cases where value fits within + * range of a smaller integral numeric value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, int v) { + return _put(fieldName, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, Integer v) { + return _put(fieldName, (v == null) ? nullNode() + : numberNode(v.intValue())); + } + + /** + * Method for setting value of a field to specified numeric value. + * The underlying {@link JsonNode} that will be added is constructed + * using {@link JsonNodeFactory#numberNode(long)}, and may be + * "smaller" (like {@link IntNode}) in cases where value fits within + * range of a smaller integral numeric value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, long v) { + return _put(fieldName, numberNode(v)); + } + + /** + * Method for setting value of a field to specified numeric value. + * The underlying {@link JsonNode} that will be added is constructed + * using {@link JsonNodeFactory#numberNode(Long)}, and may be + * "smaller" (like {@link IntNode}) in cases where value fits within + * range of a smaller integral numeric value. + *

+ * Note that this is alternative to {@link #put(String, long)} needed to avoid + * bumping into NPE issues with auto-unboxing. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, Long v) { + return _put(fieldName, (v == null) ? nullNode() + : numberNode(v.longValue())); + } + + /** + * Method for setting value of a field to specified numeric value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, float v) { + return _put(fieldName, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, Float v) { + return _put(fieldName, (v == null) ? nullNode() + : numberNode(v.floatValue())); + } + + /** + * Method for setting value of a field to specified numeric value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, double v) { + return _put(fieldName, numberNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, Double v) { + return _put(fieldName, (v == null) ? nullNode() + : numberNode(v.doubleValue())); + } + + /** + * Method for setting value of a field to specified numeric value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, BigDecimal v) { + return _put(fieldName, (v == null) ? nullNode() + : numberNode(v)); + } + + /** + * Method for setting value of a field to specified String value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, String v) { + return _put(fieldName, (v == null) ? nullNode() + : textNode(v)); + } + + /** + * Method for setting value of a field to specified String value. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, boolean v) { + return _put(fieldName, booleanNode(v)); + } + + /** + * Alternative method that we need to avoid bumping into NPE issues + * with auto-unboxing. + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, Boolean v) { + return _put(fieldName, (v == null) ? nullNode() + : booleanNode(v.booleanValue())); + } + + /** + * Method for setting value of a field to specified binary value + * + * @return This node (to allow chaining) + */ + public ObjectNode put(String fieldName, byte[] v) { + return _put(fieldName, (v == null) ? nullNode() + : binaryNode(v)); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof ObjectNode) { + return _childrenEqual((ObjectNode) o); + } + return false; + } + + /** + * @since 2.3 + */ + protected boolean _childrenEqual(ObjectNode other) + { + return _children.equals(other._children); + } + + @Override + public int hashCode() + { + return _children.hashCode(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(32 + (size() << 4)); + sb.append("{"); + int count = 0; + for (Map.Entry en : _children.entrySet()) { + if (count > 0) { + sb.append(","); + } + ++count; + TextNode.appendQuoted(sb, en.getKey()); + sb.append(':'); + sb.append(en.getValue().toString()); + } + sb.append("}"); + return sb.toString(); + } + + /* + /********************************************************** + /* Internal methods (overridable) + /********************************************************** + */ + + protected ObjectNode _put(String fieldName, JsonNode value) + { + _children.put(fieldName, value); + return this; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/POJONode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/POJONode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/POJONode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,170 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.util.RawValue; + +/** + * Value node that contains a wrapped POJO, to be serialized as + * a JSON constructed through data mapping (usually done by + * calling {@link com.fasterxml.jackson.databind.ObjectMapper}). + */ +public class POJONode + extends ValueNode +{ + protected final Object _value; + + public POJONode(Object v) { _value = v; } + + /* + /********************************************************** + /* Base class overrides + /********************************************************** + */ + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.POJO; + } + + @Override public JsonToken asToken() { return JsonToken.VALUE_EMBEDDED_OBJECT; } + + /** + * As it is possible that some implementations embed byte[] as POJONode + * (despite optimal being {@link BinaryNode}), let's add support for exposing + * binary data here too. + */ + @Override + public byte[] binaryValue() throws IOException + { + if (_value instanceof byte[]) { + return (byte[]) _value; + } + return super.binaryValue(); + } + + /* + /********************************************************** + /* General type coercions + /********************************************************** + */ + + @Override + public String asText() { return (_value == null) ? "null" : _value.toString(); } + + @Override public String asText(String defaultValue) { + return (_value == null) ? defaultValue : _value.toString(); + } + + @Override + public boolean asBoolean(boolean defaultValue) + { + if (_value != null && _value instanceof Boolean) { + return ((Boolean) _value).booleanValue(); + } + return defaultValue; + } + + @Override + public int asInt(int defaultValue) + { + if (_value instanceof Number) { + return ((Number) _value).intValue(); + } + return defaultValue; + } + + @Override + public long asLong(long defaultValue) + { + if (_value instanceof Number) { + return ((Number) _value).longValue(); + } + return defaultValue; + } + + @Override + public double asDouble(double defaultValue) + { + if (_value instanceof Number) { + return ((Number) _value).doubleValue(); + } + return defaultValue; + } + + /* + /********************************************************** + /* Public API, serialization + /********************************************************** + */ + + @Override + public final void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException + { + if (_value == null) { + serializers.defaultSerializeNull(gen); + } else if (_value instanceof JsonSerializable) { + ((JsonSerializable) _value).serialize(gen, serializers); + } else { + gen.writeObject(_value); + } + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Method that can be used to access the POJO this node wraps. + */ + public Object getPojo() { return _value; } + + /* + /********************************************************** + /* Overridden standard methods + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof POJONode) { + return _pojoEquals((POJONode) o); + } + return false; + } + + /** + * @since 2.3 + */ + protected boolean _pojoEquals(POJONode other) + { + if (_value == null) { + return other._value == null; + } + return _value.equals(other._value); + } + + @Override + public int hashCode() { return _value.hashCode(); } + + @Override + public String toString() + { + // [databind#743]: Let's try indicating content type, for debugging purposes + if (_value instanceof byte[]) { + return String.format("(binary value of %d bytes)", ((byte[]) _value).length); + } + if (_value instanceof RawValue) { + return String.format("(raw value '%s')", ((RawValue) _value).toString()); + } + return String.valueOf(_value); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ShortNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ShortNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ShortNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,109 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.NumberOutput; +import com.fasterxml.jackson.databind.SerializerProvider; + + +/** + * Numeric node that contains simple 16-bit integer values. + */ +public class ShortNode + extends NumericNode +{ + protected final short _value; + + /* + ************************************************ + * Construction + ************************************************ + */ + + public ShortNode(short v) { _value = v; } + + public static ShortNode valueOf(short l) { return new ShortNode(l); } + + /* + ************************************************ + * Overridden JsonNode methods + ************************************************ + */ + + @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; } + + @Override + public JsonParser.NumberType numberType() { return JsonParser.NumberType.INT; } // should be SHORT + + + @Override + public boolean isIntegralNumber() { return true; } + + @Override + public boolean isShort() { return true; } + + @Override public boolean canConvertToInt() { return true; } + @Override public boolean canConvertToLong() { return true; } + + @Override + public Number numberValue() { + return Short.valueOf(_value); + } + + @Override + public short shortValue() { return _value; } + + @Override + public int intValue() { return _value; } + + @Override + public long longValue() { return _value; } + + @Override + public float floatValue() { return _value; } + + @Override + public double doubleValue() { return _value; } + + @Override + public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); } + + @Override + public BigInteger bigIntegerValue() { return BigInteger.valueOf(_value); } + + @Override + public String asText() { + return NumberOutput.toString(_value); + } + + @Override + public boolean asBoolean(boolean defaultValue) { + return _value != 0; + } + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) + throws IOException, JsonProcessingException + { + jg.writeNumber(_value); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof ShortNode) { + return ((ShortNode) o)._value == _value; + } + return false; + } + + @Override + public int hashCode() { + return _value; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/TextNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/TextNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/TextNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,301 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.io.CharTypes; +import com.fasterxml.jackson.core.io.NumberInput; +import com.fasterxml.jackson.core.util.ByteArrayBuilder; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * Value node that contains a text value. + */ +public class TextNode + extends ValueNode +{ + final static TextNode EMPTY_STRING_NODE = new TextNode(""); + + protected final String _value; + + public TextNode(String v) { _value = v; } + + /** + * Factory method that should be used to construct instances. + * For some common cases, can reuse canonical instances: currently + * this is the case for empty Strings, in future possible for + * others as well. If null is passed, will return null. + * + * @return Resulting {@link TextNode} object, if v + * is NOT null; null if it is. + */ + public static TextNode valueOf(String v) + { + if (v == null) { + return null; + } + if (v.length() == 0) { + return EMPTY_STRING_NODE; + } + return new TextNode(v); + } + + @Override + public JsonNodeType getNodeType() { + return JsonNodeType.STRING; + } + + @Override public JsonToken asToken() { return JsonToken.VALUE_STRING; } + + @Override + public String textValue() { + return _value; + } + + /** + * Method for accessing textual contents assuming they were + * base64 encoded; if so, they are decoded and resulting binary + * data is returned. + */ + public byte[] getBinaryValue(Base64Variant b64variant) throws IOException + { + @SuppressWarnings("resource") + ByteArrayBuilder builder = new ByteArrayBuilder(100); + final String str = _value; + int ptr = 0; + int len = str.length(); + + main_loop: + while (ptr < len) { + // first, we'll skip preceding white space, if any + char ch; + do { + ch = str.charAt(ptr++); + if (ptr >= len) { + break main_loop; + } + } while (ch <= ' '); + int bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + _reportInvalidBase64(b64variant, ch, 0); + } + int decodedData = bits; + // then second base64 char; can't get padding yet, nor ws + if (ptr >= len) { + _reportBase64EOF(); + } + ch = str.charAt(ptr++); + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + _reportInvalidBase64(b64variant, ch, 1); + } + decodedData = (decodedData << 6) | bits; + // third base64 char; can be padding, but not ws + if (ptr >= len) { + // but as per [JACKSON-631] can be end-of-input, iff not using padding + if (!b64variant.usesPadding()) { + // Got 12 bits, only need 8, need to shift + decodedData >>= 4; + builder.append(decodedData); + break; + } + _reportBase64EOF(); + } + ch = str.charAt(ptr++); + bits = b64variant.decodeBase64Char(ch); + + // First branch: can get padding (-> 1 byte) + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + _reportInvalidBase64(b64variant, ch, 2); + } + // Ok, must get padding + if (ptr >= len) { + _reportBase64EOF(); + } + ch = str.charAt(ptr++); + if (!b64variant.usesPaddingChar(ch)) { + _reportInvalidBase64(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'"); + } + // Got 12 bits, only need 8, need to shift + decodedData >>= 4; + builder.append(decodedData); + continue; + } + // Nope, 2 or 3 bytes + decodedData = (decodedData << 6) | bits; + // fourth and last base64 char; can be padding, but not ws + if (ptr >= len) { + // but as per [JACKSON-631] can be end-of-input, iff not using padding + if (!b64variant.usesPadding()) { + decodedData >>= 2; + builder.appendTwoBytes(decodedData); + break; + } + _reportBase64EOF(); + } + ch = str.charAt(ptr++); + bits = b64variant.decodeBase64Char(ch); + if (bits < 0) { + if (bits != Base64Variant.BASE64_VALUE_PADDING) { + _reportInvalidBase64(b64variant, ch, 3); + } + decodedData >>= 2; + builder.appendTwoBytes(decodedData); + } else { + // otherwise, our triple is now complete + decodedData = (decodedData << 6) | bits; + builder.appendThreeBytes(decodedData); + } + } + return builder.toByteArray(); + } + + @Override + public byte[] binaryValue() throws IOException { + return getBinaryValue(Base64Variants.getDefaultVariant()); + } + + /* + /********************************************************** + /* General type coercions + /********************************************************** + */ + + @Override + public String asText() { + return _value; + } + + @Override + public String asText(String defaultValue) { + return (_value == null) ? defaultValue : _value; + } + + // note: neither fast nor elegant, but these work for now: + + @Override + public boolean asBoolean(boolean defaultValue) { + if (_value != null) { + String v = _value.trim(); + if ("true".equals(v)) { + return true; + } + if ("false".equals(v)) { + return false; + } + } + return defaultValue; + } + + @Override + public int asInt(int defaultValue) { + return NumberInput.parseAsInt(_value, defaultValue); + } + + @Override + public long asLong(long defaultValue) { + return NumberInput.parseAsLong(_value, defaultValue); + } + + @Override + public double asDouble(double defaultValue) { + return NumberInput.parseAsDouble(_value, defaultValue); + } + + /* + /********************************************************** + /* Serialization + /********************************************************** + */ + + @Override + public final void serialize(JsonGenerator jg, SerializerProvider provider) throws IOException + { + if (_value == null) { + jg.writeNull(); + } else { + jg.writeString(_value); + } + } + + /* + /********************************************************** + /* Overridden standard methods + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof TextNode) { + return ((TextNode) o)._value.equals(_value); + } + return false; + } + + @Override + public int hashCode() { return _value.hashCode(); } + + /** + * Different from other values, Strings need quoting + */ + @Override + public String toString() + { + int len = _value.length(); + len = len + 2 + (len >> 4); + StringBuilder sb = new StringBuilder(len); + appendQuoted(sb, _value); + return sb.toString(); + } + + protected static void appendQuoted(StringBuilder sb, String content) + { + sb.append('"'); + CharTypes.appendQuoted(sb, content); + sb.append('"'); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex) + throws JsonParseException + { + _reportInvalidBase64(b64variant, ch, bindex, null); + } + + /** + * @param bindex Relative index within base64 character unit; between 0 + * and 3 (as unit has exactly 4 characters) + */ + protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg) + throws JsonParseException + { + String base; + if (ch <= ' ') { + base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units"; + } else if (b64variant.usesPaddingChar(ch)) { + base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; + } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { + // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level) + base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; + } else { + base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; + } + if (msg != null) { + base = base + ": " + msg; + } + throw new JsonParseException(null, base, JsonLocation.NA); + } + + protected void _reportBase64EOF() throws JsonParseException { + throw new JsonParseException(null, "Unexpected end-of-String when base64 content"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/TreeTraversingParser.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/TreeTraversingParser.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/TreeTraversingParser.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,408 @@ +package com.fasterxml.jackson.databind.node; + +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.base.ParserMinimalBase; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Facade over {@link JsonNode} that implements {@link JsonParser} to allow + * accessing contents of JSON tree in alternate form (stream of tokens). + * Useful when a streaming source is expected by code, such as data binding + * functionality. + */ +public class TreeTraversingParser extends ParserMinimalBase +{ + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected ObjectCodec _objectCodec; + + /** + * Traversal context within tree + */ + protected NodeCursor _nodeCursor; + + /* + /********************************************************** + /* State + /********************************************************** + */ + + /** + * Sometimes parser needs to buffer a single look-ahead token; if so, + * it'll be stored here. This is currently used for handling + */ + protected JsonToken _nextToken; + + /** + * Flag needed to handle recursion into contents of child + * Array/Object nodes. + */ + protected boolean _startContainer; + + /** + * 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; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public TreeTraversingParser(JsonNode n) { this(n, null); } + + public TreeTraversingParser(JsonNode n, ObjectCodec codec) + { + super(0); + _objectCodec = codec; + if (n.isArray()) { + _nextToken = JsonToken.START_ARRAY; + _nodeCursor = new NodeCursor.ArrayCursor(n, null); + } else if (n.isObject()) { + _nextToken = JsonToken.START_OBJECT; + _nodeCursor = new NodeCursor.ObjectCursor(n, null); + } else { // value node + _nodeCursor = new NodeCursor.RootCursor(n, null); + } + } + + @Override + public void setCodec(ObjectCodec c) { + _objectCodec = c; + } + + @Override + public ObjectCodec getCodec() { + return _objectCodec; + } + + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Closeable implementation + /********************************************************** + */ + + @Override + public void close() throws IOException + { + if (!_closed) { + _closed = true; + _nodeCursor = null; + _currToken = null; + } + } + + /* + /********************************************************** + /* Public API, traversal + /********************************************************** + */ + + @Override + public JsonToken nextToken() throws IOException, JsonParseException + { + if (_nextToken != null) { + _currToken = _nextToken; + _nextToken = null; + return _currToken; + } + // are we to descend to a container child? + if (_startContainer) { + _startContainer = false; + // minor optimization: empty containers can be skipped + if (!_nodeCursor.currentHasChildren()) { + _currToken = (_currToken == JsonToken.START_OBJECT) ? + JsonToken.END_OBJECT : JsonToken.END_ARRAY; + return _currToken; + } + _nodeCursor = _nodeCursor.iterateChildren(); + _currToken = _nodeCursor.nextToken(); + if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { + _startContainer = true; + } + return _currToken; + } + // No more content? + if (_nodeCursor == null) { + _closed = true; // if not already set + return null; + } + // Otherwise, next entry from current cursor + _currToken = _nodeCursor.nextToken(); + if (_currToken != null) { + if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { + _startContainer = true; + } + return _currToken; + } + // null means no more children; need to return end marker + _currToken = _nodeCursor.endToken(); + _nodeCursor = _nodeCursor.getParent(); + return _currToken; + } + + // default works well here: + //public JsonToken nextValue() throws IOException, JsonParseException + + @Override + public JsonParser skipChildren() throws IOException, JsonParseException + { + if (_currToken == JsonToken.START_OBJECT) { + _startContainer = false; + _currToken = JsonToken.END_OBJECT; + } else if (_currToken == JsonToken.START_ARRAY) { + _startContainer = false; + _currToken = JsonToken.END_ARRAY; + } + return this; + } + + @Override + public boolean isClosed() { + return _closed; + } + + /* + /********************************************************** + /* Public API, token accessors + /********************************************************** + */ + + @Override + public String getCurrentName() { + return (_nodeCursor == null) ? null : _nodeCursor.getCurrentName(); + } + + @Override + public void overrideCurrentName(String name) + { + if (_nodeCursor != null) { + _nodeCursor.overrideCurrentName(name); + } + } + + @Override + public JsonStreamContext getParsingContext() { + return _nodeCursor; + } + + @Override + public JsonLocation getTokenLocation() { + return JsonLocation.NA; + } + + @Override + public JsonLocation getCurrentLocation() { + return JsonLocation.NA; + } + + /* + /********************************************************** + /* Public API, access to textual content + /********************************************************** + */ + + @Override + public String getText() + { + if (_closed) { + return null; + } + // need to separate handling a bit... + switch (_currToken) { + case FIELD_NAME: + return _nodeCursor.getCurrentName(); + case VALUE_STRING: + return currentNode().textValue(); + case VALUE_NUMBER_INT: + case VALUE_NUMBER_FLOAT: + return String.valueOf(currentNode().numberValue()); + case VALUE_EMBEDDED_OBJECT: + JsonNode n = currentNode(); + if (n != null && n.isBinary()) { + // this will convert it to base64 + return n.asText(); + } + default: + return (_currToken == null) ? null : _currToken.asString(); + } + } + + @Override + public char[] getTextCharacters() throws IOException, JsonParseException { + return getText().toCharArray(); + } + + @Override + public int getTextLength() throws IOException, JsonParseException { + return getText().length(); + } + + @Override + public int getTextOffset() throws IOException, JsonParseException { + return 0; + } + + @Override + public boolean hasTextCharacters() { + // generally we do not have efficient access as char[], hence: + return false; + } + + /* + /********************************************************** + /* Public API, typed non-text access + /********************************************************** + */ + + //public byte getByteValue() throws IOException, JsonParseException + + @Override + public NumberType getNumberType() throws IOException, JsonParseException { + JsonNode n = currentNumericNode(); + return (n == null) ? null : n.numberType(); + } + + @Override + public BigInteger getBigIntegerValue() throws IOException, JsonParseException + { + return currentNumericNode().bigIntegerValue(); + } + + @Override + public BigDecimal getDecimalValue() throws IOException, JsonParseException { + return currentNumericNode().decimalValue(); + } + + @Override + public double getDoubleValue() throws IOException, JsonParseException { + return currentNumericNode().doubleValue(); + } + + @Override + public float getFloatValue() throws IOException, JsonParseException { + return (float) currentNumericNode().doubleValue(); + } + + @Override + public long getLongValue() throws IOException, JsonParseException { + return currentNumericNode().longValue(); + } + + @Override + public int getIntValue() throws IOException, JsonParseException { + return currentNumericNode().intValue(); + } + + @Override + public Number getNumberValue() throws IOException, JsonParseException { + return currentNumericNode().numberValue(); + } + + @Override + public Object getEmbeddedObject() + { + if (!_closed) { + JsonNode n = currentNode(); + if (n != null) { + if (n.isPojo()) { + return ((POJONode) n).getPojo(); + } + if (n.isBinary()) { + return ((BinaryNode) n).binaryValue(); + } + } + } + return null; + } + + /* + /********************************************************** + /* Public API, typed binary (base64) access + /********************************************************** + */ + + @Override + public byte[] getBinaryValue(Base64Variant b64variant) + throws IOException, JsonParseException + { + // Multiple possibilities... + JsonNode n = currentNode(); + if (n != null) { // binary node? + byte[] data = n.binaryValue(); + // (or TextNode, which can also convert automatically!) + if (data != null) { + return data; + } + // Or maybe byte[] as POJO? + if (n.isPojo()) { + Object ob = ((POJONode) n).getPojo(); + if (ob instanceof byte[]) { + return (byte[]) ob; + } + } + } + // otherwise return null to mark we have no binary content + return null; + } + + + @Override + public int readBinaryValue(Base64Variant b64variant, OutputStream out) + throws IOException, JsonParseException + { + byte[] data = getBinaryValue(b64variant); + if (data != null) { + out.write(data, 0, data.length); + return data.length; + } + return 0; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected JsonNode currentNode() { + if (_closed || _nodeCursor == null) { + return null; + } + return _nodeCursor.currentNode(); + } + + protected JsonNode currentNumericNode() + throws JsonParseException + { + JsonNode n = currentNode(); + if (n == null || !n.isNumber()) { + JsonToken t = (n == null) ? null : n.asToken(); + throw _constructError("Current token ("+t+") not numeric, can not use numeric value accessors"); + } + return n; + } + + @Override + protected void _handleEOF() throws JsonParseException { + _throwInternal(); // should never get called + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ValueNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ValueNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/ValueNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,118 @@ +package com.fasterxml.jackson.databind.node; + +import java.io.IOException; +import java.util.List; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * This intermediate base class is used for all leaf nodes, that is, + * all non-container (array or object) nodes, except for the + * "missing node". + */ +public abstract class ValueNode + extends BaseJsonNode +{ + protected ValueNode() { } + + @Override + protected JsonNode _at(JsonPointer ptr) { + // will only allow direct matches, but no traversal through + // (base class checks for direct match) + return MissingNode.getInstance(); + } + + /** + * All current value nodes are immutable, so we can just return + * them as is. + */ + @SuppressWarnings("unchecked") + @Override + public T deepCopy() { return (T) this; } + + @Override public abstract JsonToken asToken(); + + @Override + public void serializeWithType(JsonGenerator jg, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + typeSer.writeTypePrefixForScalar(this, jg); + serialize(jg, provider); + typeSer.writeTypeSuffixForScalar(this, jg); + } + + /* + /********************************************************************** + /* Base impls for standard methods + /********************************************************************** + */ + + @Override + public String toString() { return asText(); } + + /* + ********************************************************************** + * Navigation methods + ********************************************************************** + */ + + @Override + public final JsonNode get(int index) { return null; } + + @Override + public final JsonNode path(int index) { return MissingNode.getInstance(); } + + @Override + public final boolean has(int index) { return false; } + + @Override + public final boolean hasNonNull(int index) { return false; } + + @Override + public final JsonNode get(String fieldName) { return null; } + + @Override + public final JsonNode path(String fieldName) { return MissingNode.getInstance(); } + + @Override + public final boolean has(String fieldName) { return false; } + + @Override + public final boolean hasNonNull(String fieldName) { return false; } + + /* + ********************************************************************** + * Find methods: all "leaf" nodes return the same for these + ********************************************************************** + */ + + @Override + public final JsonNode findValue(String fieldName) { + return null; + } + + // note: co-variant return type + @Override + public final ObjectNode findParent(String fieldName) { + return null; + } + + @Override + public final List findValues(String fieldName, List foundSoFar) { + return foundSoFar; + } + + @Override + public final List findValuesAsText(String fieldName, List foundSoFar) { + return foundSoFar; + } + + @Override + public final List findParents(String fieldName, List foundSoFar) { + return foundSoFar; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/node/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,8 @@ +/** + * Contains concrete {@link com.fasterxml.jackson.databind.JsonNode} implementations + * Jackson uses for the Tree model. + * These classes are public since concrete type will be needed + * for most operations that modify node trees. For read-only access concrete + * types are usually not needed. + */ +package com.fasterxml.jackson.databind.node; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,64 @@ +/** +Basic data binding (mapping) functionality that +allows for reading JSON content into Java Objects (POJOs) +and JSON Trees ({@link com.fasterxml.jackson.databind.JsonNode}), as well as +writing Java Objects and trees as JSON. +Reading and writing (as well as related additional functionality) is accessed through +{@link com.fasterxml.jackson.databind.ObjectMapper}, +{@link com.fasterxml.jackson.databind.ObjectReader} and +{@link com.fasterxml.jackson.databind.ObjectWriter} + classes. + In addition to reading and writing JSON content, it is also possible to use the + general databinding functionality for many other data formats, using + Jackson extension modules that provide such support: if so, you typically + simply construct an {@link com.fasterxml.jackson.databind.ObjectMapper} with + different underlying streaming parser, generator implementation. +

+The main starting point for operations is {@link com.fasterxml.jackson.databind.ObjectMapper}, +which can be used either directly (via multiple overloaded +readValue, +readTree, +writeValue and +writeTree methods, or it can be used as a configurable factory for constructing +fully immutable, thread-safe and reusable {@link com.fasterxml.jackson.databind.ObjectReader} +and {@link com.fasterxml.jackson.databind.ObjectWriter} objects. +

+In addition to simple reading and writing of JSON as POJOs or JSON trees (represented as +{@link com.fasterxml.jackson.databind.JsonNode}, and configurability needed to change aspects +of reading/writing, mapper contains additional functionality such as: +

    +
  • Value conversions using {@link com.fasterxml.jackson.databind.ObjectMapper#convertValue(Object, Class)}, + {@link com.fasterxml.jackson.databind.ObjectMapper#valueToTree(Object)} and + {@link com.fasterxml.jackson.databind.ObjectMapper#treeToValue(com.fasterxml.jackson.core.TreeNode, Class)} methods. +
  • +
  • Type introspection needed for things like generation of Schemas (like JSON Schema, Avro Schema, or protoc + definitions), using {@link com.fasterxml.jackson.databind.ObjectMapper#acceptJsonFormatVisitor(Class, com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper)} + (note: actual handles are usually provided by various Jackson modules: mapper simply initiates calling of + callbacks, based on serializers registered) +
  • +
+

+Simplest usage is of form: +

+  final ObjectMapper mapper = new ObjectMapper(); // can use static singleton, inject: just make sure to reuse!
+  MyValue value = new MyValue();
+  // ... and configure
+  File newState = new File("my-stuff.json");
+  mapper.writeValue(newState, value); // writes JSON serialization of MyValue instance
+  // or, read
+  MyValue older = mapper.readValue(new File("my-older-stuff.json"), MyValue.class);
+
+  // Or if you prefer JSON Tree representation:
+  JsonNode root = mapper.readTree(newState);
+  // and find values by, for example, using a {@link com.fasterxml.jackson.core.JsonPointer} expression:
+  int age = root.at("/personal/age").getValueAsInt(); 
+
+

+For more usage, refer to +{@link com.fasterxml.jackson.databind.ObjectMapper}, +{@link com.fasterxml.jackson.databind.ObjectReader} and +{@link com.fasterxml.jackson.databind.ObjectWriter} +Javadocs. +*/ + +package com.fasterxml.jackson.databind; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,96 @@ +package com.fasterxml.jackson.databind.ser; + +import java.util.Map; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.ser.std.MapSerializer; + +/** + * Class similar to {@link BeanPropertyWriter}, but that will be used + * for serializing {@link com.fasterxml.jackson.annotation.JsonAnyGetter} annotated + * (Map) properties + */ +public class AnyGetterWriter +{ + protected final BeanProperty _property; + + /** + * Method (or field) that represents the "any getter" + */ + protected final AnnotatedMember _accessor; + + protected JsonSerializer _serializer; + + protected MapSerializer _mapSerializer; + + @SuppressWarnings("unchecked") + public AnyGetterWriter(BeanProperty property, + AnnotatedMember accessor, JsonSerializer serializer) + { + _accessor = accessor; + _property = property; + _serializer = (JsonSerializer) serializer; + if (serializer instanceof MapSerializer) { + _mapSerializer = (MapSerializer) serializer; + } + } + + public void getAndSerialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws Exception + { + Object value = _accessor.getValue(bean); + if (value == null) { + return; + } + if (!(value instanceof Map)) { + throw JsonMappingException.from(gen, "Value returned by 'any-getter' (" + +_accessor.getName()+"()) not java.util.Map but "+value.getClass().getName()); + } + // 23-Feb-2015, tatu: Nasty, but has to do (for now) + if (_mapSerializer != null) { + _mapSerializer.serializeFields((Map) value, gen, provider); + return; + } + _serializer.serialize(value, gen, provider); + } + + /** + * @since 2.3 + */ + public void getAndFilter(Object bean, JsonGenerator gen, SerializerProvider provider, + PropertyFilter filter) + throws Exception + { + Object value = _accessor.getValue(bean); + if (value == null) { + return; + } + if (!(value instanceof Map)) { + throw JsonMappingException.from(gen, "Value returned by 'any-getter' (" + +_accessor.getName()+"()) not java.util.Map but "+value.getClass().getName()); + } + // 19-Oct-2014, tatu: Should we try to support @JsonInclude options here? + if (_mapSerializer != null) { + _mapSerializer.serializeFilteredFields((Map) value, gen, provider, filter, null); + return; + } + // ... not sure how custom handler would do it + _serializer.serialize(value, gen, provider); + } + + // Note: NOT part of ResolvableSerializer... + @SuppressWarnings("unchecked") + public void resolve(SerializerProvider provider) throws JsonMappingException + { + // 05-Sep-2013, tatu: I _think_ this can be considered a primary property... + if (_serializer instanceof ContextualSerializer) { + JsonSerializer ser = provider.handlePrimaryContextualization(_serializer, _property); + _serializer = (JsonSerializer) ser; + if (ser instanceof MapSerializer) { + _mapSerializer = (MapSerializer) ser; + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1060 @@ +package com.fasterxml.jackson.databind.ser; + +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; +import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.impl.*; +import com.fasterxml.jackson.databind.ser.std.*; +import com.fasterxml.jackson.databind.type.*; +import com.fasterxml.jackson.databind.util.*; + +/** + * Factory class that can provide serializers for standard JDK classes, + * as well as custom classes that extend standard classes or implement + * one of "well-known" interfaces (such as {@link java.util.Collection}). + *

+ * Since all the serializers are eagerly instantiated, and there is + * no additional introspection or customizability of these types, + * this factory is essentially stateless. + */ +@SuppressWarnings("serial") +public abstract class BasicSerializerFactory + extends SerializerFactory + implements java.io.Serializable +{ + /* + /********************************************************** + /* Configuration, lookup tables/maps + /********************************************************** + */ + + /** + * Since these are all JDK classes, we shouldn't have to worry + * about ClassLoader used to load them. Rather, we can just + * use the class name, and keep things simple and efficient. + */ + protected final static HashMap> _concrete; + + /** + * Actually it may not make much sense to eagerly instantiate all + * kinds of serializers: so this Map actually contains class references, + * not instances + */ + protected final static HashMap>> _concreteLazy; + + static { + HashMap>> concLazy + = new HashMap>>(); + HashMap> concrete + = new HashMap>(); + + + /* String and string-like types (note: date types explicitly + * not included -- can use either textual or numeric serialization) + */ + concrete.put(String.class.getName(), new StringSerializer()); + final ToStringSerializer sls = ToStringSerializer.instance; + concrete.put(StringBuffer.class.getName(), sls); + concrete.put(StringBuilder.class.getName(), sls); + concrete.put(Character.class.getName(), sls); + concrete.put(Character.TYPE.getName(), sls); + + // Primitives/wrappers for primitives (primitives needed for Beans) + NumberSerializers.addAll(concrete); + concrete.put(Boolean.TYPE.getName(), new BooleanSerializer(true)); + concrete.put(Boolean.class.getName(), new BooleanSerializer(false)); + + // Other numbers, more complicated + concrete.put(BigInteger.class.getName(), new NumberSerializer(BigInteger.class)); + concrete.put(BigDecimal.class.getName(),new NumberSerializer(BigDecimal.class)); + + // Other discrete non-container types: + // First, Date/Time zoo: + concrete.put(Calendar.class.getName(), CalendarSerializer.instance); + concrete.put(java.util.Date.class.getName(), DateSerializer.instance); + + // And then other standard non-structured JDK types + for (Map.Entry,Object> en : StdJdkSerializers.all()) { + Object value = en.getValue(); + if (value instanceof JsonSerializer) { + concrete.put(en.getKey().getName(), (JsonSerializer) value); + } else if (value instanceof Class) { + @SuppressWarnings("unchecked") + Class> cls = (Class>) value; + concLazy.put(en.getKey().getName(), cls); + } else { // should never happen, but: + throw new IllegalStateException("Internal error: unrecognized value of type "+en.getClass().getName()); + } + } + + // Jackson-specific type(s) + // (Q: can this ever be sub-classed?) + concLazy.put(TokenBuffer.class.getName(), TokenBufferSerializer.class); + + _concrete = concrete; + _concreteLazy = concLazy; + } + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Configuration settings for this factory; immutable instance (just like this + * factory), new version created via copy-constructor (fluent-style) + */ + protected final SerializerFactoryConfig _factoryConfig; + + /* + /********************************************************** + /* Life cycle + /********************************************************** + */ + + /** + * We will provide default constructor to allow sub-classing, + * but make it protected so that no non-singleton instances of + * the class will be instantiated. + */ + protected BasicSerializerFactory(SerializerFactoryConfig config) { + _factoryConfig = (config == null) ? new SerializerFactoryConfig() : config; + } + + /** + * Method for getting current {@link SerializerFactoryConfig}. + *

+ * Note that since instances are immutable, you can NOT change settings + * by accessing an instance and calling methods: this will simply create + * new instance of config object. + */ + public SerializerFactoryConfig getFactoryConfig() { + return _factoryConfig; + } + + /** + * Method used for creating a new instance of this factory, but with different + * configuration. Reason for specifying factory method (instead of plain constructor) + * is to allow proper sub-classing of factories. + *

+ * Note that custom sub-classes generally must override implementation + * of this method, as it usually requires instantiating a new instance of + * factory type. Check out javadocs for + * {@link com.fasterxml.jackson.databind.ser.BeanSerializerFactory} for more details. + */ + public abstract SerializerFactory withConfig(SerializerFactoryConfig config); + + /** + * Convenience method for creating a new factory instance with an additional + * serializer provider. + */ + @Override + public final SerializerFactory withAdditionalSerializers(Serializers additional) { + return withConfig(_factoryConfig.withAdditionalSerializers(additional)); + } + + /** + * Convenience method for creating a new factory instance with an additional + * key serializer provider. + */ + @Override + public final SerializerFactory withAdditionalKeySerializers(Serializers additional) { + return withConfig(_factoryConfig.withAdditionalKeySerializers(additional)); + } + + /** + * Convenience method for creating a new factory instance with additional bean + * serializer modifier. + */ + @Override + public final SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) { + return withConfig(_factoryConfig.withSerializerModifier(modifier)); + } + + /* + /********************************************************** + /* SerializerFactory impl + /********************************************************** + */ + + // Implemented by sub-classes + @Override + public abstract JsonSerializer createSerializer(SerializerProvider prov, + JavaType type) + throws JsonMappingException; + + @Override + @SuppressWarnings("unchecked") + public JsonSerializer createKeySerializer(SerializationConfig config, + JavaType keyType, JsonSerializer defaultImpl) + { + // We should not need any member method info; at most class annotations for Map type + // ... at least, not here. + BeanDescription beanDesc = config.introspectClassAnnotations(keyType.getRawClass()); + JsonSerializer ser = null; + // Minor optimization: to avoid constructing beanDesc, bail out if none registered + if (_factoryConfig.hasKeySerializers()) { + // Only thing we have here are module-provided key serializers: + for (Serializers serializers : _factoryConfig.keySerializers()) { + ser = serializers.findSerializer(config, keyType, beanDesc); + if (ser != null) { + break; + } + } + } + if (ser == null) { + ser = defaultImpl; + if (ser == null) { + ser = StdKeySerializers.getStdKeySerializer(config, keyType.getRawClass(), false); + // As per [databind#47], also need to support @JsonValue + if (ser == null) { + beanDesc = config.introspect(keyType); + AnnotatedMethod am = beanDesc.findJsonValueMethod(); + if (am != null) { + final Class rawType = am.getRawReturnType(); + JsonSerializer delegate = StdKeySerializers.getStdKeySerializer(config, + rawType, true); + Method m = am.getAnnotated(); + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(m, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + ser = new JsonValueSerializer(m, delegate); + } else { + ser = StdKeySerializers.getFallbackKeySerializer(config, keyType.getRawClass()); + } + } + } + } + + // [databind#120]: Allow post-processing + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyKeySerializer(config, keyType, beanDesc, ser); + } + } + return (JsonSerializer) ser; + } + + /** + * Method called to construct a type serializer for values with given declared + * base type. This is called for values other than those of bean property + * types. + */ + @Override + public TypeSerializer createTypeSerializer(SerializationConfig config, + JavaType baseType) + { + BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass()); + AnnotatedClass ac = bean.getClassInfo(); + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + TypeResolverBuilder b = ai.findTypeResolver(config, ac, baseType); + /* Ok: if there is no explicit type info handler, we may want to + * use a default. If so, config object knows what to use. + */ + Collection subtypes = null; + if (b == null) { + b = config.getDefaultTyper(baseType); + } else { + subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, ac); + } + if (b == null) { + return null; + } + // 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing + // wrt EXTERNAL_PROPERTY + return b.buildTypeSerializer(config, baseType, subtypes); + } + + /* + /********************************************************** + /* Additional API for other core classes + /********************************************************** + */ + + protected abstract Iterable customSerializers(); + + /* + /********************************************************** + /* Overridable secondary serializer accessor methods + /********************************************************** + */ + + /** + * Method that will use fast lookup (and identity comparison) methods to + * see if we know serializer to use for given type. + */ + protected final JsonSerializer findSerializerByLookup(JavaType type, + SerializationConfig config, BeanDescription beanDesc, + boolean staticTyping) + { + Class raw = type.getRawClass(); + String clsName = raw.getName(); + JsonSerializer ser = _concrete.get(clsName); + if (ser == null) { + Class> serClass = _concreteLazy.get(clsName); + if (serClass != null) { + try { + return serClass.newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Failed to instantiate standard serializer (of type "+serClass.getName()+"): " + +e.getMessage(), e); + } + } + } + return ser; + } + + /** + * Method called to see if one of primary per-class annotations + * (or related, like implementing of {@link JsonSerializable}) + * determines the serializer to use. + *

+ * Currently handles things like: + *

    + *
  • If type implements {@link JsonSerializable}, use that + *
  • + *
  • If type has {@link com.fasterxml.jackson.annotation.JsonValue} annotation (or equivalent), build serializer + * based on that property + *
  • + *
+ * + * @since 2.0 + */ + protected final JsonSerializer findSerializerByAnnotations(SerializerProvider prov, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + Class raw = type.getRawClass(); + // First: JsonSerializable? + if (JsonSerializable.class.isAssignableFrom(raw)) { + return SerializableSerializer.instance; + } + // Second: @JsonValue for any type + AnnotatedMethod valueMethod = beanDesc.findJsonValueMethod(); + if (valueMethod != null) { + Method m = valueMethod.getAnnotated(); + if (prov.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(m, prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + JsonSerializer ser = findSerializerFromAnnotation(prov, valueMethod); + return new JsonValueSerializer(m, ser); + } + // No well-known annotations... + return null; + } + + /** + * Method for checking if we can determine serializer to use based on set of + * known primary types, checking for set of known base types (exact matches + * having been compared against with findSerializerByLookup). + * This does not include "secondary" interfaces, but + * mostly concrete or abstract base classes. + */ + protected final JsonSerializer findSerializerByPrimaryType(SerializerProvider prov, + JavaType type, BeanDescription beanDesc, + boolean staticTyping) + throws JsonMappingException + { + Class raw = type.getRawClass(); + + // Then check for optional/external serializers + JsonSerializer ser = findOptionalStdSerializer(prov, type, beanDesc, staticTyping); + if (ser != null) { + return ser; + } + + if (Calendar.class.isAssignableFrom(raw)) { + return CalendarSerializer.instance; + } + if (java.util.Date.class.isAssignableFrom(raw)) { + return DateSerializer.instance; + } + if (Map.Entry.class.isAssignableFrom(raw)) { + // 18-Oct-2015, tatu: With 2.7, need to dig type info: + JavaType mapEntryType = type.findSuperType(Map.Entry.class); + + // 28-Apr-2015, tatu: TypeFactory does it all for us already so + JavaType kt = mapEntryType.containedType(0); + if (kt == null) { + kt = TypeFactory.unknownType(); + } + JavaType vt = mapEntryType.containedType(1); + if (vt == null) { + vt = TypeFactory.unknownType(); + } + return buildMapEntrySerializer(prov.getConfig(), type, beanDesc, staticTyping, kt, vt); + } + if (ByteBuffer.class.isAssignableFrom(raw)) { + return new ByteBufferSerializer(); + } + if (InetAddress.class.isAssignableFrom(raw)) { + return new InetAddressSerializer(); + } + if (InetSocketAddress.class.isAssignableFrom(raw)) { + return new InetSocketAddressSerializer(); + } + if (TimeZone.class.isAssignableFrom(raw)) { + return new TimeZoneSerializer(); + } + if (java.nio.charset.Charset.class.isAssignableFrom(raw)) { + return ToStringSerializer.instance; + } + if (Number.class.isAssignableFrom(raw)) { + // 21-May-2014, tatu: Couple of alternatives actually + JsonFormat.Value format = beanDesc.findExpectedFormat(null); + if (format != null) { + switch (format.getShape()) { + case STRING: + return ToStringSerializer.instance; + case OBJECT: // need to bail out to let it be serialized as POJO + case ARRAY: // or, I guess ARRAY; otherwise no point in speculating + return null; + default: + } + } + return NumberSerializer.instance; + } + if (Enum.class.isAssignableFrom(raw)) { + return buildEnumSerializer(prov.getConfig(), type, beanDesc); + } + return null; + } + + /** + * Overridable method called after checking all other types. + * + * @since 2.2 + */ + protected JsonSerializer findOptionalStdSerializer(SerializerProvider prov, + JavaType type, BeanDescription beanDesc, boolean staticTyping) + throws JsonMappingException + { + return OptionalHandlerFactory.instance.findSerializer(prov.getConfig(), type, beanDesc); + } + + /** + * Reflection-based serialized find method, which checks if + * given class implements one of recognized "add-on" interfaces. + * Add-on here means a role that is usually or can be a secondary + * trait: for example, + * bean classes may implement {@link Iterable}, but their main + * function is usually something else. The reason for + */ + protected final JsonSerializer findSerializerByAddonType(SerializationConfig config, + JavaType javaType, BeanDescription beanDesc, boolean staticTyping) throws JsonMappingException + { + Class rawType = javaType.getRawClass(); + + if (Iterator.class.isAssignableFrom(rawType)) { + JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterator.class); + JavaType vt = (params == null || params.length != 1) ? + TypeFactory.unknownType() : params[0]; + return buildIteratorSerializer(config, javaType, beanDesc, staticTyping, vt); + } + if (Iterable.class.isAssignableFrom(rawType)) { + JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterable.class); + JavaType vt = (params == null || params.length != 1) ? + TypeFactory.unknownType() : params[0]; + return buildIterableSerializer(config, javaType, beanDesc, staticTyping, vt); + } + if (CharSequence.class.isAssignableFrom(rawType)) { + return ToStringSerializer.instance; + } + return null; + } + + /** + * Helper method called to check if a class or method + * has an annotation + * (@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using) + * that tells the class to use for serialization. + * Returns null if no such annotation found. + */ + @SuppressWarnings("unchecked") + protected JsonSerializer findSerializerFromAnnotation(SerializerProvider prov, + Annotated a) + throws JsonMappingException + { + Object serDef = prov.getAnnotationIntrospector().findSerializer(a); + if (serDef == null) { + return null; + } + JsonSerializer ser = prov.serializerInstance(a, serDef); + // One more thing however: may need to also apply a converter: + return (JsonSerializer) findConvertingSerializer(prov, a, ser); + } + + /** + * Helper method that will check whether given annotated entity (usually class, + * but may also be a property accessor) indicates that a {@link Converter} is to + * be used; and if so, to construct and return suitable serializer for it. + * If not, will simply return given serializer as is. + */ + protected JsonSerializer findConvertingSerializer(SerializerProvider prov, + Annotated a, JsonSerializer ser) + throws JsonMappingException + { + Converter conv = findConverter(prov, a); + if (conv == null) { + return ser; + } + JavaType delegateType = conv.getOutputType(prov.getTypeFactory()); + return new StdDelegatingSerializer(conv, delegateType, ser); + } + + protected Converter findConverter(SerializerProvider prov, + Annotated a) + throws JsonMappingException + { + Object convDef = prov.getAnnotationIntrospector().findSerializationConverter(a); + if (convDef == null) { + return null; + } + return prov.converterInstance(a, convDef); + } + + /* + /********************************************************** + /* Factory methods, container types: + /********************************************************** + */ + + /** + * @since 2.1 + */ + protected JsonSerializer buildContainerSerializer(SerializerProvider prov, + JavaType type, BeanDescription beanDesc, boolean staticTyping) + throws JsonMappingException + { + final SerializationConfig config = prov.getConfig(); + + /* [databind#23], 15-Mar-2013, tatu: must force static handling of root value type, + * with just one important exception: if value type is "untyped", let's + * leave it as is; no clean way to make it work. + */ + if (!staticTyping && type.useStaticType()) { + if (!type.isContainerType() || type.getContentType().getRawClass() != Object.class) { + staticTyping = true; + } + } + + // Let's see what we can learn about element/content/value type, type serializer for it: + JavaType elementType = type.getContentType(); + TypeSerializer elementTypeSerializer = createTypeSerializer(config, + elementType); + + // if elements have type serializer, can not force static typing: + if (elementTypeSerializer != null) { + staticTyping = false; + } + JsonSerializer elementValueSerializer = _findContentSerializer(prov, + beanDesc.getClassInfo()); + if (type.isMapLikeType()) { // implements java.util.Map + MapLikeType mlt = (MapLikeType) type; + /* 29-Sep-2012, tatu: This is actually too early to (try to) find + * key serializer from property annotations, and can lead to caching + * issues (see [databind#75]). Instead, must be done from 'createContextual()' call. + * But we do need to check class annotations. + */ + JsonSerializer keySerializer = _findKeySerializer(prov, beanDesc.getClassInfo()); + if (mlt.isTrueMapType()) { + return buildMapSerializer(prov, (MapType) mlt, beanDesc, staticTyping, + keySerializer, elementTypeSerializer, elementValueSerializer); + } + // With Map-like, just 2 options: (1) Custom, (2) Annotations + JsonSerializer ser = null; + MapLikeType mlType = (MapLikeType) type; + for (Serializers serializers : customSerializers()) { // (1) Custom + ser = serializers.findMapLikeSerializer(config, + mlType, beanDesc, keySerializer, elementTypeSerializer, elementValueSerializer); + if (ser != null) { + break; + } + } + if (ser == null) { // (2) Annotations-based ones: + ser = findSerializerByAnnotations(prov, type, beanDesc); + } + if (ser != null) { + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyMapLikeSerializer(config, mlType, beanDesc, ser); + } + } + } + return ser; + } + if (type.isCollectionLikeType()) { + CollectionLikeType clt = (CollectionLikeType) type; + if (clt.isTrueCollectionType()) { + return buildCollectionSerializer(prov, (CollectionType) clt, beanDesc, staticTyping, + elementTypeSerializer, elementValueSerializer); + } + // With Map-like, just 2 options: (1) Custom, (2) Annotations + JsonSerializer ser = null; + CollectionLikeType clType = (CollectionLikeType) type; + for (Serializers serializers : customSerializers()) { // (1) Custom + ser = serializers.findCollectionLikeSerializer(config, + clType, beanDesc, elementTypeSerializer, elementValueSerializer); + if (ser != null) { + break; + } + } + if (ser == null) { // (2) Annotations-based ones: + ser = findSerializerByAnnotations(prov, type, beanDesc); + } + if (ser != null) { + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyCollectionLikeSerializer(config, clType, beanDesc, ser); + } + } + } + return ser; + } + if (type.isArrayType()) { + return buildArraySerializer(prov, (ArrayType) type, beanDesc, staticTyping, + elementTypeSerializer, elementValueSerializer); + } + return null; + } + + /** + * Helper method that handles configuration details when constructing serializers for + * {@link java.util.List} types that support efficient by-index access + * + * @since 2.1 + */ + protected JsonSerializer buildCollectionSerializer(SerializerProvider prov, + CollectionType type, BeanDescription beanDesc, boolean staticTyping, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + throws JsonMappingException + { + SerializationConfig config = prov.getConfig(); + JsonSerializer ser = null; + // Order of lookups: + // 1. Custom serializers + // 2. Annotations (@JsonValue, @JsonDeserialize) + // 3. Defaults + for (Serializers serializers : customSerializers()) { // (1) Custom + ser = serializers.findCollectionSerializer(config, + type, beanDesc, elementTypeSerializer, elementValueSerializer); + if (ser != null) { + break; + } + } + + if (ser == null) { + ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations + if (ser == null) { + // We may also want to use serialize Collections "as beans", if (and only if) + // this is specified with `@JsonFormat(shape=Object)` + JsonFormat.Value format = beanDesc.findExpectedFormat(null); + if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) { + return null; + } + Class raw = type.getRawClass(); + if (EnumSet.class.isAssignableFrom(raw)) { + // this may or may not be available (Class doesn't; type of field/method does) + JavaType enumType = type.getContentType(); + // and even if nominally there is something, only use if it really is enum + if (!enumType.isEnumType()) { + enumType = null; + } + ser = buildEnumSetSerializer(enumType); + } else { + Class elementRaw = type.getContentType().getRawClass(); + if (isIndexedList(raw)) { + if (elementRaw == String.class) { + // [JACKSON-829] Must NOT use if we have custom serializer + if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) { + ser = IndexedStringListSerializer.instance; + } + } else { + ser = buildIndexedListSerializer(type.getContentType(), staticTyping, + elementTypeSerializer, elementValueSerializer); + } + } else if (elementRaw == String.class) { + // [JACKSON-829] Must NOT use if we have custom serializer + if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) { + ser = StringCollectionSerializer.instance; + } + } + if (ser == null) { + ser = buildCollectionSerializer(type.getContentType(), staticTyping, + elementTypeSerializer, elementValueSerializer); + } + } + } + } + // [databind#120]: Allow post-processing + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyCollectionSerializer(config, type, beanDesc, ser); + } + } + return ser; + } + + /* + /********************************************************** + /* Factory methods, for Collections + /********************************************************** + */ + + protected boolean isIndexedList(Class cls) + { + return RandomAccess.class.isAssignableFrom(cls); + } + + public ContainerSerializer buildIndexedListSerializer(JavaType elemType, + boolean staticTyping, TypeSerializer vts, JsonSerializer valueSerializer) { + return new IndexedListSerializer(elemType, staticTyping, vts, valueSerializer); + } + public ContainerSerializer buildCollectionSerializer(JavaType elemType, + boolean staticTyping, TypeSerializer vts, JsonSerializer valueSerializer) { + return new CollectionSerializer(elemType, staticTyping, vts, valueSerializer); + } + + public JsonSerializer buildEnumSetSerializer(JavaType enumType) { + return new EnumSetSerializer(enumType); + } + + /* + /********************************************************** + /* Factory methods, for Maps + /********************************************************** + */ + + /** + * Helper method that handles configuration details when constructing serializers for + * {@link java.util.Map} types. + */ + protected JsonSerializer buildMapSerializer(SerializerProvider prov, + MapType type, BeanDescription beanDesc, + boolean staticTyping, JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + throws JsonMappingException + { + final SerializationConfig config = prov.getConfig(); + JsonSerializer ser = null; + + // Order of lookups: + // 1. Custom serializers + // 2. Annotations (@JsonValue, @JsonDeserialize) + // 3. Defaults + + for (Serializers serializers : customSerializers()) { // (1) Custom + ser = serializers.findMapSerializer(config, type, beanDesc, + keySerializer, elementTypeSerializer, elementValueSerializer); + if (ser != null) { break; } + } + if (ser == null) { + ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations + if (ser == null) { + Object filterId = findFilterId(config, beanDesc); + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + MapSerializer mapSer = MapSerializer.construct(ai.findPropertiesToIgnore(beanDesc.getClassInfo(), true), + type, staticTyping, elementTypeSerializer, + keySerializer, elementValueSerializer, filterId); + Object suppressableValue = findSuppressableContentValue(config, + type.getContentType(), beanDesc); + if (suppressableValue != null) { + mapSer = mapSer.withContentInclusion(suppressableValue); + } + ser = mapSer; + } + } + // [databind#120]: Allow post-processing + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyMapSerializer(config, type, beanDesc, ser); + } + } + return ser; + } + + /** + *

+ * NOTE: although return type is left opaque, it really needs to be + * JsonInclude.Include for things to work as expected. + * + * @since 2.5 + */ + protected Object findSuppressableContentValue(SerializationConfig config, + JavaType contentType, BeanDescription beanDesc) + throws JsonMappingException + { + JsonInclude.Value inclV = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion()); + + if (inclV == null) { + return null; + } + JsonInclude.Include incl = inclV.getContentInclusion(); + switch (incl) { + case USE_DEFAULTS: // means "dunno" + return null; + case NON_DEFAULT: + // 19-Oct-2014, tatu: Not sure what this'd mean; so take it to mean "NON_EMPTY"... + // 11-Nov-2015, tatu: With 2.6, we did indeed revert to "NON_EMPTY", but that did + // not go well, so with 2.7, we'll do this instead... + // But not 100% sure if we ought to call new `JsonSerializer.findDefaultValue()`; + // to do that, would need to locate said serializer +// incl = JsonInclude.Include.NON_EMPTY; + break; + default: + // all other modes actually good as is, unless we'll find better ways + break; + } + return incl; + } + + /* + /********************************************************** + /* Factory methods, for Arrays + /********************************************************** + */ + + /** + * Helper method that handles configuration details when constructing serializers for + * Object[] (and subtypes, except for String). + */ + protected JsonSerializer buildArraySerializer(SerializerProvider prov, + ArrayType type, BeanDescription beanDesc, + boolean staticTyping, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + throws JsonMappingException + { + // 25-Jun-2015, tatu: Note that unlike with Collection(Like) and Map(Like) types, array + // types can not be annotated (in theory I guess we could have mix-ins but... ?) + // so we need not do primary annotation lookup here. + // So all we need is (1) Custom, (2) Default array serializers + SerializationConfig config = prov.getConfig(); + JsonSerializer ser = null; + + for (Serializers serializers : customSerializers()) { // (1) Custom + ser = serializers.findArraySerializer(config, + type, beanDesc, elementTypeSerializer, elementValueSerializer); + if (ser != null) { + break; + } + } + + if (ser == null) { + Class raw = type.getRawClass(); + // Important: do NOT use standard serializers if non-standard element value serializer specified + if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) { + if (String[].class == raw) { + ser = StringArraySerializer.instance; + } else { + // other standard types? + ser = StdArraySerializers.findStandardImpl(raw); + } + } + if (ser == null) { + ser = new ObjectArraySerializer(type.getContentType(), staticTyping, elementTypeSerializer, + elementValueSerializer); + } + } + // [databind#120]: Allow post-processing + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyArraySerializer(config, type, beanDesc, ser); + } + } + return ser; + } + + /* + /********************************************************** + /* Factory methods, for non-container types + /********************************************************** + */ + + /** + * @since 2.5 + */ + protected JsonSerializer buildIteratorSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, boolean staticTyping, + JavaType valueType) + throws JsonMappingException + { + return new IteratorSerializer(valueType, staticTyping, createTypeSerializer(config, valueType)); + } + + @Deprecated // since 2.5 + protected JsonSerializer buildIteratorSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, boolean staticTyping) throws JsonMappingException + { + JavaType[] params = config.getTypeFactory().findTypeParameters(type, Iterator.class); + JavaType vt = (params == null || params.length != 1) ? + TypeFactory.unknownType() : params[0]; + return buildIteratorSerializer(config, type, beanDesc, staticTyping, vt); + } + + /** + * @since 2.5 + */ + protected JsonSerializer buildIterableSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, boolean staticTyping, + JavaType valueType) + throws JsonMappingException + { + return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType)); + } + + @Deprecated // since 2.5 + protected JsonSerializer buildIterableSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, + boolean staticTyping) + throws JsonMappingException + { + JavaType[] params = config.getTypeFactory().findTypeParameters(type, Iterable.class); + JavaType vt = (params == null || params.length != 1) ? + TypeFactory.unknownType() : params[0]; + return buildIterableSerializer(config, type, beanDesc, staticTyping, vt); + } + + /** + * @since 2.5 + */ + protected JsonSerializer buildMapEntrySerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc, boolean staticTyping, + JavaType keyType, JavaType valueType) + throws JsonMappingException + { + return new MapEntrySerializer(valueType, keyType, valueType, + staticTyping, createTypeSerializer(config, valueType), null); + } + + protected JsonSerializer buildEnumSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc) + throws JsonMappingException + { + /* As per [databind#24], may want to use alternate shape, serialize as JSON Object. + * Challenge here is that EnumSerializer does not know how to produce + * POJO style serialization, so we must handle that special case separately; + * otherwise pass it to EnumSerializer. + */ + JsonFormat.Value format = beanDesc.findExpectedFormat(null); + if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) { + // one special case: suppress serialization of "getDeclaringClass()"... + ((BasicBeanDescription) beanDesc).removeProperty("declaringClass"); + // returning null will mean that eventually BeanSerializer gets constructed + return null; + } + @SuppressWarnings("unchecked") + Class> enumClass = (Class>) type.getRawClass(); + JsonSerializer ser = EnumSerializer.construct(enumClass, config, beanDesc, format); + // [Issue#120]: Allow post-processing + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifyEnumSerializer(config, type, beanDesc, ser); + } + } + return ser; + } + + /* + /********************************************************** + /* Other helper methods + /********************************************************** + */ + + /** + * Helper method called to try to find whether there is an annotation in the + * class that indicates key serializer to use. + * If so, will try to instantiate key serializer and return it; otherwise returns null. + */ + protected JsonSerializer _findKeySerializer(SerializerProvider prov, + Annotated a) + throws JsonMappingException + { + AnnotationIntrospector intr = prov.getAnnotationIntrospector(); + Object serDef = intr.findKeySerializer(a); + if (serDef != null) { + return prov.serializerInstance(a, serDef); + } + return null; + } + + /** + * Helper method called to try to find whether there is an annotation in the + * class that indicates content ("value") serializer to use. + * If so, will try to instantiate value serializer and return it; otherwise returns null. + */ + protected JsonSerializer _findContentSerializer(SerializerProvider prov, + Annotated a) + throws JsonMappingException + { + AnnotationIntrospector intr = prov.getAnnotationIntrospector(); + Object serDef = intr.findContentSerializer(a); + if (serDef != null) { + return prov.serializerInstance(a, serDef); + } + return null; + } + + /** + * Method called to find filter that is configured to be used with bean + * serializer being built, if any. + */ + protected Object findFilterId(SerializationConfig config, BeanDescription beanDesc) { + return config.getAnnotationIntrospector().findFilterId((Annotated)beanDesc.getClassInfo()); + } + + /** + * Helper method to check whether global settings and/or class + * annotations for the bean class indicate that static typing + * (declared types) should be used for properties. + * (instead of dynamic runtime types). + * + * @since 2.1 (earlier had variant with additional 'property' parameter) + */ + protected boolean usesStaticTyping(SerializationConfig config, + BeanDescription beanDesc, TypeSerializer typeSer) + { + /* 16-Aug-2010, tatu: If there is a (value) type serializer, we can not force + * static typing; that would make it impossible to handle expected subtypes + */ + if (typeSer != null) { + return false; + } + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + JsonSerialize.Typing t = intr.findSerializationTyping(beanDesc.getClassInfo()); + if (t != null && t != JsonSerialize.Typing.DEFAULT_TYPING) { + return (t == JsonSerialize.Typing.STATIC); + } + return config.isEnabled(MapperFeature.USE_STATIC_TYPING); + } + + protected Class _verifyAsClass(Object src, String methodName, Class noneClass) + { + if (src == null) { + return null; + } + if (!(src instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class instead"); + } + Class cls = (Class) src; + if (cls == noneClass || ClassUtil.isBogusClass(cls)) { + return null; + } + return cls; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,94 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Interface that defines API for filter objects use (as configured + * using {@link com.fasterxml.jackson.annotation.JsonFilter}) + * for filtering bean properties to serialize. + *

+ * Starting with version 2.3 this class is deprecated; use + * {@link PropertyFilter} instead. + * + * @deprecated Since 2.3: use {@link PropertyFilter} instead. + */ +@Deprecated +public interface BeanPropertyFilter +{ + /** + * Method called by {@link BeanSerializer} to let filter decide what to do with + * given bean property value: the usual choices are to either filter out (i.e. + * do nothing) or write using given {@link BeanPropertyWriter}, although filters + * can choose other to do something different altogether. + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.serializeAsField(pojo, jgen, prov);
+     * }
+     *
+ * + * @param pojo Object that contains property value to serialize + * @param jgen Generator use for serializing value + * @param prov Provider that can be used for accessing dynamic aspects of serialization + * processing + * @param writer Default bean property serializer to use + */ + public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, + BeanPropertyWriter writer) + throws Exception; + + /** + * Method called by {@link BeanSerializer} to let the filter determine whether, and in what + * form the given property exist within the parent, or root, schema. Filters can omit + * adding the property to the node, or choose the form of the schema value for the property. + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.depositSchemaProperty(propertiesNode, provider);
+     * }
+     *
+ * + * @param writer Bean property writer to use to create schema value + * @param propertiesNode Node which the given property would exist within + * @param provider Provider that can be used for accessing dynamic aspects of serialization + * processing + * + * @since 2.1 + * @deprecated Since 2.3: new code should use the alternative depositSchemaProperty + * method + */ + @Deprecated + public void depositSchemaProperty(BeanPropertyWriter writer, ObjectNode propertiesNode, + SerializerProvider provider) + throws JsonMappingException; + + /** + * Method called by {@link BeanSerializer} to let the filter determine whether, and in what + * form the given property exist within the parent, or root, schema. Filters can omit + * adding the property to the node, or choose the form of the schema value for the property + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.depositSchemaProperty(objectVisitor, provider);
+     * }
+     *
+ * + * @param writer Bean property serializer to use to create schema value + * @param objectVisitor JsonObjectFormatVisitor which should be aware of + * the property's existence + * @param provider Provider that can be used for accessing dynamic aspects of serialization + * processing + * + * @since 2.1 + */ + public void depositSchemaProperty(BeanPropertyWriter writer, JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,904 @@ +package com.fasterxml.jackson.databind.ser; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.HashMap; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.Annotations; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Base bean property handler class, which implements common parts of + * reflection-based functionality for accessing a property value + * and serializing it. + *

+ * Note that current design tries to keep instances immutable (semi-functional + * style); mostly because these instances are exposed to application + * code and this is to reduce likelihood of data corruption and + * synchronization issues. + */ +@JacksonStdImpl // since 2.6. NOTE: sub-classes typically are not +public class BeanPropertyWriter + extends PropertyWriter // which extends `ConcreteBeanPropertyBase` + implements java.io.Serializable // since 2.6 +{ + // As of 2.7 + private static final long serialVersionUID = 1L; + + /** + * Marker object used to indicate "do not serialize if empty" + */ + public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY; + + /* + /********************************************************** + /* Basic property metadata: name, type, other + /********************************************************** + */ + + /** + * Logical name of the property; will be used as the field name + * under which value for the property is written. + *

+ * NOTE: do NOT change name of this field; it is accessed by + * Afterburner module (until 2.4; not directly from 2.5) + * ALSO NOTE: ... and while it really ought to be `SerializableString`, + * changing that is also binary-incompatible change. So nope. + */ + protected final SerializedString _name; + + /** + * Wrapper name to use for this element, if any + * + * @since 2.2 + */ + protected final PropertyName _wrapperName; + + /** + * Type property is declared to have, either in class definition + * or associated annotations. + */ + protected final JavaType _declaredType; + + /** + * Type to use for locating serializer; normally same as return + * type of the accessor method, but may be overridden by annotations. + */ + protected final JavaType _cfgSerializationType; + + /** + * Base type of the property, if the declared type is "non-trivial"; + * meaning it is either a structured type (collection, map, array), + * or parameterized. Used to retain type information about contained + * type, which is mostly necessary if type meta-data is to be + * included. + */ + protected JavaType _nonTrivialBaseType; + + /** + * Annotations from context (most often, class that declares property, + * or in case of sub-class serializer, from that sub-class) + *

+ * NOTE: transient just to support JDK serializability; Annotations + * do not serialize. At all. + */ + protected final transient Annotations _contextAnnotations; + + /* + /********************************************************** + /* Settings for accessing property value to serialize + /********************************************************** + */ + + /** + * Member (field, method) that represents property and allows access + * to associated annotations. + */ + protected final AnnotatedMember _member; + + /** + * Accessor method used to get property value, for + * method-accessible properties. + * Null if and only if {@link #_field} is null. + *

+ * `transient` (and non-final) only to support JDK serializability. + */ + protected transient Method _accessorMethod; + + /** + * Field that contains the property value for field-accessible + * properties. + * Null if and only if {@link #_accessorMethod} is null. + *

+ * `transient` (and non-final) only to support JDK serializability. + */ + protected transient Field _field; + + /* + /********************************************************** + /* Serializers needed + /********************************************************** + */ + + /** + * Serializer to use for writing out the value: null if it can not + * be known statically; non-null if it can. + */ + protected JsonSerializer _serializer; + + /** + * Serializer used for writing out null values, if any: if null, + * null values are to be suppressed. + */ + protected JsonSerializer _nullSerializer; + + /** + * If property being serialized needs type information to be + * included this is the type serializer to use. + * Declared type (possibly augmented with annotations) of property + * is used for determining exact mechanism to use (compared to + * actual runtime type used for serializing actual state). + */ + protected TypeSerializer _typeSerializer; + + /** + * In case serializer is not known statically (i.e. _serializer + * is null), we will use a lookup structure for storing dynamically + * resolved mapping from type(s) to serializer(s). + */ + protected transient PropertySerializerMap _dynamicSerializers; + + /* + /********************************************************** + /* Filtering + /********************************************************** + */ + + /** + * Whether null values are to be suppressed (nothing written out if + * value is null) or not. Note that this is a configuration value + * during construction, and actual handling relies on setting + * (or not) of {@link #_nullSerializer}. + */ + protected final boolean _suppressNulls; + + /** + * Value that is considered default value of the property; used for + * default-value-suppression if enabled. + */ + protected final Object _suppressableValue; + + /** + * Alternate set of property writers used when view-based filtering + * is available for the Bean. + */ + protected final Class[] _includeInViews; + + /* + /********************************************************** + /* Opaque internal data that bean serializer factory and + /* bean serializers can add. + /********************************************************** + */ + + protected transient HashMap _internalSettings; + + /* + /********************************************************** + /* Construction, configuration + /********************************************************** + */ + + @SuppressWarnings("unchecked") + public BeanPropertyWriter(BeanPropertyDefinition propDef, + AnnotatedMember member, Annotations contextAnnotations, + JavaType declaredType, + JsonSerializer ser, TypeSerializer typeSer, JavaType serType, + boolean suppressNulls, Object suppressableValue) + { + super(propDef); + _member = member; + _contextAnnotations = contextAnnotations; + + _name = new SerializedString(propDef.getName()); + _wrapperName = propDef.getWrapperName(); + _includeInViews = propDef.findViews(); + + _declaredType = declaredType; + _serializer = (JsonSerializer) ser; + _dynamicSerializers = (ser == null) ? PropertySerializerMap.emptyForProperties() : null; + _typeSerializer = typeSer; + _cfgSerializationType = serType; + + if (member instanceof AnnotatedField) { + _accessorMethod = null; + _field = (Field) member.getMember(); + } else if (member instanceof AnnotatedMethod) { + _accessorMethod = (Method) member.getMember(); + _field = null; + } else { + // 01-Dec-2014, tatu: Used to be illegal, but now explicitly allowed for virtual props + _accessorMethod = null; + _field = null; + } + _suppressNulls = suppressNulls; + _suppressableValue = suppressableValue; + + // this will be resolved later on, unless nulls are to be suppressed + _nullSerializer = null; + } + + /** + * Constructor that may be of use to virtual properties, when there is need for + * the zero-arg ("default") constructor, and actual initialization is done + * after constructor call. + * + * @since 2.5 + */ + protected BeanPropertyWriter() { + super(PropertyMetadata.STD_REQUIRED_OR_OPTIONAL); + _member = null; + _contextAnnotations = null; + + _name = null; + _wrapperName = null; + _includeInViews = null; + + _declaredType = null; + _serializer = null; + _dynamicSerializers = null; + _typeSerializer = null; + _cfgSerializationType = null; + + _accessorMethod = null; + _field = null; + _suppressNulls = false; + _suppressableValue = null; + + _nullSerializer = null; + } + + /** + * "Copy constructor" to be used by filtering sub-classes + */ + protected BeanPropertyWriter(BeanPropertyWriter base) { + this(base, base._name); + } + + /** + * @since 2.5 + */ + protected BeanPropertyWriter(BeanPropertyWriter base, PropertyName name) + { + super(base); + /* 02-Dec-2014, tatu: This is a big mess, alas, what with dependency + * to MapperConfig to encode, and Afterburner having heartburn + * for SerializableString (vs SerializedString). + * Hope it can be resolved/reworked in 2.6 timeframe, if not for 2.5 + */ + _name = new SerializedString(name.getSimpleName()); + _wrapperName = base._wrapperName; + + _contextAnnotations = base._contextAnnotations; + _declaredType = base._declaredType; + + _member = base._member; + _accessorMethod = base._accessorMethod; + _field = base._field; + + _serializer = base._serializer; + _nullSerializer = base._nullSerializer; + // one more thing: copy internal settings, if any + if (base._internalSettings != null) { + _internalSettings = new HashMap(base._internalSettings); + } + _cfgSerializationType = base._cfgSerializationType; + _dynamicSerializers = base._dynamicSerializers; + _suppressNulls = base._suppressNulls; + _suppressableValue = base._suppressableValue; + _includeInViews = base._includeInViews; + _typeSerializer = base._typeSerializer; + _nonTrivialBaseType = base._nonTrivialBaseType; + } + + protected BeanPropertyWriter(BeanPropertyWriter base, SerializedString name) { + super(base); + _name = name; + _wrapperName = base._wrapperName; + + _member = base._member; + _contextAnnotations = base._contextAnnotations; + _declaredType = base._declaredType; + _accessorMethod = base._accessorMethod; + _field = base._field; + _serializer = base._serializer; + _nullSerializer = base._nullSerializer; + if (base._internalSettings != null) { + _internalSettings = new HashMap(base._internalSettings); + } + _cfgSerializationType = base._cfgSerializationType; + _dynamicSerializers = base._dynamicSerializers; + _suppressNulls = base._suppressNulls; + _suppressableValue = base._suppressableValue; + _includeInViews = base._includeInViews; + _typeSerializer = base._typeSerializer; + _nonTrivialBaseType = base._nonTrivialBaseType; + } + + public BeanPropertyWriter rename(NameTransformer transformer) { + String newName = transformer.transform(_name.getValue()); + if (newName.equals(_name.toString())) { + return this; + } + return _new(PropertyName.construct(newName)); + } + + /** + * Overridable factory method used by sub-classes + * + * @since 2.6.0 + */ + protected BeanPropertyWriter _new(PropertyName newName) { + return new BeanPropertyWriter(this, newName); + } + + /** + * Method called to set, reset or clear the configured type serializer + * for property. + * + * @since 2.6 + */ + public void assignTypeSerializer(TypeSerializer typeSer) { + _typeSerializer = typeSer; + } + + /** + * Method called to assign value serializer for property + * + * @since 2.0 + */ + public void assignSerializer(JsonSerializer ser) { + // may need to disable check in future? + if (_serializer != null && _serializer != ser) { + throw new IllegalStateException("Can not override serializer"); + } + _serializer = ser; + } + + /** + * Method called to assign null value serializer for property + * + * @since 2.0 + */ + public void assignNullSerializer(JsonSerializer nullSer) { + // may need to disable check in future? + if (_nullSerializer != null && _nullSerializer != nullSer) { + throw new IllegalStateException("Can not override null serializer"); + } + _nullSerializer = nullSer; + } + + /** + * Method called create an instance that handles details of unwrapping + * contained value. + */ + public BeanPropertyWriter unwrappingWriter(NameTransformer unwrapper) { + return new UnwrappingBeanPropertyWriter(this, unwrapper); + } + + /** + * Method called to define type to consider as "non-trivial" basetype, + * needed for dynamic serialization resolution for complex (usually container) + * types + */ + public void setNonTrivialBaseType(JavaType t) { + _nonTrivialBaseType = t; + } + + /* + /********************************************************** + /* JDK Serializability + /********************************************************** + */ + + /* Ideally would not require mutable state, and instead would re-create with + * final settings. However, as things are, with sub-types and all, simplest + * to just change Field/Method value directly. + */ + Object readResolve() { + if (_member instanceof AnnotatedField) { + _accessorMethod = null; + _field = (Field) _member.getMember(); + } else if (_member instanceof AnnotatedMethod) { + _accessorMethod = (Method) _member.getMember(); + _field = null; + } + if (_serializer == null) { + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + } + return this; + } + + /* + /********************************************************** + /* BeanProperty impl + /********************************************************** + */ + + // Note: also part of 'PropertyWriter' + @Override public String getName() { return _name.getValue(); } + + // Note: also part of 'PropertyWriter' + @Override public PropertyName getFullName() { // !!! TODO: impl properly + return new PropertyName(_name.getValue()); + } + + @Override public JavaType getType() { return _declaredType; } + @Override public PropertyName getWrapperName() { return _wrapperName; } + + // Note: also part of 'PropertyWriter' + @Override + public A getAnnotation(Class acls) { + return (_member == null) ? null : _member.getAnnotation(acls); + } + + // Note: also part of 'PropertyWriter' + @Override + public A getContextAnnotation(Class acls) { + return (_contextAnnotations == null) ? null : _contextAnnotations.get(acls); + } + + @Override public AnnotatedMember getMember() { return _member; } + + // @since 2.3 -- needed so it can be overridden by unwrapping writer + protected void _depositSchemaProperty(ObjectNode propertiesNode, JsonNode schemaNode) { + propertiesNode.set(getName(), schemaNode); + } + + /* + /********************************************************** + /* Managing and accessing of opaque internal settings + /* (used by extensions) + /********************************************************** + */ + + /** + * Method for accessing value of specified internal setting. + * + * @return Value of the setting, if any; null if none. + */ + public Object getInternalSetting(Object key) { + return (_internalSettings == null) ? null : _internalSettings.get(key); + } + + /** + * Method for setting specific internal setting to given value + * + * @return Old value of the setting, if any (null if none) + */ + public Object setInternalSetting(Object key, Object value) { + if (_internalSettings == null) { + _internalSettings = new HashMap(); + } + return _internalSettings.put(key, value); + } + + /** + * Method for removing entry for specified internal setting. + * + * @return Existing value of the setting, if any (null if none) + */ + public Object removeInternalSetting(Object key) { + Object removed = null; + if (_internalSettings != null) { + removed = _internalSettings.remove(key); + // to reduce memory usage, let's also drop the Map itself, if empty + if (_internalSettings.size() == 0) { + _internalSettings = null; + } + } + return removed; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + public SerializableString getSerializedName() { return _name; } + + public boolean hasSerializer() { return _serializer != null; } + public boolean hasNullSerializer() { return _nullSerializer != null; } + + /** + * @since 2.6 + */ + public TypeSerializer getTypeSerializer() { return _typeSerializer; } + + /** + * Accessor that will return true if this bean property has to support + * "unwrapping"; ability to replace POJO structural wrapping with optional + * name prefix and/or suffix (or in some cases, just removal of wrapper name). + *

+ * Default implementation simply returns false. + * + * @since 2.3 + */ + public boolean isUnwrapping() { return false; } + + public boolean willSuppressNulls() { return _suppressNulls; } + + /** + * Method called to check to see if this property has a name that would + * conflict with a given name. + * + * @since 2.6 + */ + public boolean wouldConflictWithName(PropertyName name) { + if (_wrapperName != null) { + return _wrapperName.equals(name); + } + // Bit convoluted since our support for namespaces is spotty but: + return name.hasSimpleName(_name.getValue()) + && !name.hasNamespace(); + } + + // Needed by BeanSerializer#getSchema + public JsonSerializer getSerializer() { return _serializer; } + + public JavaType getSerializationType() { return _cfgSerializationType; } + + public Class getRawSerializationType() { + return (_cfgSerializationType == null) ? null : _cfgSerializationType.getRawClass(); + } + + /* + public JavaType getFullPropertyType() { + if (_accessorMethod != null) { + return _accessorMethod.getType() + } + if (_field != null) { + return _field.getType(); + } + return null; + } + */ + + /** + * @deprecated Since 2.7, to be removed from 2.8, use {@link #getType()} instead. + */ + @Deprecated + public Class getPropertyType() { + if (_accessorMethod != null) { + return _accessorMethod.getReturnType(); + } + if (_field != null) { + return _field.getType(); + } + return null; + } + + /** + * Get the generic property type of this property writer. + * + * @return The property type, or null if not found. + * + * @deprecated Since 2.7, to be removed from 2.8, use {@link #getType()} instead. + */ + @Deprecated + public Type getGenericPropertyType() { + if (_accessorMethod != null) { + return _accessorMethod.getGenericReturnType(); + } + if (_field != null) { + return _field.getGenericType(); + } + return null; + } + + public Class[] getViews() { return _includeInViews; } + + /* + /********************************************************** + /* PropertyWriter methods (serialization) + /********************************************************** + */ + + /** + * Method called to access property that this bean stands for, from + * within given bean, and to serialize it as a JSON Object field + * using appropriate serializer. + */ + @Override + public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception + { + // inlined 'get()' + final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean); + + // Null handling is bit different, check that first + if (value == null) { + if (_nullSerializer != null) { + gen.writeFieldName(_name); + _nullSerializer.serialize(null, gen, prov); + } + return; + } + // then find serializer to use + JsonSerializer ser = _serializer; + if (ser == null) { + Class cls = value.getClass(); + PropertySerializerMap m = _dynamicSerializers; + ser = m.serializerFor(cls); + if (ser == null) { + ser = _findAndAddDynamic(m, cls, prov); + } + } + // and then see if we must suppress certain values (default, empty) + if (_suppressableValue != null) { + if (MARKER_FOR_EMPTY == _suppressableValue) { + if (ser.isEmpty(prov, value)) { + return; + } + } else if (_suppressableValue.equals(value)) { + return; + } + } + // For non-nulls: simple check for direct cycles + if (value == bean) { + // three choices: exception; handled by call; or pass-through + if (_handleSelfReference(bean, gen, prov, ser)) { + return; + } + } + gen.writeFieldName(_name); + if (_typeSerializer == null) { + ser.serialize(value, gen, prov); + } else { + ser.serializeWithType(value, gen, prov, _typeSerializer); + } + } + + /** + * Method called to indicate that serialization of a field was omitted + * due to filtering, in cases where backend data format does not allow + * basic omission. + * + * @since 2.3 + */ + @Override + public void serializeAsOmittedField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception + { + if (!gen.canOmitFields()) { + gen.writeOmittedField(_name.getValue()); + } + } + + /** + * Alternative to {@link #serializeAsField} that is used when a POJO + * is serialized as JSON Array; the difference is that no field names + * are written. + * + * @since 2.3 + */ + @Override + public void serializeAsElement(Object bean, JsonGenerator gen, SerializerProvider prov) + throws Exception + { + // inlined 'get()' + final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean); + if (value == null) { // nulls need specialized handling + if (_nullSerializer != null) { + _nullSerializer.serialize(null, gen, prov); + } else { // can NOT suppress entries in tabular output + gen.writeNull(); + } + return; + } + // otherwise find serializer to use + JsonSerializer ser = _serializer; + if (ser == null) { + Class cls = value.getClass(); + PropertySerializerMap map = _dynamicSerializers; + ser = map.serializerFor(cls); + if (ser == null) { + ser = _findAndAddDynamic(map, cls, prov); + } + } + // and then see if we must suppress certain values (default, empty) + if (_suppressableValue != null) { + if (MARKER_FOR_EMPTY == _suppressableValue) { + if (ser.isEmpty(prov, value)) { // can NOT suppress entries in tabular output + serializeAsPlaceholder(bean, gen, prov); + return; + } + } else if (_suppressableValue.equals(value)) { // can NOT suppress entries in tabular output + serializeAsPlaceholder(bean, gen, prov); + return; + } + } + // For non-nulls: simple check for direct cycles + if (value == bean) { + if (_handleSelfReference(bean, gen, prov, ser)) { + return; + } + } + if (_typeSerializer == null) { + ser.serialize(value, gen, prov); + } else { + ser.serializeWithType(value, gen, prov, _typeSerializer); + } + } + + /** + * Method called to serialize a placeholder used in tabular output when + * real value is not to be included (is filtered out), but when we need + * an entry so that field indexes will not be off. Typically this should + * output null or empty String, depending on datatype. + * + * @since 2.1 + */ + @Override + public void serializeAsPlaceholder(Object bean, JsonGenerator gen, SerializerProvider prov) + throws Exception + { + if (_nullSerializer != null) { + _nullSerializer.serialize(null, gen, prov); + } else { + gen.writeNull(); + } + } + + /* + /********************************************************** + /* PropertyWriter methods (schema generation) + /********************************************************** + */ + + // Also part of BeanProperty implementation + @Override + public void depositSchemaProperty(JsonObjectFormatVisitor v, + SerializerProvider provider) + throws JsonMappingException + { + if (v != null) { + if (isRequired()) { + v.property(this); + } else { + v.optionalProperty(this); + } + } + } + + // // // Legacy support for JsonFormatVisitable + + /** + * Attempt to add the output of the given {@link BeanPropertyWriter} in the given {@link ObjectNode}. + * Otherwise, add the default schema {@link JsonNode} in place of the writer's output + * + * @param propertiesNode Node which the given property would exist within + * @param provider Provider that can be used for accessing dynamic aspects of serialization + * processing + */ + @Override + @Deprecated + public void depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider provider) + throws JsonMappingException + { + JavaType propType = getSerializationType(); + // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas it doesn't... + Type hint = (propType == null) ? getType() : propType.getRawClass(); + JsonNode schemaNode; + // Maybe it already has annotated/statically configured serializer? + JsonSerializer ser = getSerializer(); + if (ser == null) { // nope + ser = provider.findValueSerializer(getType(), this); + } + boolean isOptional = !isRequired(); + if (ser instanceof SchemaAware) { + schemaNode = ((SchemaAware) ser).getSchema(provider, hint, isOptional) ; + } else { + schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); + } + _depositSchemaProperty(propertiesNode, schemaNode); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result; + if (_nonTrivialBaseType != null) { + JavaType t = provider.constructSpecializedType(_nonTrivialBaseType, type); + result = map.findAndAddPrimarySerializer(t, provider, this); + } else { + result = map.findAndAddPrimarySerializer(type, provider, this); + } + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicSerializers = result.map; + } + return result.serializer; + } + + /** + * Method that can be used to access value of the property this + * Object describes, from given bean instance. + *

+ * Note: method is final as it should not need to be overridden -- rather, + * calling method(s) ({@link #serializeAsField}) should be overridden + * to change the behavior + */ + public final Object get(Object bean) throws Exception { + return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean); + } + + /** + * Method called to handle a direct self-reference through this property. + * Method can choose to indicate an error by throwing {@link JsonMappingException}; + * fully handle serialization (and return true); or indicate that it should be + * serialized normally (return false). + *

+ * Default implementation will throw {@link JsonMappingException} if + * {@link SerializationFeature#FAIL_ON_SELF_REFERENCES} is enabled; + * or return false if it is disabled. + * + * @return True if method fully handled self-referential value; false if not (caller + * is to handle it) or {@link JsonMappingException} if there is no way handle it + */ + protected boolean _handleSelfReference(Object bean, JsonGenerator gen, SerializerProvider prov, JsonSerializer ser) + throws JsonMappingException { + if (prov.isEnabled(SerializationFeature.FAIL_ON_SELF_REFERENCES) + && !ser.usesObjectId()) { + // 05-Feb-2013, tatu: Usually a problem, but NOT if we are handling + // object id; this may be the case for BeanSerializers at least. + // 13-Feb-2014, tatu: another possible ok case: custom serializer (something + // OTHER than {@link BeanSerializerBase} + if (ser instanceof BeanSerializerBase) { + throw JsonMappingException.from(gen, "Direct self-reference leading to cycle"); + } + } + return false; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(40); + sb.append("property '").append(getName()).append("' ("); + if (_accessorMethod != null) { + sb.append("via method ").append(_accessorMethod.getDeclaringClass().getName()).append("#").append(_accessorMethod.getName()); + } else if (_field != null) { + sb.append("field \"").append(_field.getDeclaringClass().getName()).append("#").append(_field.getName()); + } else { + sb.append("virtual"); + } + if (_serializer == null) { + sb.append(", no static serializer"); + } else { + sb.append(", static serializer of type "+_serializer.getClass().getName()); + } + sb.append(')'); + return sb.toString(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,171 @@ +package com.fasterxml.jackson.databind.ser; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; +import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Serializer class that can serialize Java objects that map + * to JSON Object output. Internally handling is mostly dealt with + * by a sequence of {@link BeanPropertyWriter}s that will handle + * access value to serialize and call appropriate serializers to + * write out JSON. + *

+ * Implementation note: we will post-process resulting serializer, + * to figure out actual serializers for final types. This must be + * done from {@link #resolve} method, and NOT from constructor; + * otherwise we could end up with an infinite loop. + */ +public class BeanSerializer + extends BeanSerializerBase +{ + private static final long serialVersionUID = -3618164443537292758L; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + /** + * @param builder Builder object that contains collected information + * that may be needed for serializer + * @param properties Property writers used for actual serialization + */ + public BeanSerializer(JavaType type, BeanSerializerBuilder builder, + BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) + { + super(type, builder, properties, filteredProperties); + } + + /** + * Alternate copy constructor that can be used to construct + * standard {@link BeanSerializer} passing an instance of + * "compatible enough" source serializer. + */ + protected BeanSerializer(BeanSerializerBase src) { + super(src); + } + + protected BeanSerializer(BeanSerializerBase src, + ObjectIdWriter objectIdWriter) { + super(src, objectIdWriter); + } + + protected BeanSerializer(BeanSerializerBase src, + ObjectIdWriter objectIdWriter, Object filterId) { + super(src, objectIdWriter, filterId); + } + + protected BeanSerializer(BeanSerializerBase src, String[] toIgnore) { + super(src, toIgnore); + } + + /* + /********************************************************** + /* Life-cycle: factory methods, fluent factories + /********************************************************** + */ + + /** + * Method for constructing dummy bean serializer; one that + * never outputs any properties + */ + public static BeanSerializer createDummy(JavaType forType) + { + return new BeanSerializer(forType, null, NO_PROPS, null); + } + + @Override + public JsonSerializer unwrappingSerializer(NameTransformer unwrapper) { + return new UnwrappingBeanSerializer(this, unwrapper); + } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + return new BeanSerializer(this, objectIdWriter, _propertyFilterId); + } + + @Override + public BeanSerializerBase withFilterId(Object filterId) { + return new BeanSerializer(this, _objectIdWriter, filterId); + } + + @Override + protected BeanSerializerBase withIgnorals(String[] toIgnore) { + return new BeanSerializer(this, toIgnore); + } + + /** + * Implementation has to check whether as-array serialization + * is possible reliably; if (and only if) so, will construct + * a {@link BeanAsArraySerializer}, otherwise will return this + * serializer as is. + */ + @Override + protected BeanSerializerBase asArraySerializer() + { + /* Can not: + * + * - have Object Id (may be allowed in future) + * - have "any getter" + * - have per-property filters + */ + if ((_objectIdWriter == null) + && (_anyGetterWriter == null) + && (_propertyFilterId == null) + ) { + return new BeanAsArraySerializer(this); + } + // already is one, so: + return this; + } + + /* + /********************************************************** + /* JsonSerializer implementation that differs between impls + /********************************************************** + */ + + /** + * Main serialization method that will delegate actual output to + * configured + * {@link BeanPropertyWriter} instances. + */ + @Override + public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + if (_objectIdWriter != null) { + gen.setCurrentValue(bean); // [databind#631] + _serializeWithObjectId(bean, gen, provider, true); + return; + } + gen.writeStartObject(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(bean); + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + gen.writeEndObject(); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override public String toString() { + return "BeanSerializer for "+handledType().getName(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,196 @@ +package com.fasterxml.jackson.databind.ser; + +import java.util.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; + +/** + * Builder class used for aggregating deserialization information about + * a POJO, in order to build a {@link JsonSerializer} for serializing + * intances. + * Main reason for using separate builder class is that this makes it easier + * to make actual serializer class fully immutable. + */ +public class BeanSerializerBuilder +{ + private final static BeanPropertyWriter[] NO_PROPERTIES = new BeanPropertyWriter[0]; + + /* + /********************************************************** + /* Basic configuration we start with + /********************************************************** + */ + + final protected BeanDescription _beanDesc; + + protected SerializationConfig _config; + + /* + /********************************************************** + /* Accumulated information about properties + /********************************************************** + */ + + /** + * Bean properties, in order of serialization + */ + protected List _properties; + + /** + * Optional array of filtered property writers; if null, no + * view-based filtering is performed. + */ + protected BeanPropertyWriter[] _filteredProperties; + + /** + * Writer used for "any getter" properties, if any. + */ + protected AnyGetterWriter _anyGetter; + + /** + * Id of the property filter to use for POJO, if any. + */ + protected Object _filterId; + + /** + * Property that is used for type id (and not serialized as regular + * property) + */ + protected AnnotatedMember _typeId; + + /** + * Object responsible for serializing Object Ids for the handled + * type, if any. + */ + protected ObjectIdWriter _objectIdWriter; + + /* + /********************************************************** + /* Construction and setter methods + /********************************************************** + */ + + public BeanSerializerBuilder(BeanDescription beanDesc) { + _beanDesc = beanDesc; + } + + /** + * Copy-constructor that may be used for sub-classing + */ + protected BeanSerializerBuilder(BeanSerializerBuilder src) { + _beanDesc = src._beanDesc; + _properties = src._properties; + _filteredProperties = src._filteredProperties; + _anyGetter = src._anyGetter; + _filterId = src._filterId; + } + + /** + * Initialization method called right after construction, to specify + * configuration to use. + *

+ * Note: ideally should be passed in constructor, but for backwards + * compatibility, needed to add a setter instead + * + * @since 2.1 + */ + protected void setConfig(SerializationConfig config) { + _config = config; + } + + public void setProperties(List properties) { + _properties = properties; + } + + public void setFilteredProperties(BeanPropertyWriter[] properties) { + _filteredProperties = properties; + } + + public void setAnyGetter(AnyGetterWriter anyGetter) { + _anyGetter = anyGetter; + } + + public void setFilterId(Object filterId) { + _filterId = filterId; + } + + public void setTypeId(AnnotatedMember idProp) { + // Not legal to use multiple ones... + if (_typeId != null) { + throw new IllegalArgumentException("Multiple type ids specified with "+_typeId+" and "+idProp); + } + _typeId = idProp; + } + + public void setObjectIdWriter(ObjectIdWriter w) { + _objectIdWriter = w; + } + + /* + /********************************************************** + /* Accessors for things BeanSerializer cares about: + /* note -- likely to change between minor revisions + /* by new methods getting added. + /********************************************************** + */ + + public AnnotatedClass getClassInfo() { return _beanDesc.getClassInfo(); } + + public BeanDescription getBeanDescription() { return _beanDesc; } + + public List getProperties() { return _properties; } + public boolean hasProperties() { + return (_properties != null) && (_properties.size() > 0); + } + + public BeanPropertyWriter[] getFilteredProperties() { return _filteredProperties; } + + public AnyGetterWriter getAnyGetter() { return _anyGetter; } + + public Object getFilterId() { return _filterId; } + + public AnnotatedMember getTypeId() { return _typeId; } + + public ObjectIdWriter getObjectIdWriter() { return _objectIdWriter; } + + /* + /********************************************************** + /* Build methods for actually creating serializer instance + /********************************************************** + */ + + /** + * Method called to create {@link BeanSerializer} instance with + * all accumulated information. Will construct a serializer if we + * have enough information, or return null if not. + */ + public JsonSerializer build() + { + BeanPropertyWriter[] properties; + // No properties, any getter or object id writer? + // No real serializer; caller gets to handle + if (_properties == null || _properties.isEmpty()) { + if (_anyGetter == null && _objectIdWriter == null) { + return null; + } + properties = NO_PROPERTIES; + } else { + properties = _properties.toArray(new BeanPropertyWriter[_properties.size()]); + } + return new BeanSerializer(_beanDesc.getType(), this, + properties, _filteredProperties); + } + + /** + * Factory method for constructing an "empty" serializer; one that + * outputs no properties (but handles JSON objects properly, including + * type information) + */ + public BeanSerializer createDummy() { + return BeanSerializer.createDummy(_beanDesc.getType()); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,809 @@ +package com.fasterxml.jackson.databind.ser; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; +import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator; +import com.fasterxml.jackson.databind.ser.std.AtomicReferenceSerializer; +import com.fasterxml.jackson.databind.ser.std.MapSerializer; +import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer; +import com.fasterxml.jackson.databind.type.ReferenceType; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Factory class that can provide serializers for any regular Java beans + * (as defined by "having at least one get method recognizable as bean + * accessor" -- where {@link Object#getClass} does not count); + * as well as for "standard" JDK types. Latter is achieved + * by delegating calls to {@link BasicSerializerFactory} + * to find serializers both for "standard" JDK types (and in some cases, + * sub-classes as is the case for collection classes like + * {@link java.util.List}s and {@link java.util.Map}s) and bean (value) + * classes. + *

+ * Note about delegating calls to {@link BasicSerializerFactory}: + * although it would be nicer to use linear delegation + * for construction (to essentially dispatch all calls first to the + * underlying {@link BasicSerializerFactory}; or alternatively after + * failing to provide bean-based serializer}, there is a problem: + * priority levels for detecting standard types are mixed. That is, + * we want to check if a type is a bean after some of "standard" JDK + * types, but before the rest. + * As a result, "mixed" delegation used, and calls are NOT done using + * regular {@link SerializerFactory} interface but rather via + * direct calls to {@link BasicSerializerFactory}. + *

+ * Finally, since all caching is handled by the serializer provider + * (not factory) and there is no configurability, this + * factory is stateless. + * This means that a global singleton instance can be used. + */ +public class BeanSerializerFactory + extends BasicSerializerFactory + implements java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1; + + /** + * Like {@link BasicSerializerFactory}, this factory is stateless, and + * thus a single shared global (== singleton) instance can be used + * without thread-safety issues. + */ + public final static BeanSerializerFactory instance = new BeanSerializerFactory(null); + + /* + /********************************************************** + /* Life-cycle: creation, configuration + /********************************************************** + */ + + /** + * Constructor for creating instances with specified configuration. + */ + protected BeanSerializerFactory(SerializerFactoryConfig config) + { + super(config); + } + + /** + * Method used by module registration functionality, to attach additional + * serializer providers into this serializer factory. This is typically + * handled by constructing a new instance with additional serializers, + * to ensure thread-safe access. + */ + @Override + public SerializerFactory withConfig(SerializerFactoryConfig config) + { + if (_factoryConfig == config) { + return this; + } + /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor; + * and we pretty much have to here either choose between losing subtype instance + * when registering additional serializers, or losing serializers. + * Instead, let's actually just throw an error if this method is called when subtype + * has not properly overridden this method; this to indicate problem as soon as possible. + */ + if (getClass() != BeanSerializerFactory.class) { + throw new IllegalStateException("Subtype of BeanSerializerFactory ("+getClass().getName() + +") has not properly overridden method 'withAdditionalSerializers': can not instantiate subtype with " + +"additional serializer definitions"); + } + return new BeanSerializerFactory(config); + } + + @Override + protected Iterable customSerializers() { + return _factoryConfig.serializers(); + } + + /* + /********************************************************** + /* SerializerFactory impl + /********************************************************** + */ + + /** + * Main serializer constructor method. We will have to be careful + * with respect to ordering of various method calls: essentially + * we want to reliably figure out which classes are standard types, + * and which are beans. The problem is that some bean Classes may + * implement standard interfaces (say, {@link java.lang.Iterable}. + *

+ * Note: sub-classes may choose to complete replace implementation, + * if they want to alter priority of serializer lookups. + */ + @Override + @SuppressWarnings("unchecked") + public JsonSerializer createSerializer(SerializerProvider prov, + JavaType origType) + throws JsonMappingException + { + // Very first thing, let's check if there is explicit serializer annotation: + final SerializationConfig config = prov.getConfig(); + BeanDescription beanDesc = config.introspect(origType); + JsonSerializer ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); + if (ser != null) { + return (JsonSerializer) ser; + } + boolean staticTyping; + // Next: we may have annotations that further indicate actual type to use (a super type) + final AnnotationIntrospector intr = config.getAnnotationIntrospector(); + JavaType type = (intr == null) ? origType + : intr.refineSerializationType(config, beanDesc.getClassInfo(), origType); + if (type == origType) { // no changes, won't force static typing + staticTyping = false; + } else { // changes; assume static typing; plus, need to re-introspect if class differs + staticTyping = true; + if (!type.hasRawClass(origType.getRawClass())) { + beanDesc = config.introspect(type); + } + } + // Slight detour: do we have a Converter to consider? + Converter conv = beanDesc.findSerializationConverter(); + if (conv == null) { // no, simple + return (JsonSerializer) _createSerializer2(prov, type, beanDesc, staticTyping); + } + JavaType delegateType = conv.getOutputType(prov.getTypeFactory()); + + // One more twist, as per [databind#288]; probably need to get new BeanDesc + if (!delegateType.hasRawClass(type.getRawClass())) { + beanDesc = config.introspect(delegateType); + // [#359]: explicitly check (again) for @JsonSerializer... + ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); + } + // [databind#731]: Should skip if nominally java.lang.Object + if (ser == null && !delegateType.isJavaLangObject()) { + ser = _createSerializer2(prov, delegateType, beanDesc, true); + } + return new StdDelegatingSerializer(conv, delegateType, ser); + } + + protected JsonSerializer _createSerializer2(SerializerProvider prov, + JavaType type, BeanDescription beanDesc, boolean staticTyping) + throws JsonMappingException + { + JsonSerializer ser = null; + final SerializationConfig config = prov.getConfig(); + + // Container types differ from non-container types + // (note: called method checks for module-provided serializers) + if (type.isContainerType()) { + if (!staticTyping) { + staticTyping = usesStaticTyping(config, beanDesc, null); + } + // 03-Aug-2012, tatu: As per [databind#40], may require POJO serializer... + ser = buildContainerSerializer(prov, type, beanDesc, staticTyping); + // Will return right away, since called method does post-processing: + if (ser != null) { + return ser; + } + } else { + if (type.isReferenceType()) { + ser = findReferenceSerializer(prov, (ReferenceType) type, beanDesc, staticTyping); + } else { + // Modules may provide serializers of POJO types: + for (Serializers serializers : customSerializers()) { + ser = serializers.findSerializer(config, type, beanDesc); + if (ser != null) { + break; + } + } + } + // 25-Jun-2015, tatu: Then JsonSerializable, @JsonValue etc. NOTE! Prior to 2.6, + // this call was BEFORE custom serializer lookup, which was wrong. + if (ser == null) { + ser = findSerializerByAnnotations(prov, type, beanDesc); + } + } + + if (ser == null) { + // Otherwise, we will check "primary types"; both marker types that + // indicate specific handling (JsonSerializable), or main types that have + // precedence over container types + ser = findSerializerByLookup(type, config, beanDesc, staticTyping); + if (ser == null) { + ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping); + if (ser == null) { + // And this is where this class comes in: if type is not a + // known "primary JDK type", perhaps it's a bean? We can still + // get a null, if we can't find a single suitable bean property. + ser = findBeanSerializer(prov, type, beanDesc); + // Finally: maybe we can still deal with it as an implementation of some basic JDK interface? + if (ser == null) { + ser = findSerializerByAddonType(config, type, beanDesc, staticTyping); + // 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get + // 'unknown' serializer assigned earlier, here, so that it gets properly + // post-processed + if (ser == null) { + ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass()); + } + } + } + } + } + if (ser != null) { + // [databind#120]: Allow post-processing + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + ser = mod.modifySerializer(config, beanDesc, ser); + } + } + } + return ser; + } + + /* + /********************************************************** + /* Other public methods that are not part of + /* JsonSerializerFactory API + /********************************************************** + */ + + /** + * Method that will try to construct a {@link BeanSerializer} for + * given class. Returns null if no properties are found. + */ + public JsonSerializer findBeanSerializer(SerializerProvider prov, JavaType type, + BeanDescription beanDesc) + throws JsonMappingException + { + // First things first: we know some types are not beans... + if (!isPotentialBeanType(type.getRawClass())) { + // 03-Aug-2012, tatu: Except we do need to allow serializers for Enums, + // as per [databind#24] + if (!type.isEnumType()) { + return null; + } + } + return constructBeanSerializer(prov, beanDesc); + } + + /** + * @since 2.7 + */ + public JsonSerializer findReferenceSerializer(SerializerProvider prov, ReferenceType refType, + BeanDescription beanDesc, boolean staticTyping) + throws JsonMappingException + { + JavaType contentType = refType.getContentType(); + TypeSerializer contentTypeSerializer = contentType.getTypeHandler(); + final SerializationConfig config = prov.getConfig(); + if (contentTypeSerializer == null) { + contentTypeSerializer = createTypeSerializer(config, contentType); + } + JsonSerializer contentSerializer = contentType.getValueHandler(); + for (Serializers serializers : customSerializers()) { + JsonSerializer ser = serializers.findReferenceSerializer(config, refType, beanDesc, + contentTypeSerializer, contentSerializer); + if (ser != null) { + return ser; + } + } + if (refType.isTypeOrSubTypeOf(AtomicReference.class)) { + return new AtomicReferenceSerializer(refType, staticTyping, + contentTypeSerializer, contentSerializer); + } + return null; + } + + /** + * Method called to create a type information serializer for values of given + * non-container property + * if one is needed. If not needed (no polymorphic handling configured), should + * return null. + * + * @param baseType Declared type to use as the base type for type information serializer + * + * @return Type serializer to use for property values, if one is needed; null if not. + */ + public TypeSerializer findPropertyTypeSerializer(JavaType baseType, + SerializationConfig config, AnnotatedMember accessor) + throws JsonMappingException + { + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + TypeResolverBuilder b = ai.findPropertyTypeResolver(config, accessor, baseType); + TypeSerializer typeSer; + + // Defaulting: if no annotations on member, check value class + if (b == null) { + typeSer = createTypeSerializer(config, baseType); + } else { + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass( + config, accessor, baseType); + typeSer = b.buildTypeSerializer(config, baseType, subtypes); + } + return typeSer; + } + + /** + * Method called to create a type information serializer for values of given + * container property + * if one is needed. If not needed (no polymorphic handling configured), should + * return null. + * + * @param containerType Declared type of the container to use as the base type for type information serializer + * + * @return Type serializer to use for property value contents, if one is needed; null if not. + */ + public TypeSerializer findPropertyContentTypeSerializer(JavaType containerType, + SerializationConfig config, AnnotatedMember accessor) + throws JsonMappingException + { + JavaType contentType = containerType.getContentType(); + AnnotationIntrospector ai = config.getAnnotationIntrospector(); + TypeResolverBuilder b = ai.findPropertyContentTypeResolver(config, accessor, containerType); + TypeSerializer typeSer; + + // Defaulting: if no annotations on member, check value class + if (b == null) { + typeSer = createTypeSerializer(config, contentType); + } else { + Collection subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, + accessor, contentType); + typeSer = b.buildTypeSerializer(config, contentType, subtypes); + } + return typeSer; + } + + /* + /********************************************************** + /* Overridable non-public factory methods + /********************************************************** + */ + + /** + * Method called to construct serializer for serializing specified bean type. + * + * @since 2.1 + */ + @SuppressWarnings("unchecked") + protected JsonSerializer constructBeanSerializer(SerializerProvider prov, + BeanDescription beanDesc) + throws JsonMappingException + { + // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object + // 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right? + if (beanDesc.getBeanClass() == Object.class) { + return prov.getUnknownTypeSerializer(Object.class); +// throw new IllegalArgumentException("Can not create bean serializer for Object.class"); + } + final SerializationConfig config = prov.getConfig(); + BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); + builder.setConfig(config); + + // First: any detectable (auto-detect, annotations) properties to serialize? + List props = findBeanProperties(prov, beanDesc, builder); + if (props == null) { + props = new ArrayList(); + } else { + props = removeOverlappingTypeIds(prov, beanDesc, builder, props); + } + + // [databind#638]: Allow injection of "virtual" properties: + prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props); + + // [JACKSON-440] Need to allow modification bean properties to serialize: + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + props = mod.changeProperties(config, beanDesc, props); + } + } + + // Any properties to suppress? + props = filterBeanProperties(config, beanDesc, props); + + // [JACKSON-440] Need to allow reordering of properties to serialize + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + props = mod.orderProperties(config, beanDesc, props); + } + } + + /* And if Object Id is needed, some preparation for that as well: better + * do before view handling, mostly for the custom id case which needs + * access to a property + */ + builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props)); + + builder.setProperties(props); + builder.setFilterId(findFilterId(config, beanDesc)); + + AnnotatedMember anyGetter = beanDesc.findAnyGetter(); + if (anyGetter != null) { + if (config.canOverrideAccessModifiers()) { + anyGetter.fixAccess(config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + JavaType type = anyGetter.getType(); + // copied from BasicSerializerFactory.buildMapSerializer(): + boolean staticTyping = config.isEnabled(MapperFeature.USE_STATIC_TYPING); + JavaType valueType = type.getContentType(); + TypeSerializer typeSer = createTypeSerializer(config, valueType); + // last 2 nulls; don't know key, value serializers (yet) + // 23-Feb-2015, tatu: As per [#705], need to support custom serializers + JsonSerializer anySer = findSerializerFromAnnotation(prov, anyGetter); + if (anySer == null) { + // TODO: support '@JsonIgnoreProperties' with any setter? + anySer = MapSerializer.construct(/* ignored props*/ null, type, staticTyping, + typeSer, null, null, /*filterId*/ null); + } + // TODO: can we find full PropertyName? + PropertyName name = PropertyName.construct(anyGetter.getName()); + BeanProperty.Std anyProp = new BeanProperty.Std(name, valueType, null, + beanDesc.getClassAnnotations(), anyGetter, PropertyMetadata.STD_OPTIONAL); + builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, anySer)); + } + // Next: need to gather view information, if any: + processViews(config, builder); + + // Finally: let interested parties mess with the result bit more... + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { + builder = mod.updateBuilder(config, beanDesc, builder); + } + } + + JsonSerializer ser = (JsonSerializer) builder.build(); + + if (ser == null) { + // If we get this far, there were no properties found, so no regular BeanSerializer + // would be constructed. But, couple of exceptions. + // First: if there are known annotations, just create 'empty bean' serializer + if (beanDesc.hasKnownClassAnnotations()) { + return builder.createDummy(); + } + } + return ser; + } + + protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov, + BeanDescription beanDesc, List props) + throws JsonMappingException + { + ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo(); + if (objectIdInfo == null) { + return null; + } + ObjectIdGenerator gen; + Class implClass = objectIdInfo.getGeneratorType(); + + // Just one special case: Property-based generator is trickier + if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work + String propName = objectIdInfo.getPropertyName().getSimpleName(); + BeanPropertyWriter idProp = null; + + for (int i = 0, len = props.size() ;; ++i) { + if (i == len) { + throw new IllegalArgumentException("Invalid Object Id definition for "+beanDesc.getBeanClass().getName() + +": can not find property with name '"+propName+"'"); + } + BeanPropertyWriter prop = props.get(i); + if (propName.equals(prop.getName())) { + idProp = prop; + /* Let's force it to be the first property to output + * (although it may still get rearranged etc) + */ + if (i > 0) { + props.remove(i); + props.add(0, idProp); + } + break; + } + } + JavaType idType = idProp.getType(); + gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp); + // one more thing: must ensure that ObjectIdWriter does not actually write the value: + return ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId()); + + } + // other types are simpler + JavaType type = prov.constructType(implClass); + // Could require type to be passed explicitly, but we should be able to find it too: + JavaType idType = prov.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; + gen = prov.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo); + return ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen, + objectIdInfo.getAlwaysAsId()); + } + + /** + * Method called to construct a filtered writer, for given view + * definitions. Default implementation constructs filter that checks + * active view type to views property is to be included in. + */ + protected BeanPropertyWriter constructFilteredBeanWriter(BeanPropertyWriter writer, + Class[] inViews) + { + return FilteredBeanPropertyWriter.constructViewBased(writer, inViews); + } + + protected PropertyBuilder constructPropertyBuilder(SerializationConfig config, + BeanDescription beanDesc) + { + return new PropertyBuilder(config, beanDesc); + } + + protected BeanSerializerBuilder constructBeanSerializerBuilder(BeanDescription beanDesc) { + return new BeanSerializerBuilder(beanDesc); + } + + /* + /********************************************************** + /* Overridable non-public introspection methods + /********************************************************** + */ + + /** + * Helper method used to skip processing for types that we know + * can not be (i.e. are never consider to be) beans: + * things like primitives, Arrays, Enums, and proxy types. + *

+ * Note that usually we shouldn't really be getting these sort of + * types anyway; but better safe than sorry. + */ + protected boolean isPotentialBeanType(Class type) + { + return (ClassUtil.canBeABeanType(type) == null) && !ClassUtil.isProxyType(type); + } + + /** + * Method used to collect all actual serializable properties. + * Can be overridden to implement custom detection schemes. + */ + protected List findBeanProperties(SerializerProvider prov, + BeanDescription beanDesc, BeanSerializerBuilder builder) + throws JsonMappingException + { + List properties = beanDesc.findProperties(); + final SerializationConfig config = prov.getConfig(); + + // [JACKSON-429]: ignore specified types + removeIgnorableTypes(config, beanDesc, properties); + + // and possibly remove ones without matching mutator... + if (config.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS)) { + removeSetterlessGetters(config, beanDesc, properties); + } + + // nothing? can't proceed (caller may or may not throw an exception) + if (properties.isEmpty()) { + return null; + } + // null is for value type serializer, which we don't have access to from here (ditto for bean prop) + boolean staticTyping = usesStaticTyping(config, beanDesc, null); + PropertyBuilder pb = constructPropertyBuilder(config, beanDesc); + + ArrayList result = new ArrayList(properties.size()); + final boolean fixAccess = config.canOverrideAccessModifiers(); + final boolean forceAccess = fixAccess && config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS); + for (BeanPropertyDefinition property : properties) { + final AnnotatedMember accessor = property.getAccessor(); + // Type id? Requires special handling: + if (property.isTypeId()) { + if (accessor != null) { // only add if we can access... but otherwise? + if (fixAccess) { + accessor.fixAccess(forceAccess); + } + builder.setTypeId(accessor); + } + continue; + } + // suppress writing of back references + AnnotationIntrospector.ReferenceProperty refType = property.findReferenceType(); + if (refType != null && refType.isBackReference()) { + continue; + } + if (accessor instanceof AnnotatedMethod) { + result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedMethod) accessor)); + } else { + result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedField) accessor)); + } + } + return result; + } + + /* + /********************************************************** + /* Overridable non-public methods for manipulating bean properties + /********************************************************** + */ + + /** + * Overridable method that can filter out properties. Default implementation + * checks annotations class may have. + */ + protected List filterBeanProperties(SerializationConfig config, + BeanDescription beanDesc, List props) + { + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + AnnotatedClass ac = beanDesc.getClassInfo(); + String[] ignored = intr.findPropertiesToIgnore(ac, true); + if (ignored != null && ignored.length > 0) { + HashSet ignoredSet = ArrayBuilders.arrayToSet(ignored); + Iterator it = props.iterator(); + while (it.hasNext()) { + if (ignoredSet.contains(it.next().getName())) { + it.remove(); + } + } + } + return props; + } + + /** + * Method called to handle view information for constructed serializer, + * based on bean property writers. + *

+ * Note that this method is designed to be overridden by sub-classes + * if they want to provide custom view handling. As such it is not + * considered an internal implementation detail, and will be supported + * as part of API going forward. + */ + protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) + { + // [JACKSON-232]: whether non-annotated fields are included by default or not is configurable + List props = builder.getProperties(); + boolean includeByDefault = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); + final int propCount = props.size(); + int viewsFound = 0; + BeanPropertyWriter[] filtered = new BeanPropertyWriter[propCount]; + // Simple: view information is stored within individual writers, need to combine: + for (int i = 0; i < propCount; ++i) { + BeanPropertyWriter bpw = props.get(i); + Class[] views = bpw.getViews(); + if (views == null) { // no view info? include or exclude by default? + if (includeByDefault) { + filtered[i] = bpw; + } + } else { + ++viewsFound; + filtered[i] = constructFilteredBeanWriter(bpw, views); + } + } + // minor optimization: if no view info, include-by-default, can leave out filtering info altogether: + if (includeByDefault && viewsFound == 0) { + return; + } + builder.setFilteredProperties(filtered); + } + + /** + * Method that will apply by-type limitations (as per [JACKSON-429]); + * by default this is based on {@link com.fasterxml.jackson.annotation.JsonIgnoreType} + * annotation but can be supplied by module-provided introspectors too. + */ + protected void removeIgnorableTypes(SerializationConfig config, BeanDescription beanDesc, + List properties) + { + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + HashMap,Boolean> ignores = new HashMap,Boolean>(); + Iterator it = properties.iterator(); + while (it.hasNext()) { + BeanPropertyDefinition property = it.next(); + AnnotatedMember accessor = property.getAccessor(); + if (accessor == null) { + it.remove(); + continue; + } + Class type = accessor.getRawType(); + Boolean result = ignores.get(type); + if (result == null) { + BeanDescription desc = config.introspectClassAnnotations(type); + AnnotatedClass ac = desc.getClassInfo(); + result = intr.isIgnorableType(ac); + // default to false, non-ignorable + if (result == null) { + result = Boolean.FALSE; + } + ignores.put(type, result); + } + // lotsa work, and yes, it is ignorable type, so: + if (result.booleanValue()) { + it.remove(); + } + } + } + + /** + * Helper method that will remove all properties that do not have a mutator. + */ + protected void removeSetterlessGetters(SerializationConfig config, BeanDescription beanDesc, + List properties) + { + Iterator it = properties.iterator(); + while (it.hasNext()) { + BeanPropertyDefinition property = it.next(); + // one caveat: as per [JACKSON-806], only remove implicit properties; + // explicitly annotated ones should remain + if (!property.couldDeserialize() && !property.isExplicitlyIncluded()) { + it.remove(); + } + } + } + + /** + * Helper method called to ensure that we do not have "duplicate" type ids. + * Added to resolve [databind#222] + * + * @since 2.6 + */ + protected List removeOverlappingTypeIds(SerializerProvider prov, + BeanDescription beanDesc, BeanSerializerBuilder builder, + List props) + { + for (int i = 0, end = props.size(); i < end; ++i) { + BeanPropertyWriter bpw = props.get(i); + TypeSerializer td = bpw.getTypeSerializer(); + if ((td == null) || (td.getTypeInclusion() != As.EXTERNAL_PROPERTY)) { + continue; + } + String n = td.getPropertyName(); + PropertyName typePropName = PropertyName.construct(n); + + for (BeanPropertyWriter w2 : props) { + if ((w2 != bpw) && w2.wouldConflictWithName(typePropName)) { + bpw.assignTypeSerializer(null); + break; + } + } + } + return props; + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + /** + * Secondary helper method for constructing {@link BeanPropertyWriter} for + * given member (field or method). + */ + protected BeanPropertyWriter _constructWriter(SerializerProvider prov, + BeanPropertyDefinition propDef, + PropertyBuilder pb, boolean staticTyping, AnnotatedMember accessor) + throws JsonMappingException + { + final PropertyName name = propDef.getFullName(); + if (prov.canOverrideAccessModifiers()) { + accessor.fixAccess(prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + JavaType type = accessor.getType(); + BeanProperty.Std property = new BeanProperty.Std(name, type, propDef.getWrapperName(), + pb.getClassAnnotations(), accessor, propDef.getMetadata()); + + // Does member specify a serializer? If so, let's use it. + JsonSerializer annotatedSerializer = findSerializerFromAnnotation(prov, + accessor); + // Unlike most other code paths, serializer produced + // here will NOT be resolved or contextualized, unless done here, so: + if (annotatedSerializer instanceof ResolvableSerializer) { + ((ResolvableSerializer) annotatedSerializer).resolve(prov); + } + // 05-Sep-2013, tatu: should be primary property serializer so: + annotatedSerializer = prov.handlePrimaryContextualization(annotatedSerializer, property); + // And how about polymorphic typing? First special to cover JAXB per-field settings: + TypeSerializer contentTypeSer = null; + // 16-Feb-2014, cgc: contentType serializers for collection-like and map-like types + if (type.isContainerType() || type.isReferenceType()) { + contentTypeSer = findPropertyContentTypeSerializer(type, prov.getConfig(), accessor); + } + // and if not JAXB collection/array with annotations, maybe regular type info? + TypeSerializer typeSer = findPropertyTypeSerializer(type, prov.getConfig(), accessor); + BeanPropertyWriter pbw = pb.buildWriter(prov, propDef, type, annotatedSerializer, + typeSer, contentTypeSer, accessor, staticTyping); + return pbw; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,187 @@ +package com.fasterxml.jackson.databind.ser; + +import java.util.List; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.DeserializerFactory; +import com.fasterxml.jackson.databind.type.*; + +/** + * Abstract class that defines API for objects that can be registered (for {@link BeanSerializerFactory} + * to participate in constructing {@link BeanSerializer} instances. + * This is typically done by modules that want alter some aspects of serialization + * process; and is preferable to sub-classing of {@link BeanSerializerFactory}. + *

+ * Sequence in which callback methods are called is as follows: + *

    + *
  1. After factory has collected tentative set of properties (instances of + * BeanPropertyWriter) is sent for modification via + * {@link #changeProperties}. Changes can include removal, addition and + * replacement of suggested properties. + *
  2. Resulting set of properties are ordered (sorted) by factory, as per + * configuration, and then {@link #orderProperties} is called to allow + * modifiers to alter ordering. + *
  3. After all bean properties and related information is accumulated, + * {@link #updateBuilder} is called with builder, to allow builder state + * to be modified (including possibly replacing builder itself if necessary) + *
  4. Once all bean information has been determined, + * factory creates default {@link BeanSerializer} instance and passes + * it to modifiers using {@link #modifySerializer}, for possible + * modification or replacement (by any {@link com.fasterxml.jackson.databind.JsonSerializer} instance) + *
+ *

+ * Default method implementations are "no-op"s, meaning that methods are implemented + * but have no effect. + */ +public abstract class BeanSerializerModifier +{ + /** + * Method called by {@link BeanSerializerFactory} with tentative set + * of discovered properties. + * Implementations can add, remove or replace any of passed properties. + * + * Properties List passed as argument is modifiable, and returned List must + * likewise be modifiable as it may be passed to multiple registered + * modifiers. + */ + public List changeProperties(SerializationConfig config, + BeanDescription beanDesc, List beanProperties) { + return beanProperties; + } + + /** + * Method called by {@link BeanSerializerFactory} with set of properties + * to serialize, in default ordering (based on defaults as well as + * possible type annotations). + * Implementations can change ordering any way they like. + * + * Properties List passed as argument is modifiable, and returned List must + * likewise be modifiable as it may be passed to multiple registered + * modifiers. + */ + public List orderProperties(SerializationConfig config, + BeanDescription beanDesc, List beanProperties) { + return beanProperties; + } + + /** + * Method called by {@link BeanSerializerFactory} after collecting all information + * regarding POJO to serialize and updating builder with it, but before constructing + * serializer. + * Implementations may choose to modify state of builder (to affect serializer being + * built), or even completely replace it (if they want to build different kind of + * serializer). Typically, however, passed-in builder is returned, possibly with + * some modifications. + */ + public BeanSerializerBuilder updateBuilder(SerializationConfig config, + BeanDescription beanDesc, BeanSerializerBuilder builder) { + return builder; + } + + /** + * Method called by {@link BeanSerializerFactory} after constructing default + * bean serializer instance with properties collected and ordered earlier. + * Implementations can modify or replace given serializer and return serializer + * to use. Note that although initial serializer being passed is of type + * {@link BeanSerializer}, modifiers may return serializers of other types; + * and this is why implementations must check for type before casting. + *

+ * NOTE: since 2.2, gets called for serializer of those non-POJO types that + * do not go through any of more specific modifyXxxSerializer + * methods; mostly for JDK types like {@link java.util.Iterator} and such. + */ + public JsonSerializer modifySerializer(SerializationConfig config, + BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /* + /********************************************************** + /* Callback methods for other types (since 2.2) + /********************************************************** + */ + + /** + * Method called by {@link DeserializerFactory} after it has constructed the + * standard serializer for given + * {@link ArrayType} + * to make it possible to either replace or augment this serializer with + * additional functionality. + * + * @param config Configuration in use + * @param valueType Type of the value serializer is used for. + * @param beanDesc Details of the type in question, to allow checking class annotations + * @param serializer Default serializer that would be used. + * + * @return Serializer to use; either serializer that was passed + * in, or an instance method constructed. + * + * @since 2.2 + */ + public JsonSerializer modifyArraySerializer(SerializationConfig config, + ArrayType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /** + * @since 2.2 + */ + public JsonSerializer modifyCollectionSerializer(SerializationConfig config, + CollectionType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /** + * @since 2.2 + */ + public JsonSerializer modifyCollectionLikeSerializer(SerializationConfig config, + CollectionLikeType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /** + * @since 2.2 + */ + public JsonSerializer modifyMapSerializer(SerializationConfig config, + MapType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /** + * @since 2.2 + */ + public JsonSerializer modifyMapLikeSerializer(SerializationConfig config, + MapLikeType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /** + * @since 2.2 + */ + public JsonSerializer modifyEnumSerializer(SerializationConfig config, + JavaType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } + + /** + * Method called by {@link DeserializerFactory} after it has constructed the + * default key serializer to use for serializing {@link java.util.Map} keys of + * given type. + * This makes it possible to either replace or augment default serializer with + * additional functionality. + * + * @param config Configuration in use + * @param valueType Type of keys the serializer is used for. + * @param beanDesc Details of the type in question, to allow checking class annotations + * @param serializer Default serializer that would be used. + * + * @return Serializer to use; either serializer that was passed + * in, or an instance method constructed. + * + * @since 2.2 + */ + public JsonSerializer modifyKeySerializer(SerializationConfig config, + JavaType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ContainerSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ContainerSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ContainerSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,157 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +/** + * Intermediate base class for serializers used for serializing + * types that contain element(s) of other types, such as arrays, + * {@link java.util.Collection}s (Lists, Sets + * etc) and {@link java.util.Map}s and iterable things + * ({@link java.util.Iterator}s). + */ +@SuppressWarnings("serial") +public abstract class ContainerSerializer + extends StdSerializer +{ + /* + /********************************************************** + /* Construction, initialization + /********************************************************** + */ + + protected ContainerSerializer(Class t) { + super(t); + } + + /** + * @since 2.5 + */ + protected ContainerSerializer(JavaType fullType) { + super(fullType); + } + + /** + * Alternate constructor that is (alas!) needed to work + * around kinks of generic type handling + * + * @param t + */ + protected ContainerSerializer(Class t, boolean dummy) { + super(t, dummy); + } + + protected ContainerSerializer(ContainerSerializer src) { + super(src._handledType, false); + } + + /** + * Factory(-like) method that can be used to construct a new container + * serializer that uses specified {@link TypeSerializer} for decorating + * contained values with additional type information. + * + * @param vts Type serializer to use for contained values; can be null, + * in which case 'this' serializer is returned as is + * @return Serializer instance that uses given type serializer for values if + * that is possible (or if not, just 'this' serializer) + */ + public ContainerSerializer withValueTypeSerializer(TypeSerializer vts) { + if (vts == null) return this; + return _withValueTypeSerializer(vts); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Accessor for finding declared (static) element type for + * type this serializer is used for. + */ + public abstract JavaType getContentType(); + + /** + * Accessor for serializer used for serializing contents + * (List and array elements, Map values etc) of the + * container for which this serializer is used, if it is + * known statically. + * Note that for dynamic types this may return null; if so, + * caller has to instead use {@link #getContentType()} and + * {@link com.fasterxml.jackson.databind.SerializerProvider#findValueSerializer}. + */ + public abstract JsonSerializer getContentSerializer(); + + /* + /********************************************************** + /* Abstract methods for sub-classes to implement + /********************************************************** + */ + + /* Overridden as abstract, to force re-implementation; necessary for all + * collection types. + */ + @Override + @Deprecated + public boolean isEmpty(T value) { + return isEmpty(null, value); + } + + // since 2.5: should be declared abstract in future (2.6) +// @Override +// public abstract boolean isEmpty(SerializerProvider prov, T value); + + /** + * Method called to determine if the given value (of type handled by + * this serializer) contains exactly one element. + *

+ * Note: although it might seem sensible to instead define something + * like "getElementCount()" method, this would not work well for + * containers that do not keep track of size (like linked lists may + * not). + */ + public abstract boolean hasSingleElement(T value); + + /** + * Method that needs to be implemented to allow construction of a new + * serializer object with given {@link TypeSerializer}, used when + * addition type information is to be embedded. + */ + protected abstract ContainerSerializer _withValueTypeSerializer(TypeSerializer vts); + + /* + /********************************************************** + /* Helper methods for sub-types + /********************************************************** + */ + + /** + * Helper method used to encapsulate logic for determining whether there is + * a property annotation that overrides element type; if so, we can + * and need to statically find the serializer. + * + * @since 2.1 + * + * @deprecated Since 2.7: should not be needed; should be enough to see if + * type has 'isStatic' modifier + */ + @Deprecated + protected boolean hasContentTypeAnnotation(SerializerProvider provider, + BeanProperty property) + { + /* + if (property != null) { + AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + AnnotatedMember m = property.getMember(); + if ((m != null) && (intr != null)) { + if (intr.findSerializationContentType(m, property.getType()) != null) { + return true; + } + } + } + */ + return false; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ContextualSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ContextualSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ContextualSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,40 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.databind.*; + +/** + * Add-on interface that {@link JsonSerializer}s can implement to get a callback + * that can be used to create contextual instances of serializer to use for + * handling properties of supported type. This can be useful + * for serializers that can be configured by annotations, or should otherwise + * have differing behavior depending on what kind of property is being serialized. + *

+ * Note that in cases where serializer needs both contextualization and + * resolution -- that is, implements both this interface and {@link ResolvableSerializer} + * -- resolution via {@link ResolvableSerializer} occurs first, and contextual + * resolution (via this interface) later on. + */ +public interface ContextualSerializer +{ + /** + * Method called to see if a different (or differently configured) serializer + * is needed to serialize values of specified property. + * Note that instance that this method is called on is typically shared one and + * as a result method should NOT modify this instance but rather construct + * and return a new instance. This instance should only be returned as-is, in case + * it is already suitable for use. + * + * @param prov Serializer provider to use for accessing config, other serializers + * @param property Method or field that represents the property + * (and is used to access value to serialize). + * Should be available; but there may be cases where caller can not provide it and + * null is passed instead (in which case impls usually pass 'this' serializer as is) + * + * @return Serializer to use for serializing values of specified property; + * may be this instance or a new instance. + * + * @throws JsonMappingException + */ + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,612 @@ +package com.fasterxml.jackson.databind.ser; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; +import com.fasterxml.jackson.databind.util.ClassUtil; + +/** + * Standard implementation used by {@link ObjectMapper}: + * adds methods only exposed to {@link ObjectMapper}, + * as well as constructors. + *

+ * Note that class is abstract just because it does not + * define {@link #createInstance} method. + *

+ * Also note that all custom {@link SerializerProvider} + * implementations must sub-class this class: {@link ObjectMapper} + * requires this type, not basic provider type. + */ +public abstract class DefaultSerializerProvider + extends SerializerProvider + implements java.io.Serializable // since 2.1; only because ObjectWriter needs it +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* State, for non-blueprint instances: Object Id handling + /********************************************************** + */ + + /** + * Per-serialization map Object Ids that have seen so far, iff + * Object Id handling is enabled. + */ + protected transient Map _seenObjectIds; + + protected transient ArrayList> _objectIdGenerators; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected DefaultSerializerProvider() { super(); } + + protected DefaultSerializerProvider(SerializerProvider src, + SerializationConfig config,SerializerFactory f) { + super(src, config, f); + } + + protected DefaultSerializerProvider(DefaultSerializerProvider src) { + super(src); + } + + /** + * Method needed to ensure that {@link ObjectMapper#copy} will work + * properly; specifically, that caches are cleared, but settings + * will otherwise remain identical; and that no sharing of state + * occurs. + * + * @since 2.5 + */ + public DefaultSerializerProvider copy() { + throw new IllegalStateException("DefaultSerializerProvider sub-class not overriding copy()"); + } + + /* + /********************************************************** + /* Extended API: methods that ObjectMapper will call + /********************************************************** + */ + + /** + * Overridable method, used to create a non-blueprint instances from the blueprint. + * This is needed to retain state during serialization. + */ + public abstract DefaultSerializerProvider createInstance(SerializationConfig config, + SerializerFactory jsf); + + /** + * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} + * for serializing given value, using serializers that + * this provider has access to (via caching and/or creating new serializers + * as need be). + */ + public void serializeValue(JsonGenerator gen, Object value) throws IOException + { + if (value == null) { + _serializeNull(gen); + return; + } + Class cls = value.getClass(); + // true, since we do want to cache root-level typed serializers (ditto for null property) + final JsonSerializer ser = findTypedValueSerializer(cls, true, null); + + // Ok: should we wrap result in an additional property ("root name")? + final boolean wrap; + PropertyName rootName = _config.getFullRootName(); + + if (rootName == null) { // not explicitly specified + // [JACKSON-163] + wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); + if (wrap) { + gen.writeStartObject(); + PropertyName pname = _config.findRootName(value.getClass()); + gen.writeFieldName(pname.simpleAsEncoded(_config)); + } + } else if (rootName.isEmpty()) { + wrap = false; + } else { // [JACKSON-764] + // empty String means explicitly disabled; non-empty that it is enabled + wrap = true; + gen.writeStartObject(); + gen.writeFieldName(rootName.getSimpleName()); + } + try { + ser.serialize(value, gen, this); + if (wrap) { + gen.writeEndObject(); + } + } catch (IOException ioe) { // As per [JACKSON-99], pass IOException and subtypes as-is + throw ioe; + } catch (Exception e) { // but wrap RuntimeExceptions, to get path information + String msg = e.getMessage(); + if (msg == null) { + msg = "[no message for "+e.getClass().getName()+"]"; + } + throw new JsonMappingException(gen, msg, e); + } + } + + /** + * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} + * for serializing given value (assumed to be of specified root type, + * instead of runtime type of value), + * using serializers that + * this provider has access to (via caching and/or creating new serializers + * as need be), + * + * @param rootType Type to use for locating serializer to use, instead of actual + * runtime type. Must be actual type, or one of its super types + */ + public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) throws IOException + { + if (value == null) { + _serializeNull(gen); + return; + } + // Let's ensure types are compatible at this point + if (!rootType.getRawClass().isAssignableFrom(value.getClass())) { + _reportIncompatibleRootType(value, rootType); + } + // root value, not reached via property: + JsonSerializer ser = findTypedValueSerializer(rootType, true, null); + + // Ok: should we wrap result in an additional property ("root name")? + final boolean wrap; + PropertyName rootName = _config.getFullRootName(); + if (rootName == null) { // not explicitly specified + // [JACKSON-163] + wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); + if (wrap) { + gen.writeStartObject(); + PropertyName pname = _config.findRootName(value.getClass()); + gen.writeFieldName(pname.simpleAsEncoded(_config)); + } + } else if (rootName.isEmpty()) { + wrap = false; + } else { // [JACKSON-764] + // empty String means explicitly disabled; non-empty that it is enabled + wrap = true; + gen.writeStartObject(); + gen.writeFieldName(rootName.getSimpleName()); + } + try { + ser.serialize(value, gen, this); + if (wrap) { + gen.writeEndObject(); + } + } catch (IOException ioe) { // no wrapping for IO (and derived) + throw ioe; + } catch (Exception e) { // but others do need to be, to get path etc + String msg = e.getMessage(); + if (msg == null) { + msg = "[no message for "+e.getClass().getName()+"]"; + } + throw JsonMappingException.from(gen, msg, e); + } + } + + /** + * The method to be called by {@link ObjectWriter} + * for serializing given value (assumed to be of specified root type, + * instead of runtime type of value), when it may know specific + * {@link JsonSerializer} to use. + * + * @param rootType Type to use for locating serializer to use, instead of actual + * runtime type, if no serializer is passed + * @param ser Root Serializer to use, if not null + * + * @since 2.1 + */ + public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, + JsonSerializer ser) throws IOException + { + if (value == null) { + _serializeNull(gen); + return; + } + // Let's ensure types are compatible at this point + if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) { + _reportIncompatibleRootType(value, rootType); + } + // root value, not reached via property: + if (ser == null) { + ser = findTypedValueSerializer(rootType, true, null); + } + // Ok: should we wrap result in an additional property ("root name")? + final boolean wrap; + PropertyName rootName = _config.getFullRootName(); + if (rootName == null) { // not explicitly specified + // [JACKSON-163] + wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); + if (wrap) { + gen.writeStartObject(); + PropertyName pname = (rootType == null) + ? _config.findRootName(value.getClass()) + : _config.findRootName(rootType); + gen.writeFieldName(pname.simpleAsEncoded(_config)); + } + } else if (rootName.isEmpty()) { + wrap = false; + } else { // [JACKSON-764] + // empty String means explicitly disabled; non-empty that it is enabled + wrap = true; + gen.writeStartObject(); + gen.writeFieldName(rootName.getSimpleName()); + } + try { + ser.serialize(value, gen, this); + if (wrap) { + gen.writeEndObject(); + } + } catch (IOException ioe) { // no wrapping for IO (and derived) + throw ioe; + } catch (Exception e) { // but others do need to be, to get path etc + String msg = e.getMessage(); + if (msg == null) { + msg = "[no message for "+e.getClass().getName()+"]"; + } + throw JsonMappingException.from(gen, msg, e); + } + } + + /** + * Alternate serialization call used for polymorphic types, when {@link TypeSerializer} + * is already known, but the actual serializer may or may not be. + * + * @since 2.6 + */ + public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType, + JsonSerializer valueSer, TypeSerializer typeSer) + throws IOException + { + if (value == null) { + _serializeNull(gen); + return; + } + // Let's ensure types are compatible at this point + if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) { + _reportIncompatibleRootType(value, rootType); + } + /* 12-Jun-2015, tatu: nominal root type is necessary for Maps at least; + * possibly collections, but can cause problems for other polymorphic + * types. We really need to distinguish between serialization type, + * base type; but right we don't. Hence this check + */ + if (valueSer == null) { + if ((rootType != null) && rootType.isContainerType()) { + valueSer = findValueSerializer(rootType, null); + } else { + valueSer = findValueSerializer(value.getClass(), null); + } + } + + final boolean wrap; + PropertyName rootName = _config.getFullRootName(); + if (rootName == null) { + wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); + if (wrap) { + gen.writeStartObject(); + PropertyName pname = _config.findRootName(value.getClass()); + gen.writeFieldName(pname.simpleAsEncoded(_config)); + } + } else if (rootName.isEmpty()) { + wrap = false; + } else { + wrap = true; + gen.writeStartObject(); + gen.writeFieldName(rootName.getSimpleName()); + } + try { + valueSer.serializeWithType(value, gen, this, typeSer); + if (wrap) { + gen.writeEndObject(); + } + } catch (IOException ioe) { // no wrapping for IO (and derived) + throw ioe; + } catch (Exception e) { // but others do need to be, to get path etc + String msg = e.getMessage(); + if (msg == null) { + msg = "[no message for "+e.getClass().getName()+"]"; + } + throw JsonMappingException.from(gen, msg, e); + } + } + + /** + * @deprecated since 2.6; remove from 2.7 or later + */ + @Deprecated + public void serializePolymorphic(JsonGenerator gen, Object value, TypeSerializer typeSer) + throws IOException + { + JavaType t = (value == null) ? null : _config.constructType(value.getClass()); + serializePolymorphic(gen, value, t, null, typeSer); + } + + /** + * Helper method called when root value to serialize is null + * + * @since 2.3 + */ + protected void _serializeNull(JsonGenerator gen) throws IOException + { + JsonSerializer ser = getDefaultNullValueSerializer(); + try { + ser.serialize(null, gen, this); + } catch (IOException ioe) { // no wrapping for IO (and derived) + throw ioe; + } catch (Exception e) { // but others do need to be, to get path etc + String msg = e.getMessage(); + if (msg == null) { + msg = "[no message for "+e.getClass().getName()+"]"; + } + throw JsonMappingException.from(gen, msg, e); + } + } + + /** + * The method to be called by {@link ObjectMapper} + * to generate JSON schema for + * given type. + * + * @param type The type for which to generate schema + * + * @deprecated Should not be used any more + */ + @Deprecated // since 2.6 + public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class type) + throws JsonMappingException + { + if (type == null) { + throw new IllegalArgumentException("A class must be provided"); + } + /* no need for embedded type information for JSON schema generation (all + * type information it needs is accessible via "untyped" serializer) + */ + JsonSerializer ser = findValueSerializer(type, null); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(this, null) : com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); + if (!(schemaNode instanceof ObjectNode)) { + throw new IllegalArgumentException("Class " + type.getName() + +" would not be serialized as a JSON object and therefore has no schema"); + } + return new com.fasterxml.jackson.databind.jsonschema.JsonSchema((ObjectNode) schemaNode); + } + + /** + * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} + * to to expose the format of the given to to the given visitor + * + * @param javaType The type for which to generate format + * @param visitor the visitor to accept the format + */ + public void acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper visitor) + throws JsonMappingException + { + if (javaType == null) { + throw new IllegalArgumentException("A class must be provided"); + } + /* no need for embedded type information for JSON schema generation (all + * type information it needs is accessible via "untyped" serializer) + */ + visitor.setProvider(this); + findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType); + } + + /** + * Method that can be called to see if this serializer provider + * can find a serializer for an instance of given class. + *

+ * Note that no Exceptions are thrown, including unchecked ones: + * implementations are to swallow exceptions if necessary. + */ + public boolean hasSerializerFor(Class cls, AtomicReference cause) + { + // 07-Nov-2015, tatu: One special case, Object.class; will work only if + // empty beans are allowed or custom serializer registered. Easiest to + // check here. + if (cls == Object.class) { + if (!_config.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { + return true; + } + } + + try { + JsonSerializer ser = _findExplicitUntypedSerializer(cls); + return (ser != null); + } catch (JsonMappingException e) { + if (cause != null) { + cause.set(e); + } + } catch (RuntimeException e) { + if (cause == null) { // earlier behavior + throw e; + } + cause.set(e); + } + return false; + } + + /* + /******************************************************** + /* Access to caching details + /******************************************************** + */ + + /** + * Method that can be used to determine how many serializers this + * provider is caching currently + * (if it does caching: default implementation does) + * Exact count depends on what kind of serializers get cached; + * default implementation caches all serializers, including ones that + * are eagerly constructed (for optimal access speed) + *

+ * The main use case for this method is to allow conditional flushing of + * serializer cache, if certain number of entries is reached. + */ + public int cachedSerializersCount() { + return _serializerCache.size(); + } + + /** + * Method that will drop all serializers currently cached by this provider. + * This can be used to remove memory usage (in case some serializers are + * only used once or so), or to force re-construction of serializers after + * configuration changes for mapper than owns the provider. + */ + public void flushCachedSerializers() { + _serializerCache.flush(); + } + + /* + /********************************************************** + /* Object Id handling + /********************************************************** + */ + + @Override + public WritableObjectId findObjectId(Object forPojo, ObjectIdGenerator generatorType) + { + if (_seenObjectIds == null) { + _seenObjectIds = _createObjectIdMap(); + } else { + WritableObjectId oid = _seenObjectIds.get(forPojo); + if (oid != null) { + return oid; + } + } + // Not seen yet; must add an entry, return it. For that, we need generator + ObjectIdGenerator generator = null; + + if (_objectIdGenerators == null) { + _objectIdGenerators = new ArrayList>(8); + } else { + for (int i = 0, len = _objectIdGenerators.size(); i < len; ++i) { + ObjectIdGenerator gen = _objectIdGenerators.get(i); + if (gen.canUseFor(generatorType)) { + generator = gen; + break; + } + } + } + if (generator == null) { + generator = generatorType.newForSerialization(this); + _objectIdGenerators.add(generator); + } + WritableObjectId oid = new WritableObjectId(generator); + _seenObjectIds.put(forPojo, oid); + return oid; + } + + /** + * Overridable helper method used for creating {@link java.util.Map} + * used for storing mappings from serializable objects to their + * Object Ids. + * + * @since 2.3 + */ + protected Map _createObjectIdMap() + { + /* 06-Aug-2013, tatu: We may actually want to use equality, + * instead of identity... so: + */ + if (isEnabled(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID)) { + return new HashMap(); + } + return new IdentityHashMap(); + } + + /* + /********************************************************** + /* Factory method impls + /********************************************************** + */ + + @Override + public JsonSerializer serializerInstance(Annotated annotated, Object serDef) throws JsonMappingException + { + if (serDef == null) { + return null; + } + JsonSerializer ser; + + if (serDef instanceof JsonSerializer) { + ser = (JsonSerializer) serDef; + } else { + /* Alas, there's no way to force return type of "either class + * X or Y" -- need to throw an exception after the fact + */ + if (!(serDef instanceof Class)) { + throw new IllegalStateException("AnnotationIntrospector returned serializer definition of type " + +serDef.getClass().getName()+"; expected type JsonSerializer or Class instead"); + } + Class serClass = (Class)serDef; + // there are some known "no class" markers to consider too: + if (serClass == JsonSerializer.None.class || ClassUtil.isBogusClass(serClass)) { + return null; + } + if (!JsonSerializer.class.isAssignableFrom(serClass)) { + throw new IllegalStateException("AnnotationIntrospector returned Class " + +serClass.getName()+"; expected Class"); + } + HandlerInstantiator hi = _config.getHandlerInstantiator(); + ser = (hi == null) ? null : hi.serializerInstance(_config, annotated, serClass); + if (ser == null) { + ser = (JsonSerializer) ClassUtil.createInstance(serClass, + _config.canOverrideAccessModifiers()); + } + } + return (JsonSerializer) _handleResolvable(ser); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Concrete implementation that defines factory method(s), + * defined as final. + */ + public final static class Impl extends DefaultSerializerProvider { + private static final long serialVersionUID = 1L; + + public Impl() { super(); } + public Impl(Impl src) { super(src); } + + protected Impl(SerializerProvider src, SerializationConfig config,SerializerFactory f) { + super(src, config, f); + } + + @Override + public DefaultSerializerProvider copy() + { + if (getClass() != Impl.class) { + return super.copy(); + } + return new Impl(this); + } + + @Override + public Impl createInstance(SerializationConfig config, SerializerFactory jsf) { + return new Impl(this, config, jsf); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/FilterProvider.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/FilterProvider.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/FilterProvider.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,58 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; + +/** + * Interface for objects that providers instances of {@link PropertyFilter} + * that match given ids. A provider is configured to be used during serialization, + * to find filter to used based on id specified by {@link com.fasterxml.jackson.annotation.JsonFilter} + * annotation on bean class. + */ +public abstract class FilterProvider +{ + /** + * Lookup method used to find {@link BeanPropertyFilter} that has specified id. + * Note that id is typically a {@link java.lang.String}, but is not necessarily + * limited to that; that is, while standard components use String, custom + * implementation can choose other kinds of keys. + * + * @return Filter registered with specified id, if one defined; null if + * none found. + * + * @deprecated Since 2.3 deprecated because {@link BeanPropertyFilter} is deprecated; + */ + @Deprecated + public abstract BeanPropertyFilter findFilter(Object filterId); + + /** + * Lookup method used to find {@link PropertyFilter} that has specified id. + * Note that id is typically a {@link java.lang.String}, but is not necessarily + * limited to that; that is, while standard components use String, custom + * implementation can choose other kinds of keys. + *

+ * This method is the replacement for {@link #findFilter} starting with 2.3. + *

+ * Note that the default implementation is designed to support short-term + * backwards compatibility, and will call the deprecated findFilter + * method, then wrap filter if one found as {@link PropertyFilter}. + * It should be overridden by up-to-date implementations + * + * @param filterId Id of the filter to fetch + * @param valueToFilter Object being filtered (usually POJO, but may be a {@link java.util.Map}, + * or in future a container), if available; not available when generating + * schemas. + * + * @return Filter to use, if any. + * + * @since 2.3 + */ + public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) + { + @SuppressWarnings("deprecation") + BeanPropertyFilter old = findFilter(filterId); + if (old == null) { + return null; + } + return SimpleBeanPropertyFilter.from(old); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,337 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.util.*; + +/** + * Helper class for {@link BeanSerializerFactory} that is used to + * construct {@link BeanPropertyWriter} instances. Can be sub-classed + * to change behavior. + */ +public class PropertyBuilder +{ + // @since 2.7 + private final static Object NO_DEFAULT_MARKER = Boolean.FALSE; + + final protected SerializationConfig _config; + final protected BeanDescription _beanDesc; + + /** + * Default inclusion mode for properties of the POJO for which + * properties are collected; possibly overridden on + * per-property basis. + */ + final protected JsonInclude.Value _defaultInclusion; + + final protected AnnotationIntrospector _annotationIntrospector; + + /** + * If a property has serialization inclusion value of + * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}, + * we may need to know the default value of the bean, to know if property value + * equals default one. + *

+ * NOTE: only used if enclosing class defines NON_DEFAULT, but NOT if it is the + * global default OR per-property override. + */ + protected Object _defaultBean; + + public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc) + { + _config = config; + _beanDesc = beanDesc; + _defaultInclusion = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion()); + _annotationIntrospector = _config.getAnnotationIntrospector(); + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + public Annotations getClassAnnotations() { + return _beanDesc.getClassAnnotations(); + } + + /** + * @param contentTypeSer Optional explicit type information serializer + * to use for contained values (only used for properties that are + * of container type) + */ + protected BeanPropertyWriter buildWriter(SerializerProvider prov, + BeanPropertyDefinition propDef, JavaType declaredType, JsonSerializer ser, + TypeSerializer typeSer, TypeSerializer contentTypeSer, + AnnotatedMember am, boolean defaultUseStaticTyping) + throws JsonMappingException + { + // do we have annotation that forces type to use (to declared type or its super type)? + JavaType serializationType = findSerializationType(am, defaultUseStaticTyping, declaredType); + + // Container types can have separate type serializers for content (value / element) type + if (contentTypeSer != null) { + /* 04-Feb-2010, tatu: Let's force static typing for collection, if there is + * type information for contents. Should work well (for JAXB case); can be + * revisited if this causes problems. + */ + if (serializationType == null) { +// serializationType = TypeFactory.type(am.getGenericType(), _beanDesc.getType()); + serializationType = declaredType; + } + JavaType ct = serializationType.getContentType(); + // Not exactly sure why, but this used to occur; better check explicitly: + if (ct == null) { + throw new IllegalStateException("Problem trying to create BeanPropertyWriter for property '" + +propDef.getName()+"' (of type "+_beanDesc.getType()+"); serialization type "+serializationType+" has no content"); + } + serializationType = serializationType.withContentTypeHandler(contentTypeSer); + ct = serializationType.getContentType(); + } + + Object valueToSuppress = null; + boolean suppressNulls = false; + + JsonInclude.Value inclV = _defaultInclusion.withOverrides(propDef.findInclusion()); + JsonInclude.Include inclusion = inclV.getValueInclusion(); + if (inclusion == JsonInclude.Include.USE_DEFAULTS) { // should not occur but... + inclusion = JsonInclude.Include.ALWAYS; + } + + /* + JsonInclude.Include inclusion = propDef.findInclusion().getValueInclusion(); + if (inclusion == JsonInclude.Include.USE_DEFAULTS) { // since 2.6 + inclusion = _defaultInclusion; + if (inclusion == null) { + inclusion = JsonInclude.Include.ALWAYS; + } + } + */ + + switch (inclusion) { + case NON_DEFAULT: + // 11-Nov-2015, tatu: This is tricky because semantics differ between cases, + // so that if enclosing class has this, we may need to values of property, + // whereas for global defaults OR per-property overrides, we have more + // static definition. Sigh. + // First: case of class specifying it; try to find POJO property defaults + JavaType t = (serializationType == null) ? declaredType : serializationType; + if (_defaultInclusion.getValueInclusion() == JsonInclude.Include.NON_DEFAULT) { + valueToSuppress = getPropertyDefaultValue(propDef.getName(), am, t); + } else { + valueToSuppress = getDefaultValue(t); + } + if (valueToSuppress == null) { + suppressNulls = true; + } else { + if (valueToSuppress.getClass().isArray()) { + valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); + } + } + + break; + case NON_ABSENT: // new with 2.6, to support Guava/JDK8 Optionals + // always suppress nulls + suppressNulls = true; + // and for referential types, also "empty", which in their case means "absent" + if (declaredType.isReferenceType()) { + valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY; + } + break; + case NON_EMPTY: + // always suppress nulls + suppressNulls = true; + // but possibly also 'empty' values: + valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY; + break; + case NON_NULL: + suppressNulls = true; + // fall through + case ALWAYS: // default + default: + // we may still want to suppress empty collections, as per [JACKSON-254]: + if (declaredType.isContainerType() + && !_config.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)) { + valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY; + } + break; + } + BeanPropertyWriter bpw = new BeanPropertyWriter(propDef, + am, _beanDesc.getClassAnnotations(), declaredType, + ser, typeSer, serializationType, suppressNulls, valueToSuppress); + + // How about custom null serializer? + Object serDef = _annotationIntrospector.findNullSerializer(am); + if (serDef != null) { + bpw.assignNullSerializer(prov.serializerInstance(am, serDef)); + } + // And then, handling of unwrapping + NameTransformer unwrapper = _annotationIntrospector.findUnwrappingNameTransformer(am); + if (unwrapper != null) { + bpw = bpw.unwrappingWriter(unwrapper); + } + return bpw; + } + + /* + /********************************************************** + /* Helper methods; annotation access + /********************************************************** + */ + + /** + * Method that will try to determine statically defined type of property + * being serialized, based on annotations (for overrides), and alternatively + * declared type (if static typing for serialization is enabled). + * If neither can be used (no annotations, dynamic typing), returns null. + */ + protected JavaType findSerializationType(Annotated a, boolean useStaticTyping, JavaType declaredType) + throws JsonMappingException + { + JavaType secondary = _annotationIntrospector.refineSerializationType(_config, a, declaredType); + // 11-Oct-2015, tatu: As of 2.7, not 100% sure following checks are needed. But keeping + // for now, just in case + if (secondary != declaredType) { + Class serClass = secondary.getRawClass(); + // Must be a super type to be usable + Class rawDeclared = declaredType.getRawClass(); + if (serClass.isAssignableFrom(rawDeclared)) { + ; // fine as is + } else { + /* 18-Nov-2010, tatu: Related to fixing [JACKSON-416], an issue with such + * check is that for deserialization more specific type makes sense; + * and for serialization more generic. But alas JAXB uses but a single + * annotation to do both... Hence, we must just discard type, as long as + * types are related + */ + if (!rawDeclared.isAssignableFrom(serClass)) { + throw new IllegalArgumentException("Illegal concrete-type annotation for method '"+a.getName()+"': class "+serClass.getName()+" not a super-type of (declared) class "+rawDeclared.getName()); + } + /* 03-Dec-2010, tatu: Actually, ugh, we may need to further relax this + * and actually accept subtypes too for serialization. Bit dangerous in theory + * but need to trust user here... + */ + } + useStaticTyping = true; + declaredType = secondary; + } + // If using static typing, declared type is known to be the type... + JsonSerialize.Typing typing = _annotationIntrospector.findSerializationTyping(a); + if ((typing != null) && (typing != JsonSerialize.Typing.DEFAULT_TYPING)) { + useStaticTyping = (typing == JsonSerialize.Typing.STATIC); + } + if (useStaticTyping) { + // 11-Oct-2015, tatu: Make sure JavaType also "knows" static-ness... + return declaredType.withStaticTyping(); + + } + return null; + } + + /* + /********************************************************** + /* Helper methods for default value handling + /********************************************************** + */ + + protected Object getDefaultBean() + { + Object def = _defaultBean; + if (def == null) { + /* If we can fix access rights, we should; otherwise non-public + * classes or default constructor will prevent instantiation + */ + def = _beanDesc.instantiateBean(_config.canOverrideAccessModifiers()); + if (def == null) { + // 06-Nov-2015, tatu: As per [databind#998], do not fail. + /* + Class cls = _beanDesc.getClassInfo().getAnnotated(); + throw new IllegalArgumentException("Class "+cls.getName()+" has no default constructor; can not instantiate default bean value to support 'properties=JsonSerialize.Inclusion.NON_DEFAULT' annotation"); + */ + + // And use a marker + def = NO_DEFAULT_MARKER; + } + _defaultBean = def; + } + return (def == NO_DEFAULT_MARKER) ? null : _defaultBean; + } + + /** + * Accessor used to find out "default value" for given property, to use for + * comparing values to serialize, to determine whether to exclude value from serialization with + * inclusion type of {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_EMPTY}. + * This method is called when we specifically want to know default value within context + * of a POJO, when annotation is within containing class, and not for property or + * defined as global baseline. + *

+ * Note that returning of pseudo-type + * + * @since 2.7 + */ + protected Object getPropertyDefaultValue(String name, AnnotatedMember member, + JavaType type) + { + Object defaultBean = getDefaultBean(); + if (defaultBean == null) { + return getDefaultValue(type); + } + try { + return member.getValue(defaultBean); + } catch (Exception e) { + return _throwWrapped(e, name, defaultBean); + } + } + + /** + * Accessor used to find out "default value" to use for comparing values to + * serialize, to determine whether to exclude value from serialization with + * inclusion type of {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT}. + *

+ * Default logic is such that for primitives and wrapper types for primitives, expected + * defaults (0 for `int` and `java.lang.Integer`) are returned; for Strings, empty String, + * and for structured (Maps, Collections, arrays) and reference types, criteria + * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_DEFAULT} + * is used. + * + * @since 2.7 + */ + protected Object getDefaultValue(JavaType type) + { + // 06-Nov-2015, tatu: Returning null is fine for Object types; but need special + // handling for primitives since they are never passed as nulls. + Class cls = type.getRawClass(); + + Class prim = ClassUtil.primitiveType(cls); + if (prim != null) { + return ClassUtil.defaultValue(prim); + } + if (type.isContainerType() || type.isReferenceType()) { + return JsonInclude.Include.NON_EMPTY; + } + if (cls == String.class) { + return ""; + } + return null; + } + + /* + /********************************************************** + /* Helper methods for exception handling + /********************************************************** + */ + + protected Object _throwWrapped(Exception e, String propName, Object defaultBean) + { + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + if (t instanceof Error) throw (Error) t; + if (t instanceof RuntimeException) throw (RuntimeException) t; + throw new IllegalArgumentException("Failed to get property '"+propName+"' of default "+defaultBean.getClass().getName()+" instance"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyFilter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyFilter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyFilter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,121 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Interface that defines API for filter objects use (as configured + * using {@link com.fasterxml.jackson.annotation.JsonFilter}) + * for filtering bean properties to serialize. + *

+ * Note that this is the replacement for BeanPropertyFilter, + * which is replaced because it was too closely bound to Bean properties + * and would not work with {@link java.util.Map}s or "any getters". + *

+ * Note that since this is an interface, it is + * strongly recommended that custom implementations extend + * {@link com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter}, + * to avoid backwards compatibility issues in case interface needs to change. + * + * @since 2.3 + */ +public interface PropertyFilter +{ + /** + * Method called by {@link BeanSerializer} to let the filter decide what to do with + * given bean property value: + * the usual choices are to either filter out (i.e. + * do nothing) or write using given {@link PropertyWriter}, although filters + * can choose other to do something different altogether. + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.serializeAsField(pojo, jgen, prov);
+     * }
+     *
+ * + * @param pojo Object that contains property value to serialize + * @param jgen Generator use for serializing value + * @param prov Provider that can be used for accessing dynamic aspects of serialization + * processing + * @param writer Object called to do actual serialization of the field, if not filtered out + */ + public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, + PropertyWriter writer) + throws Exception; + + /** + * Method called by container to let the filter decide what to do with given element + * value: + * the usual choices are to either filter out (i.e. + * do nothing) or write using given {@link PropertyWriter}, although filters + * can choose other to do something different altogether. + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.serializeAsElement(pojo, jgen, prov);
+     * }
+     *
+ * + * @param elementValue Element value being serializerd + * @param jgen Generator use for serializing value + * @param prov Provider that can be used for accessing dynamic aspects of serialization + * processing + * @param writer Object called to do actual serialization of the field, if not filtered out + */ + public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider prov, + PropertyWriter writer) + throws Exception; + + /** + * Method called by {@link BeanSerializer} to let the filter determine whether, and in what + * form the given property exist within the parent, or root, schema. Filters can omit + * adding the property to the node, or choose the form of the schema value for the property. + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.depositSchemaProperty(propertiesNode, provider);
+     * }
+     *
+ * + * @param writer Bean property writer to use to create schema value + * @param propertiesNode Node which the given property would exist within + * @param provider Provider that can be used for accessing dynamic aspects of serialization + * processing + * + * @deprecated Since 2.3: new code should use the alternative depositSchemaProperty + * method + */ + @Deprecated + public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, + SerializerProvider provider) + throws JsonMappingException; + + /** + * Method called by {@link BeanSerializer} to let the filter determine whether, and in what + * form the given property exist within the parent, or root, schema. Filters can omit + * adding the property to the node, or choose the form of the schema value for the property + *

+ * Typical implementation is something like: + *

+     * if (include(writer)) {
+     *      writer.depositSchemaProperty(objectVisitor, provider);
+     * }
+     *
+ * + * @param writer Bean property serializer to use to create schema value + * @param objectVisitor JsonObjectFormatVisitor which should be aware of + * the property's existence + * @param provider Provider that can be used for accessing dynamic aspects of serialization + * processing + */ + public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/PropertyWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,167 @@ +package com.fasterxml.jackson.databind.ser; + +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Base class for writers used to output property values (name-value pairs) + * as key/value pairs via streaming API. This is the most generic abstraction + * implemented by both POJO and {@link java.util.Map} serializers, and invoked + * by filtering functionality. + * + * @since 2.3 + */ +public abstract class PropertyWriter + extends ConcreteBeanPropertyBase // since 2.7 + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected PropertyWriter(PropertyMetadata md) { + super(md); + } + + protected PropertyWriter(BeanPropertyDefinition propDef) { + super(propDef.getMetadata()); + } + + protected PropertyWriter(PropertyWriter base) { + super(base); + } + + /* + /********************************************************** + /* Metadata access + /********************************************************** + */ + + @Override + public abstract String getName(); + + @Override + public abstract PropertyName getFullName(); + + /** + * Convenience method for accessing annotation that may be associated + * either directly on property, or, if not, via enclosing class (context). + * This allows adding baseline contextual annotations, for example, by adding + * an annotation for a given class and making that apply to all properties + * unless overridden by per-property annotations. + *

+ * This method is functionally equivalent to: + *

+     *  MyAnnotation ann = propWriter.getAnnotation(MyAnnotation.class);
+     *  if (ann == null) {
+     *    ann = propWriter.getContextAnnotation(MyAnnotation.class);
+     *  }
+     *
+ * that is, tries to find a property annotation first, but if one is not + * found, tries to find context-annotation (from enclosing class) of + * same type. + * + * @since 2.5 + */ + public A findAnnotation(Class acls) { + A ann = getAnnotation(acls); + if (ann == null) { + ann = getContextAnnotation(acls); + } + return ann; + } + + /** + * Method for accessing annotations directly declared for property that this + * writer is associated with. + * + * @since 2.5 + */ + @Override + public abstract A getAnnotation(Class acls); + + /** + * Method for accessing annotations declared in context of the property that this + * writer is associated with; usually this means annotations on enclosing class + * for property. + * + * @since 2.5 + */ + @Override + public abstract A getContextAnnotation(Class acls); + + /* + /********************************************************** + /* Serialization methods, regular output + /********************************************************** + */ + + /** + * The main serialization method called by filter when property is to be written normally. + */ + public abstract void serializeAsField(Object value, JsonGenerator jgen, SerializerProvider provider) + throws Exception; + + /** + * Serialization method that filter needs to call in cases where property is to be + * filtered, but the underlying data format requires a placeholder of some kind. + * This is usually the case for tabular (positional) data formats such as CSV. + */ + public abstract void serializeAsOmittedField(Object value, JsonGenerator jgen, SerializerProvider provider) + throws Exception; + + /* + /********************************************************** + /* Serialization methods, explicit positional/tabular formats + /********************************************************** + */ + + /** + * Serialization method called when output is to be done as an array, + * that is, not using property names. This is needed when serializing + * container ({@link java.util.Collection}, array) types, + * or POJOs using tabular ("as array") output format. + *

+ * Note that this mode of operation is independent of underlying + * data format; so it is typically NOT called for fully tabular formats such as CSV, + * where logical output is still as form of POJOs. + */ + public abstract void serializeAsElement(Object value, JsonGenerator jgen, SerializerProvider provider) + throws Exception; + + /** + * Serialization method called when doing tabular (positional) output from databind, + * but then value is to be omitted. This requires output of a placeholder value + * of some sort; often similar to {@link #serializeAsOmittedField}. + */ + public abstract void serializeAsPlaceholder(Object value, JsonGenerator jgen, SerializerProvider provider) + throws Exception; + + /* + /********************************************************** + /* Schema-related + /********************************************************** + */ + + /** + * Traversal method used for things like JSON Schema generation, or + * POJO introspection. + */ + @Override + public abstract void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) + throws JsonMappingException; + + /** + * Legacy method called for JSON Schema generation; should not be called by new code + * + * @deprecated Since 2.2 + */ + @Deprecated + public abstract void depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider provider) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,33 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.databind.*; + +/** + * Interface used to indicate serializers that want to do post-processing + * after construction and being added to {@link SerializerProvider}, + * but before being used. This is typically used to resolve references + * to other contained types; for example, bean serializers use this + * to eagerly find serializers for contained field types. + *

+ * Note that in cases where serializer needs both contextualization and + * resolution -- that is, implements both this interface and {@link ContextualSerializer} + * -- resolution via this interface occurs first, and contextual + * resolution (using {@link ContextualSerializer}) later on. + */ +public interface ResolvableSerializer +{ + /** + * Method called after {@link SerializerProvider} has registered + * the serializer, but before it has returned it to the caller. + * Called object can then resolve its dependencies to other types, + * including self-references (direct or indirect). + *

+ * Note that this method does NOT return serializer, since resolution + * is not allowed to change actual serializer to use. + * + * @param provider Provider that has constructed serializer this method + * is called on. + */ + public abstract void resolve(SerializerProvider provider) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/SerializerCache.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/SerializerCache.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/SerializerCache.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,209 @@ +package com.fasterxml.jackson.databind.ser; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap; +import com.fasterxml.jackson.databind.util.TypeKey; + +/** + * Simple cache object that allows for doing 2-level lookups: first level is + * by "local" read-only lookup Map (used without locking) + * and second backup level is by a shared modifiable HashMap. + * The idea is that after a while, most serializers are found from the + * local Map (to optimize performance, reduce lock contention), + * but that during buildup we can use a shared map to reduce both + * number of distinct read-only maps constructed, and number of + * serializers constructed. + *

+ * Cache contains three kinds of entries, + * based on combination of class pair key. First class in key is for the + * type to serialize, and second one is type used for determining how + * to resolve value type. One (but not both) of entries can be null. + */ +public final class SerializerCache +{ + /** + * Shared, modifiable map; all access needs to be through synchronized blocks. + *

+ * NOTE: keys are of various types (see below for key types), in addition to + * basic {@link JavaType} used for "untyped" serializers. + */ + private final HashMap> _sharedMap + = new HashMap>(64); + + /** + * Most recent read-only instance, created from _sharedMap, if any. + */ + private final AtomicReference _readOnlyMap + = new AtomicReference(); + + public SerializerCache() { } + + /** + * Method that can be called to get a read-only instance populated from the + * most recent version of the shared lookup Map. + */ + public ReadOnlyClassToSerializerMap getReadOnlyLookupMap() + { + ReadOnlyClassToSerializerMap m = _readOnlyMap.get(); + if (m != null) { + return m; + } + return _makeReadOnlyLookupMap(); + } + + private final synchronized ReadOnlyClassToSerializerMap _makeReadOnlyLookupMap() { + // double-locking; safe, but is it really needed? Not doing that is only a perf problem, + // not correctness + ReadOnlyClassToSerializerMap m = _readOnlyMap.get(); + if (m == null) { + m = ReadOnlyClassToSerializerMap.from(_sharedMap); + _readOnlyMap.set(m); + } + return m; + } + + /* + /********************************************************** + /* Lookup methods for accessing shared (slow) cache + /********************************************************** + */ + + public synchronized int size() { + return _sharedMap.size(); + } + + /** + * Method that checks if the shared (and hence, synchronized) lookup Map might have + * untyped serializer for given type. + */ + public JsonSerializer untypedValueSerializer(Class type) + { + synchronized (this) { + return _sharedMap.get(new TypeKey(type, false)); + } + } + + public JsonSerializer untypedValueSerializer(JavaType type) + { + synchronized (this) { + return _sharedMap.get(new TypeKey(type, false)); + } + } + + public JsonSerializer typedValueSerializer(JavaType type) + { + synchronized (this) { + return _sharedMap.get(new TypeKey(type, true)); + } + } + + public JsonSerializer typedValueSerializer(Class cls) + { + synchronized (this) { + return _sharedMap.get(new TypeKey(cls, true)); + } + } + + /* + /********************************************************** + /* Methods for adding shared serializer instances + /********************************************************** + */ + + /** + * Method called if none of lookups succeeded, and caller had to construct + * a serializer. If so, we will update the shared lookup map so that it + * can be resolved via it next time. + */ + public void addTypedSerializer(JavaType type, JsonSerializer ser) + { + synchronized (this) { + if (_sharedMap.put(new TypeKey(type, true), ser) == null) { + // let's invalidate the read-only copy, too, to get it updated + _readOnlyMap.set(null); + } + } + } + + public void addTypedSerializer(Class cls, JsonSerializer ser) + { + synchronized (this) { + if (_sharedMap.put(new TypeKey(cls, true), ser) == null) { + // let's invalidate the read-only copy, too, to get it updated + _readOnlyMap.set(null); + } + } + } + + public void addAndResolveNonTypedSerializer(Class type, JsonSerializer ser, + SerializerProvider provider) + throws JsonMappingException + { + synchronized (this) { + if (_sharedMap.put(new TypeKey(type, false), ser) == null) { + _readOnlyMap.set(null); + } + // Need resolution to handle cyclic POJO type dependencies + /* 14-May-2011, tatu: Resolving needs to be done in synchronized manner; + * this because while we do need to register instance first, we also must + * keep lock until resolution is complete. + */ + if (ser instanceof ResolvableSerializer) { + ((ResolvableSerializer) ser).resolve(provider); + } + } + } + + public void addAndResolveNonTypedSerializer(JavaType type, JsonSerializer ser, + SerializerProvider provider) + throws JsonMappingException + { + synchronized (this) { + if (_sharedMap.put(new TypeKey(type, false), ser) == null) { + _readOnlyMap.set(null); + } + // Need resolution to handle cyclic POJO type dependencies + /* 14-May-2011, tatu: Resolving needs to be done in synchronized manner; + * this because while we do need to register instance first, we also must + * keep lock until resolution is complete. + */ + if (ser instanceof ResolvableSerializer) { + ((ResolvableSerializer) ser).resolve(provider); + } + } + } + + /** + * Another alternative that will cover both access via raw type and matching + * fully resolved type, in one fell swoop. + * + * @since 2.7 + */ + public void addAndResolveNonTypedSerializer(Class rawType, JavaType fullType, + JsonSerializer ser, + SerializerProvider provider) + throws JsonMappingException + { + synchronized (this) { + Object ob1 = _sharedMap.put(new TypeKey(rawType, false), ser); + Object ob2 = _sharedMap.put(new TypeKey(fullType, false), ser); + if ((ob1 == null) || (ob2 == null)) { + _readOnlyMap.set(null); + } + if (ser instanceof ResolvableSerializer) { + ((ResolvableSerializer) ser).resolve(provider); + } + } + } + + /** + * Method called by StdSerializerProvider#flushCachedSerializers() to + * clear all cached serializers + */ + public synchronized void flush() { + _sharedMap.clear(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/SerializerFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/SerializerFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/SerializerFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,85 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Abstract class that defines API used by {@link SerializerProvider} + * to obtain actual + * {@link JsonSerializer} instances from multiple distinct factories. + */ +public abstract class SerializerFactory +{ + /* + /********************************************************** + /* Additional configuration methods + /********************************************************** + */ + + /** + * Convenience method for creating a new factory instance with additional serializer + * provider; equivalent to calling + *
+     *   withConfig(getConfig().withAdditionalSerializers(additional));
+     *
+ */ + public abstract SerializerFactory withAdditionalSerializers(Serializers additional); + + public abstract SerializerFactory withAdditionalKeySerializers(Serializers additional); + + /** + * Convenience method for creating a new factory instance with additional bean + * serializer modifier; equivalent to calling + *
+     *   withConfig(getConfig().withSerializerModifier(modifier));
+     *
+ */ + public abstract SerializerFactory withSerializerModifier(BeanSerializerModifier modifier); + + /* + /********************************************************** + /* Basic SerializerFactory API: + /********************************************************** + */ + + /** + * Method called to create (or, for immutable serializers, reuse) a serializer for given type. + * + * @param prov Provider that needs to be used to resolve annotation-provided + * serializers (but NOT for others) + * + * @since 2.1 (earlier versions had method with different signature) + */ + public abstract JsonSerializer createSerializer(SerializerProvider prov, + JavaType baseType) + throws JsonMappingException; + + /** + * Method called to create a type information serializer for given base type, + * if one is needed. If not needed (no polymorphic handling configured), should + * return null. + * + * @param baseType Declared type to use as the base type for type information serializer + * + * @return Type serializer to use for the base type, if one is needed; null if not. + */ + public abstract TypeSerializer createTypeSerializer(SerializationConfig config, + JavaType baseType) + throws JsonMappingException; + + /** + * Method called to create serializer to use for serializing JSON property names (which must + * be output as JsonToken.FIELD_NAME) for Map that has specified declared + * key type, and is for specified property (or, if property is null, as root value) + * + * @param type Declared type for Map keys + * @param defaultImpl Default key serializer implementation to use, if no custom ones + * are found (may be null) + * + * @return Serializer to use, if factory knows it; null if not (in which case default + * serializer is to be used) + */ + public abstract JsonSerializer createKeySerializer(SerializationConfig config, + JavaType type, JsonSerializer defaultImpl) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/Serializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/Serializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/Serializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,161 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.type.*; + +/** + * Interface that defines API for simple extensions that can provide additional serializers + * for various types. Access is by a single callback method; instance is to either return + * a configured {@link JsonSerializer} for specified type, or null to indicate that it + * does not support handling of the type. In latter case, further calls can be made + * for other providers; in former case returned serializer is used for handling of + * instances of specified type. + */ +public interface Serializers +{ + /** + * Method called by serialization framework first time a serializer is needed for + * specified type, which is not of a container or reference type (for which + * other methods are called). + * + * @param type Fully resolved type of instances to serialize + * @param config Serialization configuration in use + * @param beanDesc Additional information about type + * + * @return Configured serializer to use for the type; or null if implementation + * does not recognize or support type + */ + public JsonSerializer findSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc); + + /** + * Method called by serialization framework first time a serializer is needed for + * given {@link ReferenceType} + * + * @since 2.7 + */ + public JsonSerializer findReferenceSerializer(SerializationConfig config, + ReferenceType type, BeanDescription beanDesc, + TypeSerializer contentTypeSerializer, JsonSerializer contentValueSerializer); + + /** + * Method called by serialization framework first time a serializer is needed for + * specified array type. + * Implementation should return a serializer instance if it supports + * specified type; or null if it does not. + */ + public JsonSerializer findArraySerializer(SerializationConfig config, + ArrayType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + + /** + * Method called by serialization framework first time a serializer is needed for + * specified {@link java.util.Collection} type. + * Implementation should return a serializer instance if it supports + * specified type; or null if it does not. + */ + public JsonSerializer findCollectionSerializer(SerializationConfig config, + CollectionType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + + /** + * Method called by serialization framework first time a serializer is needed for + * specified "Collection-like" type (type that acts like {@link java.util.Collection}, + * but does not implement it). + * Implementation should return a serializer instance if it supports + * specified type; or null if it does not. + */ + public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, + CollectionLikeType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + + /** + * Method called by serialization framework first time a serializer is needed for + * specified {@link java.util.Map} type. + * Implementation should return a serializer instance if it supports + * specified type; or null if it does not. + */ + public JsonSerializer findMapSerializer(SerializationConfig config, + MapType type, BeanDescription beanDesc, + JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + + /** + * Method called by serialization framework first time a serializer is needed for + * specified "Map-like" type (type that acts like {@link java.util.Map}, + * but does not implement it). + * Implementation should return a serializer instance if it supports + * specified type; or null if it does not. + */ + public JsonSerializer findMapLikeSerializer(SerializationConfig config, + MapLikeType type, BeanDescription beanDesc, + JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer); + + /** + * Basic {@link Serializers} implementation that implements all methods but provides + * no serializers. Its main purpose is to serve as a base class so that + * sub-classes only need to override methods they need. + */ + public static class Base implements Serializers + { + @Override + public JsonSerializer findSerializer(SerializationConfig config, + JavaType type, BeanDescription beanDesc) + { + return null; + } + + @Override + public JsonSerializer findReferenceSerializer(SerializationConfig config, + ReferenceType type, BeanDescription beanDesc, + TypeSerializer contentTypeSerializer, JsonSerializer contentValueSerializer) { + // 21-Oct-2015, tatu: For backwards compatibility, let's delegate to "bean" variant, + // for 2.7 -- remove work-around from 2.8 or later + return findSerializer(config, type, beanDesc); + } + + @Override + public JsonSerializer findArraySerializer(SerializationConfig config, + ArrayType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } + + @Override + public JsonSerializer findCollectionSerializer(SerializationConfig config, + CollectionType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } + + @Override + public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, + CollectionLikeType type, BeanDescription beanDesc, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } + + @Override + public JsonSerializer findMapSerializer(SerializationConfig config, + MapType type, BeanDescription beanDesc, + JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } + + @Override + public JsonSerializer findMapLikeSerializer(SerializationConfig config, + MapLikeType type, BeanDescription beanDesc, + JsonSerializer keySerializer, + TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) + { + return null; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/VirtualBeanPropertyWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,234 @@ +package com.fasterxml.jackson.databind.ser; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * {@link BeanPropertyWriter} implementation used with + * {@link com.fasterxml.jackson.databind.annotation.JsonAppend} + * to add "virtual" properties in addition to regular ones. + * + * @since 2.5 + * + * @see com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter + */ +public abstract class VirtualBeanPropertyWriter + extends BeanPropertyWriter + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Constructor used by most sub-types. + */ + protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef, + Annotations contextAnnotations, JavaType declaredType) + { + this(propDef, contextAnnotations, declaredType, null, null, null, + propDef.findInclusion()); + } + + /** + * Constructor that may be used by sub-classes for constructing a "blue-print" instance; + * one that will only become (or create) actual usable instance when its + * {@link #withConfig} method is called. + */ + protected VirtualBeanPropertyWriter() { + super(); + } + + /** + * Pass-through constructor that may be used by sub-classes that + * want full control over implementation. + */ + protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef, + Annotations contextAnnotations, JavaType declaredType, + JsonSerializer ser, TypeSerializer typeSer, JavaType serType, + JsonInclude.Value inclusion) + { + super(propDef, propDef.getPrimaryMember(), contextAnnotations, declaredType, + ser, typeSer, serType, + _suppressNulls(inclusion), _suppressableValue(inclusion)); + } + + protected VirtualBeanPropertyWriter(VirtualBeanPropertyWriter base) { + super(base); + } + + protected VirtualBeanPropertyWriter(VirtualBeanPropertyWriter base, PropertyName name) { + super(base, name); + } + + protected static boolean _suppressNulls(JsonInclude.Value inclusion) { + if (inclusion == null) { + return false; + } + JsonInclude.Include incl = inclusion.getValueInclusion(); + return (incl != JsonInclude.Include.ALWAYS) && (incl != JsonInclude.Include.USE_DEFAULTS); + } + + protected static Object _suppressableValue(JsonInclude.Value inclusion) { + if (inclusion == null) { + return false; + } + JsonInclude.Include incl = inclusion.getValueInclusion(); + if ((incl == JsonInclude.Include.ALWAYS) + || (incl == JsonInclude.Include.NON_NULL) + || (incl == JsonInclude.Include.USE_DEFAULTS)) { + return null; + } + return MARKER_FOR_EMPTY; + } + + /* + /********************************************************** + /* Standard accessor overrides + /********************************************************** + */ + + @Override + public boolean isVirtual() { return true; } + + /* + /********************************************************** + /* Abstract methods for sub-classes to define + /********************************************************** + */ + + /** + * Method called to figure out the value to serialize. For simple sub-types + * (such as {@link com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter}) + * this may be one of few methods to define, although more advanced implementations + * may choose to not even use this method (by overriding {@link #serializeAsField}) + * and define a bogus implementation. + */ + protected abstract Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception; + + /** + * Contextualization method called on a newly constructed virtual bean property. + * Usually a new intance needs to be created due to finality of some of configuration + * members; otherwise while recommended, creating a new instance is not strictly-speaking + * mandatory because calls are made in thread-safe manner, as part of initialization + * before use. + * + * @param config Currenct configuration; guaranteed to be {@link SerializationConfig} + * (just not typed since caller does not have dependency to serialization-specific types) + * @param declaringClass Class that contains this property writer + * @param propDef Nominal property definition to use + * @param type Declared type for the property + */ + public abstract VirtualBeanPropertyWriter withConfig(MapperConfig config, + AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type); + + /* + /********************************************************** + /* PropertyWriter serialization method overrides + /********************************************************** + */ + + @Override + public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception + { + // NOTE: mostly copied from base class, but off-lined get() access + final Object value = value(bean, gen, prov); + + if (value == null) { + if (_nullSerializer != null) { + gen.writeFieldName(_name); + _nullSerializer.serialize(null, gen, prov); + } + return; + } + JsonSerializer ser = _serializer; + if (ser == null) { + Class cls = value.getClass(); + PropertySerializerMap m = _dynamicSerializers; + ser = m.serializerFor(cls); + if (ser == null) { + ser = _findAndAddDynamic(m, cls, prov); + } + } + if (_suppressableValue != null) { + if (MARKER_FOR_EMPTY == _suppressableValue) { + if (ser.isEmpty(prov, value)) { + return; + } + } else if (_suppressableValue.equals(value)) { + return; + } + } + if (value == bean) { // simple check for direct cycles + // three choices: exception; handled by call; or pass-through + if (_handleSelfReference(bean, gen, prov, ser)) { + return; + } + } + gen.writeFieldName(_name); + if (_typeSerializer == null) { + ser.serialize(value, gen, prov); + } else { + ser.serializeWithType(value, gen, prov, _typeSerializer); + } + } + + // This one's fine as-is from base class + //public void serializeAsOmittedField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception + + @Override + public void serializeAsElement(Object bean, JsonGenerator gen, SerializerProvider prov) + throws Exception + { + // NOTE: mostly copied from base class, but off-lined get() access + final Object value = value(bean, gen, prov); + + if (value == null) { + if (_nullSerializer != null) { + _nullSerializer.serialize(null, gen, prov); + } else { + gen.writeNull(); + } + return; + } + JsonSerializer ser = _serializer; + if (ser == null) { + Class cls = value.getClass(); + PropertySerializerMap map = _dynamicSerializers; + ser = map.serializerFor(cls); + if (ser == null) { + ser = _findAndAddDynamic(map, cls, prov); + } + } + if (_suppressableValue != null) { + if (MARKER_FOR_EMPTY == _suppressableValue) { + if (ser.isEmpty(prov, value)) { + serializeAsPlaceholder(bean, gen, prov); + return; + } + } else if (_suppressableValue.equals(value)) { + serializeAsPlaceholder(bean, gen, prov); + return; + } + } + if (value == bean) { + if (_handleSelfReference(bean, gen, prov, ser)) { + return; + } + } + if (_typeSerializer == null) { + ser.serialize(value, gen, prov); + } else { + ser.serializeWithType(value, gen, prov, _typeSerializer); + } + } + + // This one's fine as-is from base class + //public void serializeAsPlaceholder(Object bean, JsonGenerator jgen, SerializerProvider prov) +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/AttributePropertyWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,82 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; +import com.fasterxml.jackson.databind.util.Annotations; + +/** + * {@link VirtualBeanPropertyWriter} implementation used for + * {@link com.fasterxml.jackson.databind.annotation.JsonAppend}, + * to serialize properties backed-by dynamically assignable attribute + * values. + * + * @since 2.5 + */ +public class AttributePropertyWriter + extends VirtualBeanPropertyWriter +{ + private static final long serialVersionUID = 1; + + protected final String _attrName; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected AttributePropertyWriter(String attrName, BeanPropertyDefinition propDef, + Annotations contextAnnotations, JavaType declaredType) { + this(attrName, propDef, contextAnnotations, declaredType, propDef.findInclusion()); + } + + protected AttributePropertyWriter(String attrName, BeanPropertyDefinition propDef, + Annotations contextAnnotations, JavaType declaredType, + JsonInclude.Value inclusion) + { + super(propDef, contextAnnotations, declaredType, + /* value serializer */ null, /* type serializer */ null, /* ser type */ null, + inclusion); + _attrName = attrName; + } + + public static AttributePropertyWriter construct(String attrName, + BeanPropertyDefinition propDef, + Annotations contextAnnotations, + JavaType declaredType) + { + return new AttributePropertyWriter(attrName, propDef, + contextAnnotations, declaredType); + } + + protected AttributePropertyWriter(AttributePropertyWriter base) { + super(base); + _attrName = base._attrName; + } + + /** + * Since this method should typically not be called on this sub-type, + * default implementation simply throws an {@link IllegalStateException}. + */ + @Override + public VirtualBeanPropertyWriter withConfig(MapperConfig config, + AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) { + throw new IllegalStateException("Should not be called on this type"); + } + + /* + /********************************************************** + /* Overrides for actual serialization, value access + /********************************************************** + */ + + @Override + protected Object value(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception { + return prov.getAttribute(_attrName); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,235 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Specialized POJO serializer that differs from + * {@link com.fasterxml.jackson.databind.ser.BeanSerializer} + * in that instead of producing a JSON Object it will output + * a JSON Array, omitting field names, and serializing values in + * specified serialization order. + * This behavior is usually triggered by using annotation + * {@link com.fasterxml.jackson.annotation.JsonFormat} or its + * equivalents. + *

+ * This serializer can be used for "simple" instances; and will NOT + * be used if one of following is true: + *

    + *
  • Unwrapping is used (no way to expand out array in JSON Object) + *
  • + *
  • Type information ("type id") is to be used: while this could work + * for some embedding methods, it would likely cause conflicts. + *
  • + *
  • Object Identity ("object id") is used: while references would work, + * the problem is inclusion of id itself. + *
  • + *
+ * Note that it is theoretically possible that last 2 issues could be addressed + * (by reserving room in array, for example); and if so, support improved. + *

+ * In cases where array-based output is not feasible, this serializer + * can instead delegate to the original Object-based serializer; this + * is why a reference is retained to the original serializer. + * + * @since 2.1 + */ +public class BeanAsArraySerializer + extends BeanSerializerBase +{ + private static final long serialVersionUID = 1L; // since 2.6 + + /** + * Serializer that would produce JSON Object version; used in + * cases where array output can not be used. + */ + protected final BeanSerializerBase _defaultSerializer; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + public BeanAsArraySerializer(BeanSerializerBase src) { + super(src, (ObjectIdWriter) null); + _defaultSerializer = src; + } + + protected BeanAsArraySerializer(BeanSerializerBase src, String[] toIgnore) { + super(src, toIgnore); + _defaultSerializer = src; + } + + protected BeanAsArraySerializer(BeanSerializerBase src, + ObjectIdWriter oiw, Object filterId) { + super(src, oiw, filterId); + _defaultSerializer = src; + } + + /* + /********************************************************** + /* Life-cycle: factory methods, fluent factories + /********************************************************** + */ + + @Override + public JsonSerializer unwrappingSerializer(NameTransformer transformer) { + /* If this gets called, we will just need delegate to the default + * serializer, to "undo" as-array serialization + */ + return _defaultSerializer.unwrappingSerializer(transformer); + } + + @Override + public boolean isUnwrappingSerializer() { + return false; + } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + // can't handle Object Ids, for now, so: + return _defaultSerializer.withObjectIdWriter(objectIdWriter); + } + + @Override + public BeanSerializerBase withFilterId(Object filterId) { + return new BeanAsArraySerializer(this, _objectIdWriter, filterId); + } + + @Override + protected BeanAsArraySerializer withIgnorals(String[] toIgnore) { + return new BeanAsArraySerializer(this, toIgnore); + } + + @Override + protected BeanSerializerBase asArraySerializer() { + // already is one, so: + return this; + } + + /* + /********************************************************** + /* JsonSerializer implementation that differs between impls + /********************************************************** + */ + + // Re-defined from base class, due to differing prefixes + @Override + public void serializeWithType(Object bean, JsonGenerator gen, + SerializerProvider provider, TypeSerializer typeSer) + throws IOException + { + /* 10-Dec-2014, tatu: Not sure if this can be made to work reliably; + * but for sure delegating to default implementation will not work. So: + */ + if (_objectIdWriter != null) { + _serializeWithObjectId(bean, gen, provider, typeSer); + return; + } + String typeStr = (_typeId == null) ? null : _customTypeId(bean); + if (typeStr == null) { + typeSer.writeTypePrefixForArray(bean, gen); + } else { + typeSer.writeCustomTypePrefixForArray(bean, gen, typeStr); + } + serializeAsArray(bean, gen, provider); + if (typeStr == null) { + typeSer.writeTypeSuffixForArray(bean, gen); + } else { + typeSer.writeCustomTypeSuffixForArray(bean, gen, typeStr); + } + } + + /** + * Main serialization method that will delegate actual output to + * configured + * {@link BeanPropertyWriter} instances. + */ + @Override + public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) + && hasSingleElement(provider)) { + serializeAsArray(bean, gen, provider); + return; + } + /* note: it is assumed here that limitations (type id, object id, + * any getter, filtering) have already been checked; so code here + * is trivial. + */ + gen.writeStartArray(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(bean); + serializeAsArray(bean, gen, provider); + gen.writeEndArray(); + } + + /* + /********************************************************** + /* Field serialization methods + /********************************************************** + */ + + private boolean hasSingleElement(SerializerProvider provider) { + final BeanPropertyWriter[] props; + if (_filteredProps != null && provider.getActiveView() != null) { + props = _filteredProps; + } else { + props = _props; + } + return props.length == 1; + } + + protected final void serializeAsArray(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final BeanPropertyWriter[] props; + if (_filteredProps != null && provider.getActiveView() != null) { + props = _filteredProps; + } else { + props = _props; + } + + int i = 0; + try { + for (final int len = props.length; i < len; ++i) { + BeanPropertyWriter prop = props[i]; + if (prop == null) { // can have nulls in filtered list; but if so, MUST write placeholders + gen.writeNull(); + } else { + prop.serializeAsElement(bean, gen, provider); + } + } + // NOTE: any getters can not be supported either + //if (_anyGetterWriter != null) { + // _anyGetterWriter.getAndSerialize(bean, gen, provider); + //} + } catch (Exception e) { + String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + wrapAndThrow(provider, e, bean, name); + } catch (StackOverflowError e) { + JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); + String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + mapE.prependPath(new JsonMappingException.Reference(bean, name)); + throw mapE; + } + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override public String toString() { + return "BeanAsArraySerializer for "+handledType().getName(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +/** + * Special bogus "serializer" that will throw + * {@link JsonGenerationException} if its {@link #serialize} + * gets invoked. Most commonly registered as handler for unknown types, + * as well as for catching unintended usage (like trying to use null + * as Map/Object key). + */ +@SuppressWarnings("serial") +public class FailingSerializer + extends StdSerializer +{ + protected final String _msg; + + public FailingSerializer(String msg) { + super(Object.class); + _msg = msg; + } + + @Override + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException + { + throw new JsonGenerationException(_msg, g); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { + return null; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + ; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,184 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Decorated {@link BeanPropertyWriter} that will filter out properties + * that are not to be included in currently active JsonView. + */ +public abstract class FilteredBeanPropertyWriter +{ + public static BeanPropertyWriter constructViewBased(BeanPropertyWriter base, Class[] viewsToIncludeIn) + { + if (viewsToIncludeIn.length == 1) { + return new SingleView(base, viewsToIncludeIn[0]); + } + return new MultiView(base, viewsToIncludeIn); + } + + /* + /********************************************************** + /* Concrete sub-classes + /********************************************************** + */ + + private final static class SingleView + extends BeanPropertyWriter + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + protected final BeanPropertyWriter _delegate; + + protected final Class _view; + + protected SingleView(BeanPropertyWriter delegate, Class view) + { + super(delegate); + _delegate = delegate; + _view = view; + } + + @Override + public SingleView rename(NameTransformer transformer) { + return new SingleView(_delegate.rename(transformer), _view); + } + + @Override + public void assignSerializer(JsonSerializer ser) { + _delegate.assignSerializer(ser); + } + + @Override + public void assignNullSerializer(JsonSerializer nullSer) { + _delegate.assignNullSerializer(nullSer); + } + + @Override + public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) + throws Exception + { + Class activeView = prov.getActiveView(); + if (activeView == null || _view.isAssignableFrom(activeView)) { + _delegate.serializeAsField(bean, jgen, prov); + } else { + _delegate.serializeAsOmittedField(bean, jgen, prov); + } + } + + @Override + public void serializeAsElement(Object bean, JsonGenerator jgen, SerializerProvider prov) + throws Exception + { + Class activeView = prov.getActiveView(); + if (activeView == null || _view.isAssignableFrom(activeView)) { + _delegate.serializeAsElement(bean, jgen, prov); + } else { + _delegate.serializeAsPlaceholder(bean, jgen, prov); + } + } + + @Override + public void depositSchemaProperty(JsonObjectFormatVisitor v, + SerializerProvider provider) throws JsonMappingException + { + Class activeView = provider.getActiveView(); + if (activeView == null || _view.isAssignableFrom(activeView)) { + super.depositSchemaProperty(v, provider); + } + } + } + + private final static class MultiView + extends BeanPropertyWriter + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + protected final BeanPropertyWriter _delegate; + + protected final Class[] _views; + + protected MultiView(BeanPropertyWriter delegate, Class[] views) { + super(delegate); + _delegate = delegate; + _views = views; + } + + @Override + public MultiView rename(NameTransformer transformer) { + return new MultiView(_delegate.rename(transformer), _views); + } + + @Override + public void assignSerializer(JsonSerializer ser) { + _delegate.assignSerializer(ser); + } + + @Override + public void assignNullSerializer(JsonSerializer nullSer) { + _delegate.assignNullSerializer(nullSer); + } + + @Override + public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) + throws Exception + { + final Class activeView = prov.getActiveView(); + if (activeView != null) { + int i = 0, len = _views.length; + for (; i < len; ++i) { + if (_views[i].isAssignableFrom(activeView)) break; + } + // not included, bail out: + if (i == len) { + _delegate.serializeAsOmittedField(bean, jgen, prov); + return; + } + } + _delegate.serializeAsField(bean, jgen, prov); + } + + @Override + public void serializeAsElement(Object bean, JsonGenerator jgen, SerializerProvider prov) + throws Exception + { + final Class activeView = prov.getActiveView(); + if (activeView != null) { + int i = 0, len = _views.length; + for (; i < len; ++i) { + if (_views[i].isAssignableFrom(activeView)) break; + } + // not included, bail out: + if (i == len) { + _delegate.serializeAsPlaceholder(bean, jgen, prov); + return; + } + } + _delegate.serializeAsElement(bean, jgen, prov); + } + + @Override + public void depositSchemaProperty(JsonObjectFormatVisitor v, + SerializerProvider provider) throws JsonMappingException + { + Class activeView = provider.getActiveView(); + if (activeView != null) { + int i = 0, len = _views.length; + for (; i < len; ++i) { + if (_views[i].isAssignableFrom(activeView)) break; + } + if (i == len) { // not match? Just don't deposit + return; + } + } + super.depositSchemaProperty(v, provider); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,189 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase; + +/** + * This is an optimized serializer for Lists that can be efficiently + * traversed by index (as opposed to others, such as {@link LinkedList} + * that can not}. + */ +@JacksonStdImpl +public final class IndexedListSerializer + extends AsArraySerializerBase> +{ + private static final long serialVersionUID = 1L; + + public IndexedListSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts, + JsonSerializer valueSerializer) + { + super(List.class, elemType, staticTyping, vts, valueSerializer); + } + + public IndexedListSerializer(IndexedListSerializer src, + BeanProperty property, TypeSerializer vts, JsonSerializer valueSerializer, + Boolean unwrapSingle) { + super(src, property, vts, valueSerializer, unwrapSingle); + } + + @Override + public IndexedListSerializer withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) { + return new IndexedListSerializer(this, property, vts, elementSerializer, unwrapSingle); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public boolean isEmpty(SerializerProvider prov, List value) { + return (value == null) || value.isEmpty(); + } + + @Override + public boolean hasSingleElement(List value) { + return (value.size() == 1); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new IndexedListSerializer(this, + _property, vts, _elementSerializer, _unwrapSingle); + } + + @Override + public final void serialize(List value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final int len = value.size(); + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(List value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + if (_elementSerializer != null) { + serializeContentsUsing(value, jgen, provider, _elementSerializer); + return; + } + if (_valueTypeSerializer != null) { + serializeTypedContents(value, jgen, provider); + return; + } + final int len = value.size(); + if (len == 0) { + return; + } + int i = 0; + try { + PropertySerializerMap serializers = _dynamicSerializers; + for (; i < len; ++i) { + Object elem = value.get(i); + if (elem == null) { + provider.defaultSerializeNull(jgen); + } else { + Class cc = elem.getClass(); + JsonSerializer serializer = serializers.serializerFor(cc); + if (serializer == null) { + // To fix [JACKSON-508] + if (_elementType.hasGenericTypes()) { + serializer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_elementType, cc), provider); + } else { + serializer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicSerializers; + } + serializer.serialize(elem, jgen, provider); + } + } + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } + + public void serializeContentsUsing(List value, JsonGenerator jgen, SerializerProvider provider, + JsonSerializer ser) + throws IOException + { + final int len = value.size(); + if (len == 0) { + return; + } + final TypeSerializer typeSer = _valueTypeSerializer; + for (int i = 0; i < len; ++i) { + Object elem = value.get(i); + try { + if (elem == null) { + provider.defaultSerializeNull(jgen); + } else if (typeSer == null) { + ser.serialize(elem, jgen, provider); + } else { + ser.serializeWithType(elem, jgen, provider, typeSer); + } + } catch (Exception e) { + // [JACKSON-55] Need to add reference information + wrapAndThrow(provider, e, value, i); + } + } + } + + public void serializeTypedContents(List value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + final int len = value.size(); + if (len == 0) { + return; + } + int i = 0; + try { + final TypeSerializer typeSer = _valueTypeSerializer; + PropertySerializerMap serializers = _dynamicSerializers; + for (; i < len; ++i) { + Object elem = value.get(i); + if (elem == null) { + provider.defaultSerializeNull(jgen); + } else { + Class cc = elem.getClass(); + JsonSerializer serializer = serializers.serializerFor(cc); + if (serializer == null) { + // To fix [JACKSON-508] + if (_elementType.hasGenericTypes()) { + serializer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_elementType, cc), provider); + } else { + serializer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicSerializers; + } + serializer.serializeWithType(elem, jgen, provider, typeSer); + } + } + } catch (Exception e) { + // [JACKSON-55] Need to add reference information + wrapAndThrow(provider, e, value, i); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,147 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase; + +/** + * Efficient implement for serializing {@link List}s that contains Strings and are random-accessible. + * The only complexity is due to possibility that serializer for {@link String} + * may be overridde; because of this, logic is needed to ensure that the default + * serializer is in use to use fastest mode, or if not, to defer to custom + * String serializer. + */ +@JacksonStdImpl +public final class IndexedStringListSerializer + extends StaticListSerializerBase> +{ + private static final long serialVersionUID = 1L; + + public final static IndexedStringListSerializer instance = new IndexedStringListSerializer(); + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected IndexedStringListSerializer() { + super(List.class); + } + + public IndexedStringListSerializer(IndexedStringListSerializer src, + JsonSerializer ser, Boolean unwrapSingle) { + super(src, ser, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, + JsonSerializer ser, Boolean unwrapSingle) { + return new IndexedStringListSerializer(this, ser, unwrapSingle); + } + + @Override protected JsonNode contentSchema() { return createSchemaNode("string", true); } + + @Override + protected void acceptContentVisitor(JsonArrayFormatVisitor visitor) throws JsonMappingException { + visitor.itemsFormat(JsonFormatTypes.STRING); + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public void serialize(List value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + final int len = value.size(); + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + _serializeUnwrapped(value, gen, provider); + return; + } + } + + gen.writeStartArray(len); + if (_serializer == null) { + serializeContents(value, gen, provider, len); + } else { + serializeUsingCustom(value, gen, provider, len); + } + gen.writeEndArray(); + } + + private final void _serializeUnwrapped(List value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + if (_serializer == null) { + serializeContents(value, gen, provider, 1); + } else { + serializeUsingCustom(value, gen, provider, 1); + } + } + + @Override + public void serializeWithType(List value, JsonGenerator gen, + SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + final int len = value.size(); + typeSer.writeTypePrefixForArray(value, gen); + if (_serializer == null) { + serializeContents(value, gen, provider, len); + } else { + serializeUsingCustom(value, gen, provider, len); + } + typeSer.writeTypeSuffixForArray(value, gen); + } + + private final void serializeContents(List value, JsonGenerator gen, + SerializerProvider provider, int len) throws IOException + { + int i = 0; + try { + for (; i < len; ++i) { + String str = value.get(i); + if (str == null) { + provider.defaultSerializeNull(gen); + } else { + gen.writeString(str); + } + } + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } + + private final void serializeUsingCustom(List value, JsonGenerator gen, + SerializerProvider provider, int len) throws IOException + { + int i = 0; + try { + final JsonSerializer ser = _serializer; + for (i = 0; i < len; ++i) { + String str = value.get(i); + if (str == null) { + provider.defaultSerializeNull(gen); + } else { + ser.serialize(str, gen, provider); + } + } + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,102 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.util.Iterator; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase; + +@SuppressWarnings("serial") +@JacksonStdImpl +public class IteratorSerializer + extends AsArraySerializerBase> +{ + public IteratorSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts) { + super(Iterator.class, elemType, staticTyping, vts, null); + } + + public IteratorSerializer(IteratorSerializer src, + BeanProperty property, TypeSerializer vts, JsonSerializer valueSerializer, + Boolean unwrapSingle) { + super(src, property, vts, valueSerializer, unwrapSingle); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Iterator value) { + return (value == null) || !value.hasNext(); + } + + @Override + public boolean hasSingleElement(Iterator value) { + // no really good way to determine (without consuming iterator), so: + return false; + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new IteratorSerializer(this, _property, vts, _elementSerializer, _unwrapSingle); + } + + @Override + public IteratorSerializer withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) { + return new IteratorSerializer(this, property, vts, elementSerializer, unwrapSingle); + } + + @Override + public final void serialize(Iterator value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + if (hasSingleElement(value)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(Iterator value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + if (value.hasNext()) { + final TypeSerializer typeSer = _valueTypeSerializer; + JsonSerializer prevSerializer = null; + Class prevClass = null; + do { + Object elem = value.next(); + if (elem == null) { + provider.defaultSerializeNull(gen); + continue; + } + JsonSerializer currSerializer = _elementSerializer; + if (currSerializer == null) { + // Minor optimization to avoid most lookups: + Class cc = elem.getClass(); + if (cc == prevClass) { + currSerializer = prevSerializer; + } else { + currSerializer = provider.findValueSerializer(cc, _property); + prevSerializer = currSerializer; + prevClass = cc; + } + } + if (typeSer == null) { + currSerializer.serialize(elem, gen, provider); + } else { + currSerializer.serializeWithType(elem, gen, provider, typeSer); + } + } while (value.hasNext()); + } + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/MapEntrySerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,328 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +/** + * @since 2.5 + */ +@SuppressWarnings("serial") +@JacksonStdImpl +public class MapEntrySerializer + extends ContainerSerializer> + implements ContextualSerializer +{ + /** + * Map-valued property being serialized with this instance + */ + protected final BeanProperty _property; + + /** + * Whether static types should be used for serialization of values + * or not (if not, dynamic runtime type is used) + */ + protected final boolean _valueTypeIsStatic; + + protected final JavaType _entryType, _keyType, _valueType; + + /** + * Key serializer to use, if it can be statically determined + */ + protected JsonSerializer _keySerializer; + + /** + * Value serializer to use, if it can be statically determined + */ + protected JsonSerializer _valueSerializer; + + /** + * Type identifier serializer used for values, if any. + */ + protected final TypeSerializer _valueTypeSerializer; + + /** + * If value type can not be statically determined, mapping from + * runtime value types to serializers are stored in this object. + */ + protected PropertySerializerMap _dynamicValueSerializers; + + /* + /********************************************************** + /* Construction, initialization + /********************************************************** + */ + + public MapEntrySerializer(JavaType type, JavaType keyType, JavaType valueType, + boolean staticTyping, TypeSerializer vts, + BeanProperty property) + { + super(type); + _entryType = type; + _keyType = keyType; + _valueType = valueType; + _valueTypeIsStatic = staticTyping; + _valueTypeSerializer = vts; + _property = property; + _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); + } + + @SuppressWarnings("unchecked") + protected MapEntrySerializer(MapEntrySerializer src, BeanProperty property, + TypeSerializer vts, + JsonSerializer keySer, JsonSerializer valueSer) + { + super(Map.class, false); + _entryType = src._entryType; + _keyType = src._keyType; + _valueType = src._valueType; + _valueTypeIsStatic = src._valueTypeIsStatic; + _valueTypeSerializer = src._valueTypeSerializer; + _keySerializer = (JsonSerializer) keySer; + _valueSerializer = (JsonSerializer) valueSer; + _dynamicValueSerializers = src._dynamicValueSerializers; + _property = src._property; + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new MapEntrySerializer(this, _property, vts, _keySerializer, _valueSerializer); + } + + public MapEntrySerializer withResolved(BeanProperty property, + JsonSerializer keySerializer, JsonSerializer valueSerializer) { + return new MapEntrySerializer(this, property, _valueTypeSerializer, keySerializer, valueSerializer); + } + + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) throws JsonMappingException + { + JsonSerializer ser = null; + JsonSerializer keySer = null; + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember(); + + // First: if we have a property, may have property-annotation overrides + if (propertyAcc != null && intr != null) { + Object serDef = intr.findKeySerializer(propertyAcc); + if (serDef != null) { + keySer = provider.serializerInstance(propertyAcc, serDef); + } + serDef = intr.findContentSerializer(propertyAcc); + if (serDef != null) { + ser = provider.serializerInstance(propertyAcc, serDef); + } + } + if (ser == null) { + ser = _valueSerializer; + } + // [databind#124]: May have a content converter + ser = findConvertingContentSerializer(provider, property, ser); + if (ser == null) { + // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated, + // we can consider it a static case as well. + // 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho + if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) { + ser = provider.findValueSerializer(_valueType, property); + } + } else { + ser = provider.handleSecondaryContextualization(ser, property); + } + if (keySer == null) { + keySer = _keySerializer; + } + if (keySer == null) { + keySer = provider.findKeySerializer(_keyType, property); + } else { + keySer = provider.handleSecondaryContextualization(keySer, property); + } + MapEntrySerializer mser = withResolved(property, keySer, ser); + // but note: no filtering, ignored entries or sorting (unlike Maps) + return mser; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _valueType; + } + + @Override + public JsonSerializer getContentSerializer() { + return _valueSerializer; + } + + @Override + public boolean hasSingleElement(Map.Entry value) { + return true; + } + + @Override + public boolean isEmpty(SerializerProvider prov, Entry value) { + return (value == null); + } + + /* + /********************************************************** + /* Serialization methods + /********************************************************** + */ + + @Override + public void serialize(Map.Entry value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + gen.writeStartObject(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + if (_valueSerializer != null) { + serializeUsing(value, gen, provider, _valueSerializer); + } else { + serializeDynamic(value, gen, provider); + } + gen.writeEndObject(); + } + + @Override + public void serializeWithType(Map.Entry value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + typeSer.writeTypePrefixForObject(value, gen); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + if (_valueSerializer != null) { + serializeUsing(value, gen, provider, _valueSerializer); + } else { + serializeDynamic(value, gen, provider); + } + typeSer.writeTypeSuffixForObject(value, gen); + } + + protected void serializeDynamic(Map.Entry value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + final JsonSerializer keySerializer = _keySerializer; + final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES); + final TypeSerializer vts = _valueTypeSerializer; + + PropertySerializerMap serializers = _dynamicValueSerializers; + + Object valueElem = value.getValue(); + Object keyElem = value.getKey(); + if (keyElem == null) { + provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider); + } else { + // [JACKSON-314] skip entries with null values? + if (skipNulls && valueElem == null) return; + keySerializer.serialize(keyElem, jgen, provider); + } + // And then value + if (valueElem == null) { + provider.defaultSerializeNull(jgen); + } else { + Class cc = valueElem.getClass(); + JsonSerializer ser = serializers.serializerFor(cc); + if (ser == null) { + if (_valueType.hasGenericTypes()) { + ser = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_valueType, cc), provider); + } else { + ser = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicValueSerializers; + } + try { + if (vts == null) { + ser.serialize(valueElem, jgen, provider); + } else { + ser.serializeWithType(valueElem, jgen, provider, vts); + } + } catch (Exception e) { + // [JACKSON-55] Need to add reference information + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + + /** + * Method called to serialize fields, when the value type is statically known, + * so that value serializer is passed and does not need to be fetched from + * provider. + */ + protected void serializeUsing(Map.Entry value, JsonGenerator jgen, SerializerProvider provider, + JsonSerializer ser) + throws IOException, JsonGenerationException + { + final JsonSerializer keySerializer = _keySerializer; + final TypeSerializer vts = _valueTypeSerializer; + final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES); + + Object valueElem = value.getValue(); + Object keyElem = value.getKey(); + if (keyElem == null) { + provider.findNullKeySerializer(_keyType, _property).serialize(null, jgen, provider); + } else { + // [JACKSON-314] also may need to skip entries with null values + if (skipNulls && valueElem == null) return; + keySerializer.serialize(keyElem, jgen, provider); + } + if (valueElem == null) { + provider.defaultSerializeNull(jgen); + } else { + try { + if (vts == null) { + ser.serialize(valueElem, jgen, provider); + } else { + ser.serializeWithType(valueElem, jgen, provider, vts); + } + } catch (Exception e) { + // [JACKSON-55] Need to add reference information + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + JavaType type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } + +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,95 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.databind.*; + +/** + * Object that knows how to serialize Object Ids. + */ +public final class ObjectIdWriter +{ + public final JavaType idType; + + /** + * Name of id property to write, if not null: if null, should + * only write references, but id property is handled by some + * other entity. + */ + public final SerializableString propertyName; + + /** + * Blueprint generator instance: actual instance will be + * fetched from {@link SerializerProvider} using this as + * the key. + */ + public final ObjectIdGenerator generator; + + /** + * Serializer used for serializing id values. + */ + public final JsonSerializer serializer; + + /** + * Marker that indicates what the first reference is to be + * serialized as full POJO, or as Object Id (other references + * will always be serialized as Object Id) + * + * @since 2.1 + */ + public final boolean alwaysAsId; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + @SuppressWarnings("unchecked") + protected ObjectIdWriter(JavaType t, SerializableString propName, + ObjectIdGenerator gen, JsonSerializer ser, boolean alwaysAsId) + { + idType = t; + propertyName = propName; + generator = gen; + serializer = (JsonSerializer) ser; + this.alwaysAsId = alwaysAsId; + } + + /** + * Factory method called by {@link com.fasterxml.jackson.databind.ser.std.BeanSerializerBase} + * with the initial information based on standard settings for the type + * for which serializer is being built. + * + * @since 2.3 + */ + public static ObjectIdWriter construct(JavaType idType, PropertyName propName, + ObjectIdGenerator generator, boolean alwaysAsId) + { + String simpleName = (propName == null) ? null : propName.getSimpleName(); + return construct(idType, simpleName, generator, alwaysAsId); + } + + @Deprecated // since 2.3 + public static ObjectIdWriter construct(JavaType idType, String propName, + ObjectIdGenerator generator, boolean alwaysAsId) + { + SerializableString serName = (propName == null) ? null : new SerializedString(propName); + return new ObjectIdWriter(idType, serName, generator, null, alwaysAsId); + } + + public ObjectIdWriter withSerializer(JsonSerializer ser) { + return new ObjectIdWriter(idType, propertyName, generator, ser, alwaysAsId); + } + + /** + * @since 2.1 + */ + public ObjectIdWriter withAlwaysAsId(boolean newState) { + if (newState == alwaysAsId) { + return this; + } + return new ObjectIdWriter(idType, propertyName, generator, serializer, newState); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,80 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; +import com.fasterxml.jackson.databind.ser.*; + +public class PropertyBasedObjectIdGenerator + extends ObjectIdGenerators.PropertyGenerator +{ + private static final long serialVersionUID = 1L; + + protected final BeanPropertyWriter _property; + + public PropertyBasedObjectIdGenerator(ObjectIdInfo oid, BeanPropertyWriter prop) + { + this(oid.getScope(), prop); + } + + protected PropertyBasedObjectIdGenerator(Class scope, BeanPropertyWriter prop) + { + super(scope); + _property = prop; + } + + /** + * We must override this method, to prevent errors when scopes are the same, + * but underlying class (on which to access property) is different. + */ + @Override + public boolean canUseFor(ObjectIdGenerator gen) { + if (gen.getClass() == getClass()) { + PropertyBasedObjectIdGenerator other = (PropertyBasedObjectIdGenerator) gen; + if (other.getScope() == _scope) { + /* 26-Jul-2012, tatu: This is actually not enough, because the property + * accessor within BeanPropertyWriter won't work for other property fields + * (see [https://github.com/FasterXML/jackson-module-jaxb-annotations/issues/9] + * for details). + * So we need to verify that underlying property is actually the same. + */ + return (other._property == _property); + } + } + return false; + } + + @Override + public Object generateId(Object forPojo) { + try { + return _property.get(forPojo); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IllegalStateException("Problem accessing property '" + +_property.getName()+"': "+e.getMessage(), e); + } + } + + @Override + public ObjectIdGenerator forScope(Class scope) { + return (scope == _scope) ? this : new PropertyBasedObjectIdGenerator(scope, _property); + } + + @Override + public ObjectIdGenerator newForSerialization(Object context) { + // No state, can return this + return this; + } + + @Override + public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) { + if (key == null) { + return null; + } + // should we use general type for all; or type of property itself? + return new IdKey(getClass(), _scope, key); + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,378 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.util.Arrays; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * Helper container used for resolving serializers for dynamic (possibly but not + * necessarily polymorphic) properties: properties whose type is not forced + * to use dynamic (declared) type and that are not final. + * If so, serializer to use can only be established once actual value type is known. + * Since this happens a lot unless static typing is forced (or types are final) + * this implementation is optimized for efficiency. + * Instances are immutable; new instances are created with factory methods: this + * is important to ensure correct multi-threaded access. + */ +public abstract class PropertySerializerMap +{ + /** + * Configuration setting that determines what happens when maximum + * size (currently 8) is reached: if true, will "start from beginning"; + * if false, will simply stop adding new entries. + * + * @since 2.5 + */ + protected final boolean _resetWhenFull; + + /** + * @since 2.5 + */ + protected PropertySerializerMap(boolean resetWhenFull) { + _resetWhenFull = resetWhenFull; + } + + protected PropertySerializerMap(PropertySerializerMap base) { + _resetWhenFull = base._resetWhenFull; + } + + /** + * Main lookup method. Takes a "raw" type since usage is always from + * place where parameterization is fixed such that there can not be + * type-parametric variations. + */ + public abstract JsonSerializer serializerFor(Class type); + + /** + * Method called if initial lookup fails, when looking for a primary + * serializer (one that is directly attached to a property). + * Will both find serializer + * and construct new map instance if warranted, and return both. + * + * @since 2.3 + * + * @throws JsonMappingException + */ + public final SerializerAndMapResult findAndAddPrimarySerializer(Class type, + SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer serializer = provider.findPrimaryPropertySerializer(type, property); + return new SerializerAndMapResult(serializer, newWith(type, serializer)); + } + + public final SerializerAndMapResult findAndAddPrimarySerializer(JavaType type, + SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer serializer = provider.findPrimaryPropertySerializer(type, property); + return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); + } + + /** + * Method called if initial lookup fails, when looking for a non-primary + * serializer (one that is not directly attached to a property). + * Will both find serializer + * and construct new map instance if warranted, and return both. + * + * @since 2.3 + * + * @throws JsonMappingException + */ + public final SerializerAndMapResult findAndAddSecondarySerializer(Class type, + SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer serializer = provider.findValueSerializer(type, property); + return new SerializerAndMapResult(serializer, newWith(type, serializer)); + } + + public final SerializerAndMapResult findAndAddSecondarySerializer(JavaType type, + SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer serializer = provider.findValueSerializer(type, property); + return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); + } + + /** + * Method called if initial lookup fails, when looking for a root value + * serializer: one that is not directly attached to a property, but needs to + * have {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer} wrapped + * around it. Will both find the serializer + * and construct new map instance if warranted, and return both. + * + * @since 2.5 + * + * @throws JsonMappingException + */ + public final SerializerAndMapResult findAndAddRootValueSerializer(Class type, + SerializerProvider provider) + throws JsonMappingException + { + JsonSerializer serializer = provider.findTypedValueSerializer(type, false, null); + return new SerializerAndMapResult(serializer, newWith(type, serializer)); + } + + /** + * @since 2.5 + */ + public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type, + SerializerProvider provider) + throws JsonMappingException + { + JsonSerializer serializer = provider.findTypedValueSerializer(type, false, null); + return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); + } + + /** + * Method called if initial lookup fails, when looking for a key + * serializer (possible attached indirectly to a property) + * Will both find serializer + * and construct new map instance if warranted, and return both. + * + * @since 2.7 + */ + public final SerializerAndMapResult findAndAddKeySerializer(Class type, + SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer serializer = provider.findKeySerializer(type, property); + return new SerializerAndMapResult(serializer, newWith(type, serializer)); + } + + /** + * Method that can be used to 'register' a serializer that caller has resolved + * without help of this map. + * + * @since 2.5 + */ + public final SerializerAndMapResult addSerializer(Class type, JsonSerializer serializer) { + return new SerializerAndMapResult(serializer, newWith(type, serializer)); + } + + /** + * @since 2.5 + */ + public final SerializerAndMapResult addSerializer(JavaType type, JsonSerializer serializer) { + return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); + } + + public abstract PropertySerializerMap newWith(Class type, JsonSerializer serializer); + + /** + * @deprecated Since 2.5 Use {@link #emptyForProperties} instead + */ + @Deprecated + public static PropertySerializerMap emptyMap() { + return emptyForProperties(); + } + + /** + * @since 2.5 + */ + public static PropertySerializerMap emptyForProperties() { + return Empty.FOR_PROPERTIES; + } + + /** + * @since 2.5 + */ + public static PropertySerializerMap emptyForRootValues() { + return Empty.FOR_ROOT_VALUES; + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Value class used for returning tuple that has both serializer + * that was retrieved and new map instance + */ + public final static class SerializerAndMapResult + { + public final JsonSerializer serializer; + public final PropertySerializerMap map; + + public SerializerAndMapResult(JsonSerializer serializer, + PropertySerializerMap map) + { + this.serializer = serializer; + this.map = map; + } + } + + /** + * Trivial container for bundling type + serializer entries. + */ + private final static class TypeAndSerializer + { + public final Class type; + public final JsonSerializer serializer; + + public TypeAndSerializer(Class type, JsonSerializer serializer) { + this.type = type; + this.serializer = serializer; + } + } + + /* + /********************************************************** + /* Implementations + /********************************************************** + */ + + /** + * Bogus instance that contains no serializers; used as the default + * map with new serializers. + */ + private final static class Empty extends PropertySerializerMap + { + // No root serializers; do not reset when full + public final static Empty FOR_PROPERTIES = new Empty(false); + + // Yes, root serializers; do reset when full + public final static Empty FOR_ROOT_VALUES = new Empty(true); + + protected Empty(boolean resetWhenFull) { + super(resetWhenFull); + } + + @Override + public JsonSerializer serializerFor(Class type) { + return null; // empty, nothing to find + } + + @Override + public PropertySerializerMap newWith(Class type, JsonSerializer serializer) { + return new Single(this, type, serializer); + } + } + + /** + * Map that contains a single serializer; although seemingly silly + * this is probably the most commonly used variant because many + * theoretically dynamic or polymorphic types just have single + * actual type. + */ + private final static class Single extends PropertySerializerMap + { + private final Class _type; + private final JsonSerializer _serializer; + + public Single(PropertySerializerMap base, Class type, JsonSerializer serializer) { + super(base); + _type = type; + _serializer = serializer; + } + + @Override + public JsonSerializer serializerFor(Class type) + { + if (type == _type) { + return _serializer; + } + return null; + } + + @Override + public PropertySerializerMap newWith(Class type, JsonSerializer serializer) { + return new Double(this, _type, _serializer, type, serializer); + } + } + + private final static class Double extends PropertySerializerMap + { + private final Class _type1, _type2; + private final JsonSerializer _serializer1, _serializer2; + + public Double(PropertySerializerMap base, + Class type1, JsonSerializer serializer1, + Class type2, JsonSerializer serializer2) + { + super(base); + _type1 = type1; + _serializer1 = serializer1; + _type2 = type2; + _serializer2 = serializer2; + } + + @Override + public JsonSerializer serializerFor(Class type) + { + if (type == _type1) { + return _serializer1; + } + if (type == _type2) { + return _serializer2; + } + return null; + } + + @Override + public PropertySerializerMap newWith(Class type, JsonSerializer serializer) { + // Ok: let's just create generic one + TypeAndSerializer[] ts = new TypeAndSerializer[3]; + ts[0] = new TypeAndSerializer(_type1, _serializer1); + ts[1] = new TypeAndSerializer(_type2, _serializer2); + ts[2] = new TypeAndSerializer(type, serializer); + return new Multi(this, ts); + } + } + + private final static class Multi extends PropertySerializerMap + { + /** + * Let's limit number of serializers we actually cache; linear + * lookup won't scale too well beyond smallish number, and if + * we really want to support larger collections should use + * a hash map. But it seems unlikely this is a common use + * case so for now let's just stop building after hard-coded + * limit. 8 sounds like a reasonable stab for now. + */ + private final static int MAX_ENTRIES = 8; + + private final TypeAndSerializer[] _entries; + + public Multi(PropertySerializerMap base, TypeAndSerializer[] entries) { + super(base); + _entries = entries; + } + + @Override + public JsonSerializer serializerFor(Class type) + { + for (int i = 0, len = _entries.length; i < len; ++i) { + TypeAndSerializer entry = _entries[i]; + if (entry.type == type) { + return entry.serializer; + } + } + return null; + } + + @Override + public PropertySerializerMap newWith(Class type, JsonSerializer serializer) + { + int len = _entries.length; + // Will only grow up to N entries. We could consider couple of alternatives after + // this if we wanted to... but for now, two main choices make most sense + if (len == MAX_ENTRIES) { + if (_resetWhenFull) { + return new Single(this, type, serializer); + } + return this; + } + TypeAndSerializer[] entries = Arrays.copyOf(_entries, len+1); + entries[len] = new TypeAndSerializer(type, serializer); + return new Multi(this, entries); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,175 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.util.TypeKey; + +/** + * Optimized lookup table for accessing two types of serializers; typed + * and non-typed. Only accessed from a single thread, so no synchronization + * needed for accessors. + *

+ * Note that before 2.6 this class was much smaller, and referred most + * operations to separate JsonSerializerMap, but in 2.6 + * functions were combined. + */ +public final class ReadOnlyClassToSerializerMap +{ + private final Bucket[] _buckets; + + private final int _size; + + private final int _mask; + + public ReadOnlyClassToSerializerMap(Map> serializers) + { + int size = findSize(serializers.size()); + _size = size; + _mask = (size-1); + Bucket[] buckets = new Bucket[size]; + for (Map.Entry> entry : serializers.entrySet()) { + TypeKey key = entry.getKey(); + int index = key.hashCode() & _mask; + buckets[index] = new Bucket(buckets[index], key, entry.getValue()); + } + _buckets = buckets; + } + + private final static int findSize(int size) + { + // For small enough results (64 or less), we'll require <= 50% fill rate; otherwise 80% + int needed = (size <= 64) ? (size + size) : (size + (size >> 2)); + int result = 8; + while (result < needed) { + result += result; + } + return result; + } + + /** + * Factory method for constructing an instance. + */ + public static ReadOnlyClassToSerializerMap from(HashMap> src) { + return new ReadOnlyClassToSerializerMap(src); + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + public int size() { return _size; } + + public JsonSerializer typedValueSerializer(JavaType type) + { + Bucket bucket = _buckets[TypeKey.typedHash(type) & _mask]; + if (bucket == null) { + return null; + } + if (bucket.matchesTyped(type)) { + return bucket.value; + } + while ((bucket = bucket.next) != null) { + if (bucket.matchesTyped(type)) { + return bucket.value; + } + } + return null; + } + + public JsonSerializer typedValueSerializer(Class type) + { + Bucket bucket = _buckets[TypeKey.typedHash(type) & _mask]; + if (bucket == null) { + return null; + } + if (bucket.matchesTyped(type)) { + return bucket.value; + } + while ((bucket = bucket.next) != null) { + if (bucket.matchesTyped(type)) { + return bucket.value; + } + } + return null; + } + + public JsonSerializer untypedValueSerializer(JavaType type) + { + Bucket bucket = _buckets[TypeKey.untypedHash(type) & _mask]; + if (bucket == null) { + return null; + } + if (bucket.matchesUntyped(type)) { + return bucket.value; + } + while ((bucket = bucket.next) != null) { + if (bucket.matchesUntyped(type)) { + return bucket.value; + } + } + return null; + } + + public JsonSerializer untypedValueSerializer(Class type) + { + Bucket bucket = _buckets[TypeKey.untypedHash(type) & _mask]; + if (bucket == null) { + return null; + } + if (bucket.matchesUntyped(type)) { + return bucket.value; + } + while ((bucket = bucket.next) != null) { + if (bucket.matchesUntyped(type)) { + return bucket.value; + } + } + return null; + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + private final static class Bucket + { + public final JsonSerializer value; + public final Bucket next; + + protected final Class _class; + protected final JavaType _type; + + protected final boolean _isTyped; + + public Bucket(Bucket next, TypeKey key, JsonSerializer value) + { + this.next = next; + this.value = value; + _isTyped = key.isTyped(); + _class = key.getRawType(); + _type = key.getType(); + } + + public boolean matchesTyped(Class key) { + return (_class == key) && _isTyped; + } + + public boolean matchesUntyped(Class key) { + return (_class == key) && !_isTyped; + } + + public boolean matchesTyped(JavaType key) { + return _isTyped && key.equals(_type); + } + + public boolean matchesUntyped(JavaType key) { + return !_isTyped && key.equals(_type); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,316 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.util.*; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.ser.*; + +/** + * Simple {@link PropertyFilter} implementation that only uses property name + * to determine whether to serialize property as is, or to filter it out. + *

+ * Use of this class as the base implementation for any custom + * {@link PropertyFilter} implementations is strongly encouraged, + * because it can provide default implementation for any methods that may + * be added in {@link PropertyFilter} (as unfortunate as additions may be). + */ +@SuppressWarnings("deprecation") +public class SimpleBeanPropertyFilter + implements BeanPropertyFilter, PropertyFilter + // sub-classes must also implement java.io.Serializable +{ + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected SimpleBeanPropertyFilter() { } + + /** + * Convenience factory method that will return a "no-op" filter that will + * simply just serialize all properties that are given, and filter out + * nothing. + * + * @since 2.6 + */ + public static SimpleBeanPropertyFilter serializeAll() { + return SerializeExceptFilter.INCLUDE_ALL; + } + + /** + * Factory method that was accidentally added in 2.5 with arguments; basically + * works just as an alias of {@link #filterOutAllExcept(Set)} which is not + * very useful. Instead, see {@link #serializeAll()} for intended signature. + * + * @deprecated Since 2.6; to be removed from 2.7 + */ + @Deprecated + public static SimpleBeanPropertyFilter serializeAll(Set properties) { + return new FilterExceptFilter(properties); + } + + /** + * Factory method to construct filter that filters out all properties except + * ones includes in set + */ + public static SimpleBeanPropertyFilter filterOutAllExcept(Set properties) { + return new FilterExceptFilter(properties); + } + + public static SimpleBeanPropertyFilter filterOutAllExcept(String... propertyArray) { + HashSet properties = new HashSet(propertyArray.length); + Collections.addAll(properties, propertyArray); + return new FilterExceptFilter(properties); + } + + public static SimpleBeanPropertyFilter serializeAllExcept(Set properties) { + return new SerializeExceptFilter(properties); + } + + public static SimpleBeanPropertyFilter serializeAllExcept(String... propertyArray) { + HashSet properties = new HashSet(propertyArray.length); + Collections.addAll(properties, propertyArray); + return new SerializeExceptFilter(properties); + } + + /** + * Helper method to ease transition from {@link BeanPropertyWriter} into + * {@link PropertyWriter} + * + * @since 2.3 + */ + public static PropertyFilter from(final BeanPropertyFilter src) + { + return new PropertyFilter() { + @Override + public void serializeAsField(Object pojo, JsonGenerator jgen, + SerializerProvider prov, PropertyWriter writer) + throws Exception { + src.serializeAsField(pojo, jgen, prov, (BeanPropertyWriter) writer); + } + + @Override + public void depositSchemaProperty(PropertyWriter writer, + ObjectNode propertiesNode, SerializerProvider provider) + throws JsonMappingException { + src.depositSchemaProperty((BeanPropertyWriter) writer, propertiesNode, provider); + } + + @Override + public void depositSchemaProperty(PropertyWriter writer, + JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) throws JsonMappingException { + src.depositSchemaProperty((BeanPropertyWriter) writer, objectVisitor, provider); + } + + @Override + public void serializeAsElement(Object elementValue, + JsonGenerator jgen, SerializerProvider prov, + PropertyWriter writer) throws Exception { + // not needed; element filtering only available through new interfaces + throw new UnsupportedOperationException(); + } + + }; + } + + /* + /********************************************************** + /* Methods for sub-classes + /********************************************************** + */ + + /** + * Method called to determine whether property will be included + * (if 'true' returned) or filtered out (if 'false' returned) + */ + protected boolean include(BeanPropertyWriter writer) { + return true; + } + + /** + * Method called to determine whether property will be included + * (if 'true' returned) or filtered out (if 'false' returned) + * + * @since 2.3 + */ + protected boolean include(PropertyWriter writer) { + return true; + } + + /** + * Method that defines what to do with container elements + * (values contained in an array or {@link java.util.Collection}: + * default implementation simply writes them out. + * + * @since 2.3 + */ + protected boolean includeElement(Object elementValue) { + return true; + } + + /* + /********************************************************** + /* BeanPropertyFilter (deprecated) implementation + /********************************************************** + */ + + @Deprecated + @Override + public void serializeAsField(Object bean, JsonGenerator jgen, + SerializerProvider provider, BeanPropertyWriter writer) throws Exception + { + if (include(writer)) { + writer.serializeAsField(bean, jgen, provider); + } else if (!jgen.canOmitFields()) { // since 2.3 + writer.serializeAsOmittedField(bean, jgen, provider); + } + } + + @Deprecated + @Override + public void depositSchemaProperty(BeanPropertyWriter writer, + ObjectNode propertiesNode, SerializerProvider provider) + throws JsonMappingException + { + if (include(writer)) { + writer.depositSchemaProperty(propertiesNode, provider); + } + } + + @Deprecated + @Override + public void depositSchemaProperty(BeanPropertyWriter writer, + JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) + throws JsonMappingException + { + if (include(writer)) { + writer.depositSchemaProperty(objectVisitor, provider); + } + } + + /* + /********************************************************** + /* PropertyFilter implementation + /********************************************************** + */ + + @Override + public void serializeAsField(Object pojo, JsonGenerator jgen, + SerializerProvider provider, PropertyWriter writer) + throws Exception + { + if (include(writer)) { + writer.serializeAsField(pojo, jgen, provider); + } else if (!jgen.canOmitFields()) { // since 2.3 + writer.serializeAsOmittedField(pojo, jgen, provider); + } + } + + @Override + public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, + PropertyWriter writer) + throws Exception + { + if (includeElement(elementValue)) { + writer.serializeAsElement(elementValue, jgen, provider); + } + } + + @Deprecated + @Override + public void depositSchemaProperty(PropertyWriter writer, + ObjectNode propertiesNode, SerializerProvider provider) + throws JsonMappingException + { + if (include(writer)) { + writer.depositSchemaProperty(propertiesNode, provider); + } + } + + @Override + public void depositSchemaProperty(PropertyWriter writer, + JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) throws JsonMappingException + { + if (include(writer)) { + writer.depositSchemaProperty(objectVisitor, provider); + } + } + + /* + /********************************************************** + /* Sub-classes + /********************************************************** + */ + + /** + * Filter implementation which defaults to filtering out unknown + * properties and only serializes ones explicitly listed. + */ + public static class FilterExceptFilter + extends SimpleBeanPropertyFilter + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + /** + * Set of property names to serialize. + */ + protected final Set _propertiesToInclude; + + public FilterExceptFilter(Set properties) { + _propertiesToInclude = properties; + } + + @Override + protected boolean include(BeanPropertyWriter writer) { + return _propertiesToInclude.contains(writer.getName()); + } + + @Override + protected boolean include(PropertyWriter writer) { + return _propertiesToInclude.contains(writer.getName()); + } + } + + /** + * Filter implementation which defaults to serializing all + * properties, except for ones explicitly listed to be filtered out. + */ + public static class SerializeExceptFilter + extends SimpleBeanPropertyFilter + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + final static SerializeExceptFilter INCLUDE_ALL = new SerializeExceptFilter(); + + /** + * Set of property names to filter out. + */ + protected final Set _propertiesToExclude; + + SerializeExceptFilter() { + _propertiesToExclude = Collections.emptySet(); + } + + public SerializeExceptFilter(Set properties) { + _propertiesToExclude = properties; + } + + @Override + protected boolean include(BeanPropertyWriter writer) { + return !_propertiesToExclude.contains(writer.getName()); + } + + @Override + protected boolean include(PropertyWriter writer) { + return !_propertiesToExclude.contains(writer.getName()); + } + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,187 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.util.*; + +import com.fasterxml.jackson.databind.ser.*; + +/** + * Simple {@link FilterProvider} implementation that just stores + * direct id-to-filter mapping. + *

+ * Note that version 2.3 was a partial rewrite, now that + * {@link PropertyFilter} is set to replace BeanPropertyFilter. + */ +public class SimpleFilterProvider + extends FilterProvider + implements java.io.Serializable // since 2.1 +{ + // for 2.5+ + private static final long serialVersionUID = 1L; + + /** + * Mappings from ids to filters. + */ + protected final Map _filtersById; + + /** + * This is the filter we return in case no mapping was found for + * given id; default is 'null' (in which case caller typically + * reports an error), but can be set to an explicit filter. + */ + protected PropertyFilter _defaultFilter; + + /** + * Flag that indicates whether request for an unknown filter id should + * result an exception (default) or not. + * Note that this is only relevant if no default filter has been + * configured. + */ + protected boolean _cfgFailOnUnknownId = true; + + /* + /********************************************************** + /* Life-cycle: constructing, configuring + /********************************************************** + */ + + public SimpleFilterProvider() { + this(new HashMap()); + } + + /** + * @param mapping Mapping from id to filter; used as is if if possible + */ + @SuppressWarnings("unchecked") + public SimpleFilterProvider(Map mapping) + { + /* 16-Oct-2013, tatu: Since we can now be getting both new and old + * obsolete filters (PropertyFilter vs BeanPropertyFilter), need + * to verify contents. + */ + for (Object ob : mapping.values()) { + if (!(ob instanceof PropertyFilter)) { + _filtersById = _convert(mapping); + return; + } + } + _filtersById = (Map) mapping; + } + + @SuppressWarnings("deprecation") + private final static Map _convert(Map filters) + { + HashMap result = new HashMap(); + for (Map.Entry entry : filters.entrySet()) { + Object f = entry.getValue(); + if (f instanceof PropertyFilter) { + result.put(entry.getKey(), (PropertyFilter) f); + } else if (f instanceof BeanPropertyFilter) { + result.put(entry.getKey(), _convert((BeanPropertyFilter) f)); + } else { + throw new IllegalArgumentException("Unrecognized filter type ("+f.getClass().getName()+")"); + } + } + return result; + } + + @SuppressWarnings("deprecation") + private final static PropertyFilter _convert(BeanPropertyFilter f) { + return SimpleBeanPropertyFilter.from((BeanPropertyFilter) f); + } + + /** + * Method for defining filter to return for "unknown" filters; cases + * where there is no mapping from given id to an explicit filter. + * + * @param f Filter to return when no filter is found for given id + * + * @deprecated Since 2.3 should use {@link PropertyFilter} instead of {@link BeanPropertyFilter} + */ + @Deprecated + public SimpleFilterProvider setDefaultFilter(BeanPropertyFilter f) + { + _defaultFilter = SimpleBeanPropertyFilter.from(f); + return this; + } + + public SimpleFilterProvider setDefaultFilter(PropertyFilter f) + { + _defaultFilter = f; + return this; + } + + /** + * Overloaded variant just to resolve "ties" when using {@link SimpleBeanPropertyFilter}. + */ + public SimpleFilterProvider setDefaultFilter(SimpleBeanPropertyFilter f) + { + _defaultFilter = f; + return this; + } + + public PropertyFilter getDefaultFilter() { + return _defaultFilter; + } + + public SimpleFilterProvider setFailOnUnknownId(boolean state) { + _cfgFailOnUnknownId = state; + return this; + } + + public boolean willFailOnUnknownId() { + return _cfgFailOnUnknownId; + } + + /** + * @deprecated since 2.3 + */ + @Deprecated + public SimpleFilterProvider addFilter(String id, BeanPropertyFilter filter) { + _filtersById.put(id, _convert(filter)); + return this; + } + + public SimpleFilterProvider addFilter(String id, PropertyFilter filter) { + _filtersById.put(id, filter); + return this; + } + + /** + * Overloaded variant just to resolve "ties" when using {@link SimpleBeanPropertyFilter}. + */ + public SimpleFilterProvider addFilter(String id, SimpleBeanPropertyFilter filter) { + _filtersById.put(id, filter); + return this; + } + + public PropertyFilter removeFilter(String id) { + return _filtersById.remove(id); + } + + /* + /********************************************************** + /* Public lookup API + /********************************************************** + */ + + @Deprecated // since 2.3 + @Override + public BeanPropertyFilter findFilter(Object filterId) + { + throw new UnsupportedOperationException("Access to deprecated filters not supported"); + } + + @Override + public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) + { + PropertyFilter f = _filtersById.get(filterId); + if (f == null) { + f = _defaultFilter; + if (f == null && _cfgFailOnUnknownId) { + throw new IllegalArgumentException("No filter configured with id '"+filterId+"' (type " + +filterId.getClass().getName()+")"); + } + } + return f; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,221 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.std.ArraySerializerBase; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Standard serializer used for String[] values. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class StringArraySerializer + extends ArraySerializerBase + implements ContextualSerializer +{ + /* Note: not clean in general, but we are betting against + * anyone re-defining properties of String.class here... + */ + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(String.class); + + public final static StringArraySerializer instance = new StringArraySerializer(); + + /** + * Value serializer to use, if it's not the standard one + * (if it is we can optimize serialization a lot) + */ + protected final JsonSerializer _elementSerializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected StringArraySerializer() { + super(String[].class); + _elementSerializer = null; + } + + @SuppressWarnings("unchecked") + public StringArraySerializer(StringArraySerializer src, + BeanProperty prop, JsonSerializer ser, Boolean unwrapSingle) { + super(src, prop, unwrapSingle); + _elementSerializer = (JsonSerializer) ser; + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { + return new StringArraySerializer(this, prop, _elementSerializer, unwrapSingle); + } + + /** + * Strings never add type info; hence, even if type serializer is suggested, + * we'll ignore it... + */ + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return this; + } + + /* + /********************************************************** + /* Post-processing + /********************************************************** + */ + + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) + throws JsonMappingException + { + /* 29-Sep-2012, tatu: Actually, we need to do much more contextual + * checking here since we finally know for sure the property, + * and it may have overrides + */ + JsonSerializer ser = null; + + // First: if we have a property, may have property-annotation overrides + if (property != null) { + final AnnotationIntrospector ai = provider.getAnnotationIntrospector(); + AnnotatedMember m = property.getMember(); + if (m != null) { + Object serDef = ai.findContentSerializer(m); + if (serDef != null) { + ser = provider.serializerInstance(m, serDef); + } + } + } + // but since formats have both property overrides and global per-type defaults, + // need to do that separately + Boolean unwrapSingle = findFormatFeature(provider, property, String[].class, + JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); + if (ser == null) { + ser = _elementSerializer; + } + // May have a content converter + ser = findConvertingContentSerializer(provider, property, ser); + if (ser == null) { + ser = provider.findValueSerializer(String.class, property); + } else { + ser = provider.handleSecondaryContextualization(ser, property); + } + // Optimization: default serializer just writes String, so we can avoid a call: + if (isDefaultSerializer(ser)) { + ser = null; + } + // note: will never have TypeSerializer, because Strings are "natural" type + if ((ser == _elementSerializer) && (unwrapSingle == _unwrapSingle)) { + return this; + } + return new StringArraySerializer(this, property, ser, unwrapSingle); + } + + /* + /********************************************************** + /* Simple accessors + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + return _elementSerializer; + } + + @Override + public boolean isEmpty(SerializerProvider prov, String[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(String[] value) { + return (value.length == 1); + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public final void serialize(String[] value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(String[] value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final int len = value.length; + if (len == 0) { + return; + } + if (_elementSerializer != null) { + serializeContentsSlow(value, gen, provider, _elementSerializer); + return; + } + for (int i = 0; i < len; ++i) { + String str = value[i]; + if (str == null) { + gen.writeNull(); + } else { + gen.writeString(value[i]); + } + } + } + + private void serializeContentsSlow(String[] value, JsonGenerator gen, SerializerProvider provider, JsonSerializer ser) + throws IOException + { + for (int i = 0, len = value.length; i < len; ++i) { + String str = value[i]; + if (str == null) { + provider.defaultSerializeNull(gen); + } else { + ser.serialize(value[i], gen, provider); + } + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("array", true).set("items", createSchemaNode("string")); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.STRING); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,151 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase; + +/** + * Efficient implement for serializing {@link Collection}s that contain Strings. + * The only complexity is due to possibility that serializer for {@link String} + * may be overridde; because of this, logic is needed to ensure that the default + * serializer is in use to use fastest mode, or if not, to defer to custom + * String serializer. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class StringCollectionSerializer + extends StaticListSerializerBase> +{ + public final static StringCollectionSerializer instance = new StringCollectionSerializer(); + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected StringCollectionSerializer() { + super(Collection.class); + } + + protected StringCollectionSerializer(StringCollectionSerializer src, + JsonSerializer ser, Boolean unwrapSingle) + { + super(src, ser, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, + JsonSerializer ser, Boolean unwrapSingle) { + return new StringCollectionSerializer(this, ser, unwrapSingle); + } + + @Override protected JsonNode contentSchema() { + return createSchemaNode("string", true); + } + + @Override + protected void acceptContentVisitor(JsonArrayFormatVisitor visitor) throws JsonMappingException + { + visitor.itemsFormat(JsonFormatTypes.STRING); + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public void serialize(Collection value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + final int len = value.size(); + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + _serializeUnwrapped(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + if (_serializer == null) { + serializeContents(value, gen, provider); + } else { + serializeUsingCustom(value, gen, provider); + } + gen.writeEndArray(); + } + + private final void _serializeUnwrapped(Collection value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + if (_serializer == null) { + serializeContents(value, gen, provider); + } else { + serializeUsingCustom(value, gen, provider); + } + } + + @Override + public void serializeWithType(Collection value, JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonGenerationException + { + typeSer.writeTypePrefixForArray(value, jgen); + if (_serializer == null) { + serializeContents(value, jgen, provider); + } else { + serializeUsingCustom(value, jgen, provider); + } + typeSer.writeTypeSuffixForArray(value, jgen); + } + + private final void serializeContents(Collection value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + if (_serializer != null) { + serializeUsingCustom(value, jgen, provider); + return; + } + int i = 0; + for (String str : value) { + try { + if (str == null) { + provider.defaultSerializeNull(jgen); + } else { + jgen.writeString(str); + } + ++i; + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } + } + + private void serializeUsingCustom(Collection value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + final JsonSerializer ser = _serializer; + int i = 0; + for (String str : value) { + try { + if (str == null) { + provider.defaultSerializeNull(jgen); + } else { + ser.serialize(str, jgen, provider); + } + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Simple serializer that will call configured type serializer, passing + * in configured data serializer, and exposing it all as a simple + * serializer. + */ +public final class TypeWrappedSerializer + extends JsonSerializer +{ + final protected TypeSerializer _typeSerializer; + final protected JsonSerializer _serializer; + + @SuppressWarnings("unchecked") + public TypeWrappedSerializer(TypeSerializer typeSer, JsonSerializer ser) + { + super(); + _typeSerializer = typeSer; + _serializer = (JsonSerializer) ser; + } + + @Override + public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + _serializer.serializeWithType(value, jgen, provider, _typeSerializer); + } + + @Override + public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + /* Is this an erroneous call? For now, let's assume it is not, and + * that type serializer is just overridden if so + */ + _serializer.serializeWithType(value, jgen, provider, typeSer); + } + + @Override + public Class handledType() { return Object.class; } + + /* + /********************************************************** + /* Extended API for other core classes + /********************************************************** + */ + + public JsonSerializer valueSerializer() { + return _serializer; + } + + public TypeSerializer typeSerializer() { + return _typeSerializer; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,71 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +@SuppressWarnings("serial") +public class UnknownSerializer + extends StdSerializer +{ + public UnknownSerializer() { + super(Object.class); + } + + /** + * @since 2.6 + */ + public UnknownSerializer(Class cls) { + super(cls, false); + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + // 27-Nov-2009, tatu: As per [JACKSON-201] may or may not fail... + if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { + failForEmpty(gen, value); + } + // But if it's fine, we'll just output empty JSON Object: + gen.writeStartObject(); + gen.writeEndObject(); + } + + @Override + public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { + failForEmpty(gen, value); + } + typeSer.writeTypePrefixForObject(value, gen); + typeSer.writeTypeSuffixForObject(value, gen); + } + + @Override + public boolean isEmpty(SerializerProvider provider, Object value) { + return true; + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { + return null; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitor.expectAnyFormat(typeHint); + } + + protected void failForEmpty(JsonGenerator gen, Object value) throws JsonMappingException { + throw JsonMappingException.from(gen, + "No serializer found for class "+value.getClass().getName()+" and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,221 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.util.Iterator; +import java.util.Map.Entry; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Variant of {@link BeanPropertyWriter} which will handle unwrapping + * of JSON Object (including of properties of Object within surrounding + * JSON object, and not as sub-object). + */ +public class UnwrappingBeanPropertyWriter + extends BeanPropertyWriter + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Transformer used to add prefix and/or suffix for properties + * of unwrapped POJO. + */ + protected final NameTransformer _nameTransformer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public UnwrappingBeanPropertyWriter(BeanPropertyWriter base, NameTransformer unwrapper) { + super(base); + _nameTransformer = unwrapper; + } + + protected UnwrappingBeanPropertyWriter(UnwrappingBeanPropertyWriter base, NameTransformer transformer, + SerializedString name) { + super(base, name); + _nameTransformer = transformer; + } + + @Override + public UnwrappingBeanPropertyWriter rename(NameTransformer transformer) + { + String oldName = _name.getValue(); + String newName = transformer.transform(oldName); + + // important: combine transformers: + transformer = NameTransformer.chainedTransformer(transformer, _nameTransformer); + + return _new(transformer, new SerializedString(newName)); + } + + /** + * Overridable factory method used by sub-classes + * + * @since 2.6.0 + */ + protected UnwrappingBeanPropertyWriter _new(NameTransformer transformer, SerializedString newName) + { + return new UnwrappingBeanPropertyWriter(this, transformer, newName); + } + + /* + /********************************************************** + /* Overrides, public methods + /********************************************************** + */ + + @Override + public boolean isUnwrapping() { + return true; + } + + @Override + public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) + throws Exception + { + final Object value = get(bean); + if (value == null) { + // Hmmh. I assume we MUST pretty much suppress nulls, since we + // can't really unwrap them... + return; + } + JsonSerializer ser = _serializer; + if (ser == null) { + Class cls = value.getClass(); + PropertySerializerMap map = _dynamicSerializers; + ser = map.serializerFor(cls); + if (ser == null) { + ser = _findAndAddDynamic(map, cls, prov); + } + } + if (_suppressableValue != null) { + if (MARKER_FOR_EMPTY == _suppressableValue) { + if (ser.isEmpty(prov, value)) { + return; + } + } else if (_suppressableValue.equals(value)) { + return; + } + } + // For non-nulls, first: simple check for direct cycles + if (value == bean) { + if (_handleSelfReference(bean, gen, prov, ser)) { + return; + } + } + + // note: must verify we are using unwrapping serializer; if not, will write field name + if (!ser.isUnwrappingSerializer()) { + gen.writeFieldName(_name); + } + + if (_typeSerializer == null) { + ser.serialize(value, gen, prov); + } else { + ser.serializeWithType(value, gen, prov, _typeSerializer); + } + } + + // need to override as we must get unwrapping instance... + @Override + public void assignSerializer(JsonSerializer ser) + { + super.assignSerializer(ser); + if (_serializer != null) { + NameTransformer t = _nameTransformer; + if (_serializer.isUnwrappingSerializer()) { + t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) _serializer)._nameTransformer); + } + _serializer = _serializer.unwrappingSerializer(t); + } + } + + /* + /********************************************************** + /* Overrides: schema generation + /********************************************************** + */ + + @Override + public void depositSchemaProperty(final JsonObjectFormatVisitor visitor, + SerializerProvider provider) throws JsonMappingException + { + JsonSerializer ser = provider + .findValueSerializer(this.getType(), this) + .unwrappingSerializer(_nameTransformer); + + if (ser.isUnwrappingSerializer()) { + ser.acceptJsonFormatVisitor(new JsonFormatVisitorWrapper.Base(provider) { + // an unwrapping serializer will always expect ObjectFormat, + // hence, the other cases do not have to be implemented + @Override + public JsonObjectFormatVisitor expectObjectFormat(JavaType type) + throws JsonMappingException { + return visitor; + } + }, this.getType()); + } else { + super.depositSchemaProperty(visitor, provider); + } + } + + // Override needed to support legacy JSON Schema generator + @Override + protected void _depositSchemaProperty(ObjectNode propertiesNode, JsonNode schemaNode) + { + JsonNode props = schemaNode.get("properties"); + if (props != null) { + Iterator> it = props.fields(); + while (it.hasNext()) { + Entry entry = it.next(); + String name = entry.getKey(); + if (_nameTransformer != null) { + name = _nameTransformer.transform(name); + } + propertiesNode.set(name, entry.getValue()); + } + } + } + + /* + /********************************************************** + /* Overrides: internal, other + /********************************************************** + */ + + // need to override as we must get unwrapping instance... + @Override + protected JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + JsonSerializer serializer; + if (_nonTrivialBaseType != null) { + JavaType subtype = provider.constructSpecializedType(_nonTrivialBaseType, type); + serializer = provider.findValueSerializer(subtype, this); + } else { + serializer = provider.findValueSerializer(type, this); + } + NameTransformer t = _nameTransformer; + if (serializer.isUnwrappingSerializer()) { + t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) serializer)._nameTransformer); + } + serializer = serializer.unwrappingSerializer(t); + + _dynamicSerializers = _dynamicSerializers.newWith(type, serializer); + return serializer; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,154 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.NameTransformer; + +import java.io.IOException; + +public class UnwrappingBeanSerializer + extends BeanSerializerBase + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Transformer used to add prefix and/or suffix for properties + * of unwrapped POJO. + */ + protected final NameTransformer _nameTransformer; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + /** + * Constructor used for creating unwrapping instance of a + * standard BeanSerializer + */ + public UnwrappingBeanSerializer(BeanSerializerBase src, NameTransformer transformer) { + super(src, transformer); + _nameTransformer = transformer; + } + + public UnwrappingBeanSerializer(UnwrappingBeanSerializer src, + ObjectIdWriter objectIdWriter) { + super(src, objectIdWriter); + _nameTransformer = src._nameTransformer; + } + + public UnwrappingBeanSerializer(UnwrappingBeanSerializer src, + ObjectIdWriter objectIdWriter, Object filterId) { + super(src, objectIdWriter, filterId); + _nameTransformer = src._nameTransformer; + } + + protected UnwrappingBeanSerializer(UnwrappingBeanSerializer src, String[] toIgnore) { + super(src, toIgnore); + _nameTransformer = src._nameTransformer; + } + + /* + /********************************************************** + /* Life-cycle: factory methods, fluent factories + /********************************************************** + */ + + @Override + public JsonSerializer unwrappingSerializer(NameTransformer transformer) { + // !!! 23-Jan-2012, tatu: Should we chain transformers? + return new UnwrappingBeanSerializer(this, transformer); + } + + @Override + public boolean isUnwrappingSerializer() { + return true; // sure is + } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + return new UnwrappingBeanSerializer(this, objectIdWriter); + } + + @Override + public BeanSerializerBase withFilterId(Object filterId) { + return new UnwrappingBeanSerializer(this, _objectIdWriter, filterId); + } + + @Override + protected BeanSerializerBase withIgnorals(String[] toIgnore) { + return new UnwrappingBeanSerializer(this, toIgnore); + } + + /** + * JSON Array output can not be done if unwrapping operation is + * requested; so implementation will simply return 'this'. + */ + @Override + protected BeanSerializerBase asArraySerializer() { + return this; + } + + /* + /********************************************************** + /* JsonSerializer implementation that differs between impls + /********************************************************** + */ + + /** + * Main serialization method that will delegate actual output to + * configured + * {@link BeanPropertyWriter} instances. + */ + @Override + public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException + { + gen.setCurrentValue(bean); // [databind#631] + if (_objectIdWriter != null) { + _serializeWithObjectId(bean, gen, provider, false); + return; + } + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + } + + @Override + public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + if (provider.isEnabled(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS)) { + throw JsonMappingException.from(gen, + "Unwrapped property requires use of type information: can not serialize without disabling `SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS`"); + } + + gen.setCurrentValue(bean); // [databind#631] + if (_objectIdWriter != null) { + _serializeWithObjectId(bean, gen, provider, typeSer); + return; + } + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override public String toString() { + return "UnwrappingBeanSerializer for "+handledType().getName(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/WritableObjectId.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/WritableObjectId.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/WritableObjectId.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind.ser.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.SerializableString; + +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * Simple value container used to keep track of Object Ids during + * serialization. + */ +public final class WritableObjectId +{ + public final ObjectIdGenerator generator; + + public Object id; + + protected boolean idWritten = false; + + public WritableObjectId(ObjectIdGenerator generator) { + this.generator = generator; + } + + public boolean writeAsId(JsonGenerator gen, SerializerProvider provider, ObjectIdWriter w) throws IOException + { + if (id != null && (idWritten || w.alwaysAsId)) { + // 03-Aug-2013, tatu: Prefer Native Object Ids if available + if (gen.canWriteObjectId()) { + gen.writeObjectRef(String.valueOf(id)); + } else { + w.serializer.serialize(id, gen, provider); + } + return true; + } + return false; + } + + public Object generateId(Object forPojo) { + return (id = generator.generateId(forPojo)); + } + + /** + * Method called to output Object Id as specified. + */ + public void writeAsField(JsonGenerator gen, SerializerProvider provider, + ObjectIdWriter w) throws IOException + { + idWritten = true; + + // 03-Aug-2013, tatu: Prefer Native Object Ids if available + if (gen.canWriteObjectId()) { + // Need to assume String(ified) ids, for now... could add 'long' variant? + gen.writeObjectId(String.valueOf(id)); + return; + } + + SerializableString name = w.propertyName; + if (name != null) { + gen.writeFieldName(name); + w.serializer.serialize(id, gen, provider); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/impl/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,5 @@ +/** + * Contains implementation classes of serialization part of + * data binding. + */ +package com.fasterxml.jackson.databind.ser.impl; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,5 @@ +/** + * Contains implementation classes of serialization part of + * data binding. + */ +package com.fasterxml.jackson.databind.ser; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,144 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.*; + +/** + * Intermediate base class for serializers used for various + * Java arrays. + * + * @param Type of arrays serializer handles + */ +@SuppressWarnings("serial") +public abstract class ArraySerializerBase + extends ContainerSerializer + implements ContextualSerializer // for 'unwrapSingleElemArray' +{ + protected final BeanProperty _property; + + /** + * Setting for specific local override for "unwrap single element arrays": + * true for enable unwrapping, false for preventing it, `null` for using + * global configuration. + * + * @since 2.6 + */ + protected final Boolean _unwrapSingle; + + protected ArraySerializerBase(Class cls) + { + super(cls); + _property = null; + _unwrapSingle = null; + } + + /** + * Use either variant that just takes type (non-contextual), or, + * copy constructor that allows passing of property. + * + * @deprecated Since 2.6 + */ + @Deprecated + protected ArraySerializerBase(Class cls, BeanProperty property) + { + super(cls); + _property = property; + _unwrapSingle = null; + } + + protected ArraySerializerBase(ArraySerializerBase src) + { + super(src._handledType, false); + _property = src._property; + _unwrapSingle = src._unwrapSingle; + } + + /** + * @since 2.6 + */ + protected ArraySerializerBase(ArraySerializerBase src, BeanProperty property, + Boolean unwrapSingle) + { + super(src._handledType, false); + _property = property; + _unwrapSingle = unwrapSingle; + } + + /** + * @deprecated Since 2.6 + */ + @Deprecated + protected ArraySerializerBase(ArraySerializerBase src, BeanProperty property) + { + super(src._handledType, false); + _property = property; + _unwrapSingle = src._unwrapSingle; + } + + /** + * @since 2.6 + */ + public abstract JsonSerializer _withResolved(BeanProperty prop, + Boolean unwrapSingle); + + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) throws JsonMappingException + { + Boolean unwrapSingle = null; + + // First: if we have a property, may have property-annotation overrides + if (property != null) { + JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType); + if (format != null) { + unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); + if (unwrapSingle != _unwrapSingle) { + return _withResolved(property, unwrapSingle); + } + } + } + return this; + } + + // NOTE: as of 2.5, sub-classes SHOULD override (in 2.4 and before, was final), + // at least if they can provide access to actual size of value and use `writeStartArray()` + // variant that passes size of array to output, which is helpful with some data formats + @Override + public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + if (hasSingleElement(value)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public final void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException + { + // note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work + typeSer.writeTypePrefixForArray(value, gen); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + serializeContents(value, gen, provider); + typeSer.writeTypeSuffixForArray(value, gen); + } + + protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider) + throws IOException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,329 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; + +/** + * Base class for serializers that will output contents as JSON + * arrays; typically serializers used for {@link java.util.Collection} + * and array types. + */ +@SuppressWarnings("serial") +public abstract class AsArraySerializerBase + extends ContainerSerializer + implements ContextualSerializer +{ + protected final JavaType _elementType; + + /** + * Collection-valued property being serialized with this instance + */ + protected final BeanProperty _property; + + protected final boolean _staticTyping; + + /** + * Setting for specific local override for "unwrap single element arrays": + * true for enable unwrapping, false for preventing it, `null` for using + * global configuration. + * + * @since 2.6 + */ + protected final Boolean _unwrapSingle; + + /** + * Type serializer used for values, if any. + */ + protected final TypeSerializer _valueTypeSerializer; + + /** + * Value serializer to use, if it can be statically determined + */ + protected final JsonSerializer _elementSerializer; + + /** + * If element type can not be statically determined, mapping from + * runtime type to serializer is handled using this object + */ + protected PropertySerializerMap _dynamicSerializers; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Non-contextual, "blueprint" constructor typically called when the first + * instance is created, without knowledge of property it was used via. + * + * @since 2.6 + */ + protected AsArraySerializerBase(Class cls, JavaType et, boolean staticTyping, + TypeSerializer vts, JsonSerializer elementSerializer) + { + super(cls, false); + _elementType = et; + // static if explicitly requested, or if element type is final + _staticTyping = staticTyping || (et != null && et.isFinal()); + _valueTypeSerializer = vts; + _property = null; + _elementSerializer = elementSerializer; + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + _unwrapSingle = null; + } + + /** + * @deprecated Since 2.6 Use variants that either take 'src', or do NOT pass + * BeanProperty + */ + @Deprecated + protected AsArraySerializerBase(Class cls, JavaType et, boolean staticTyping, + TypeSerializer vts, BeanProperty property, JsonSerializer elementSerializer) + { + // typing with generics is messy... have to resort to this: + super(cls, false); + _elementType = et; + // static if explicitly requested, or if element type is final + _staticTyping = staticTyping || (et != null && et.isFinal()); + _valueTypeSerializer = vts; + _property = property; + _elementSerializer = elementSerializer; + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + _unwrapSingle = null; + } + + @SuppressWarnings("unchecked") + protected AsArraySerializerBase(AsArraySerializerBase src, + BeanProperty property, TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) + { + super(src); + _elementType = src._elementType; + _staticTyping = src._staticTyping; + _valueTypeSerializer = vts; + _property = property; + _elementSerializer = (JsonSerializer) elementSerializer; + _dynamicSerializers = src._dynamicSerializers; + _unwrapSingle = unwrapSingle; + } + + /** + * @deprecated since 2.6: use the overloaded method that takes 'unwrapSingle' + */ + @Deprecated + protected AsArraySerializerBase(AsArraySerializerBase src, + BeanProperty property, TypeSerializer vts, JsonSerializer elementSerializer) + { + this(src, property, vts, elementSerializer, src._unwrapSingle); + } + + /** + * @deprecated since 2.6: use the overloaded method that takes 'unwrapSingle' + */ + @Deprecated + public final AsArraySerializerBase withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer) { + return withResolved(property, vts, elementSerializer, _unwrapSingle); + } + + /** + * @since 2.6 + */ + public abstract AsArraySerializerBase withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle); + + /* + /********************************************************** + /* Post-processing + /********************************************************** + */ + + /** + * This method is needed to resolve contextual annotations like + * per-property overrides, as well as do recursive call + * to createContextual of content serializer, if + * known statically. + */ + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) + throws JsonMappingException + { + TypeSerializer typeSer = _valueTypeSerializer; + if (typeSer != null) { + typeSer = typeSer.forProperty(property); + } + /* 29-Sep-2012, tatu: Actually, we need to do much more contextual + * checking here since we finally know for sure the property, + * and it may have overrides + */ + JsonSerializer ser = null; + Boolean unwrapSingle = null; + // First: if we have a property, may have property-annotation overrides + + if (property != null) { + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + AnnotatedMember m = property.getMember(); + if (m != null) { + Object serDef = intr.findContentSerializer(m); + if (serDef != null) { + ser = provider.serializerInstance(m, serDef); + } + } + JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType); + if (format != null) { + unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); + } + } + if (ser == null) { + ser = _elementSerializer; + } + // 18-Feb-2013, tatu: May have a content converter: + ser = findConvertingContentSerializer(provider, property, ser); + if (ser == null) { + // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated, + // we can consider it a static case as well. + if (_elementType != null) { + if (_staticTyping && !_elementType.isJavaLangObject()) { + ser = provider.findValueSerializer(_elementType, property); + } + } + } else { + ser = provider.handleSecondaryContextualization(ser, property); + } + if ((ser != _elementSerializer) + || (property != _property) + || (_valueTypeSerializer != typeSer) + || (_unwrapSingle != unwrapSingle)) { + return withResolved(property, typeSer, ser, unwrapSingle); + } + return this; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _elementType; + } + + @Override + public JsonSerializer getContentSerializer() { + return _elementSerializer; + } + + /* + /********************************************************** + /* Serialization + /********************************************************** + */ + + // NOTE: as of 2.5, sub-classes SHOULD override (in 2.4 and before, was final), + // at least if they can provide access to actual size of value and use `writeStartArray()` + // variant that passes size of array to output, which is helpful with some data formats + @Override + public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) + && hasSingleElement(value)) { + serializeContents(value, gen, provider); + return; + } + gen.writeStartArray(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + // note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work + typeSer.writeTypePrefixForArray(value, gen); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + serializeContents(value, gen, provider); + typeSer.writeTypeSuffixForArray(value, gen); + } + + protected abstract void serializeContents(T value, JsonGenerator gen, SerializerProvider provider) + throws IOException; + + @SuppressWarnings("deprecation") + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = createSchemaNode("array", true); + JavaType contentType = _elementType; + if (contentType != null) { + JsonNode schemaNode = null; + // 15-Oct-2010, tatu: We can't serialize plain Object.class; but what should it produce here? Untyped? + if (contentType.getRawClass() != Object.class) { + JsonSerializer ser = provider.findValueSerializer(contentType, _property); + if (ser instanceof SchemaAware) { + schemaNode = ((SchemaAware) ser).getSchema(provider, null); + } + } + if (schemaNode == null) { + schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); + } + o.set("items", schemaNode); + } + return o; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + JsonSerializer valueSer = _elementSerializer; + if (valueSer == null) { + valueSer = visitor.getProvider().findValueSerializer(_elementType, _property); + } + visitArrayFormat(visitor, typeHint, valueSer, _elementType); + } + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicSerializers = result.map; + } + return result.serializer; + } + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + JavaType type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + if (map != result.map) { + _dynamicSerializers = result.map; + } + return result.serializer; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/AtomicReferenceSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,356 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.type.ReferenceType; +import com.fasterxml.jackson.databind.util.NameTransformer; + +// Since 2.6 in its own class +public class AtomicReferenceSerializer + extends StdSerializer> + implements ContextualSerializer +{ + private static final long serialVersionUID = 1L; + + /** + * Declared type parameter for Optional. + */ + protected final JavaType _referredType; + + protected final BeanProperty _property; + + /** + * Type serializer used for values, if any. + * + * @since 2.7 + */ + protected final TypeSerializer _valueTypeSerializer; + + /** + * @since 2.7 + */ + protected final JsonSerializer _valueSerializer; + + /** + * To support unwrapped values of dynamic types, will need this: + * + * @since 2.7 + */ + protected final NameTransformer _unwrapper; + + /** + * Further guidance on serialization-inclusion (or not), regarding + * contained value (if any). + * + * @since 2.7 + */ + protected final JsonInclude.Include _contentInclusion; + + /** + * If element type can not be statically determined, mapping from + * runtime type to serializer is handled using this object + * + * @since 2.7 + */ + protected transient PropertySerializerMap _dynamicSerializers; + + /* + /********************************************************** + /* Constructors, factory methods + /********************************************************** + */ + + public AtomicReferenceSerializer(ReferenceType fullType, boolean staticTyping, TypeSerializer vts, + JsonSerializer ser) + { + super(fullType); + _referredType = fullType.getReferencedType(); + _property = null; + _valueTypeSerializer = vts; + _valueSerializer = ser; + _unwrapper = null; + _contentInclusion = null; + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + } + + @SuppressWarnings("unchecked") + protected AtomicReferenceSerializer(AtomicReferenceSerializer base, BeanProperty property, + TypeSerializer vts, JsonSerializer valueSer, + NameTransformer unwrapper, + JsonInclude.Include contentIncl) + { + super(base); + _referredType = base._referredType; + _dynamicSerializers = base._dynamicSerializers; + _property = property; + _valueTypeSerializer = vts; + _valueSerializer = (JsonSerializer) valueSer; + _unwrapper = unwrapper; + if ((contentIncl == JsonInclude.Include.USE_DEFAULTS) + || (contentIncl == JsonInclude.Include.ALWAYS)) { + _contentInclusion = null; + } else { + _contentInclusion = contentIncl; + } + } + + @Override + public JsonSerializer> unwrappingSerializer(NameTransformer transformer) { + JsonSerializer ser = _valueSerializer; + if (ser != null) { + ser = ser.unwrappingSerializer(transformer); + } + NameTransformer unwrapper = (_unwrapper == null) ? transformer + : NameTransformer.chainedTransformer(transformer, _unwrapper); + return withResolved(_property, _valueTypeSerializer, ser, unwrapper, _contentInclusion); + } + + protected AtomicReferenceSerializer withResolved(BeanProperty prop, + TypeSerializer vts, JsonSerializer valueSer, + NameTransformer unwrapper, + JsonInclude.Include contentIncl) + { + if ((_property == prop) && (contentIncl == _contentInclusion) + && (_valueTypeSerializer == vts) && (_valueSerializer == valueSer) + && (_unwrapper == unwrapper)) { + return this; + } + return new AtomicReferenceSerializer(this, prop, vts, valueSer, unwrapper, contentIncl); + } + + /* + /********************************************************** + /* Contextualization (support for property annotations) + /********************************************************** + */ + + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) throws JsonMappingException + { + TypeSerializer typeSer = _valueTypeSerializer; + if (typeSer != null) { + typeSer = typeSer.forProperty(property); + } + // First: do we have an annotation override from property? + JsonSerializer ser = findAnnotatedContentSerializer(provider, property);; + if (ser == null) { + // If not, use whatever was configured by type + ser = _valueSerializer; + if (ser == null) { + // A few conditions needed to be able to fetch serializer here: + if (_useStatic(provider, property, _referredType)) { + ser = _findSerializer(provider, _referredType, property); + } + } else { + ser = provider.handlePrimaryContextualization(ser, property); + } + } + // Also: may want to have more refined exclusion based on referenced value + JsonInclude.Include contentIncl = _contentInclusion; + if (property != null) { + JsonInclude.Value incl = property.findPropertyInclusion(provider.getConfig(), + AtomicReference.class); + JsonInclude.Include newIncl = incl.getContentInclusion(); + if ((newIncl != contentIncl) && (newIncl != JsonInclude.Include.USE_DEFAULTS)) { + contentIncl = newIncl; + } + } + return withResolved(property, typeSer, ser, _unwrapper, contentIncl); + } + + protected boolean _useStatic(SerializerProvider provider, BeanProperty property, + JavaType referredType) + { + // First: no serializer for `Object.class`, must be dynamic + if (referredType.isJavaLangObject()) { + return false; + } + // but if type is final, might as well fetch + if (referredType.isFinal()) { // or should we allow annotation override? (only if requested...) + return true; + } + // also: if indicated by typing, should be considered static + if (referredType.useStaticType()) { + return true; + } + // if neither, maybe explicit annotation? + AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + if ((intr != null) && (property != null)) { + Annotated ann = property.getMember(); + if (ann != null) { + JsonSerialize.Typing t = intr.findSerializationTyping(property.getMember()); + if (t == JsonSerialize.Typing.STATIC) { + return true; + } + if (t == JsonSerialize.Typing.DYNAMIC) { + return false; + } + } + } + // and finally, may be forced by global static typing (unlikely...) + return provider.isEnabled(MapperFeature.USE_STATIC_TYPING); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public boolean isEmpty(SerializerProvider provider, AtomicReference value) + { + if (value == null) { + return true; + } + Object contents = value.get(); + if (contents == null) { + return true; + } + if (_contentInclusion == null) { + return false; + } + JsonSerializer ser = _valueSerializer; + if (ser == null) { + try { + ser = _findCachedSerializer(provider, contents.getClass()); + } catch (JsonMappingException e) { // nasty but necessary + throw new RuntimeJsonMappingException(e); + } + } + return ser.isEmpty(provider, contents); + + } + + @Override + public boolean isUnwrappingSerializer() { + return (_unwrapper != null); + } + + /* + /********************************************************** + /* Serialization methods + /********************************************************** + */ + + @Override + public void serialize(AtomicReference ref, JsonGenerator g, SerializerProvider provider) + throws IOException + { + Object value = ref.get(); + if (value == null) { + if (_unwrapper == null) { + provider.defaultSerializeNull(g); + } + return; + } + JsonSerializer ser = _valueSerializer; + if (ser == null) { + ser = _findCachedSerializer(provider, value.getClass()); + } + if (_valueTypeSerializer != null) { + ser.serializeWithType(value, g, provider, _valueTypeSerializer); + } else { + ser.serialize(value, g, provider); + } + } + + @Override + public void serializeWithType(AtomicReference ref, + JsonGenerator g, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + Object value = ref.get(); + if (value == null) { + if (_unwrapper == null) { + provider.defaultSerializeNull(g); + } + return; + } + + // 19-Apr-2016, tatu: In order to basically "skip" the whole wrapper level + // (which is what non-polymorphic serialization does too), we will need + // to simply delegate call, I think, and NOT try to use it here. + + // Otherwise apply type-prefix/suffix, then std serialize: + /* + typeSer.writeTypePrefixForScalar(ref, g); + serialize(ref, g, provider); + typeSer.writeTypeSuffixForScalar(ref, g); + */ + + JsonSerializer ser = _valueSerializer; + if (ser == null) { + ser = _findCachedSerializer(provider, value.getClass()); + } + ser.serializeWithType(value, g, provider, typeSer); + } + + /* + /********************************************************** + /* Introspection support + /********************************************************** + */ + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + JsonSerializer ser = _valueSerializer; + if (ser == null) { + ser = _findSerializer(visitor.getProvider(), _referredType, _property); + if (_unwrapper != null) { + ser = ser.unwrappingSerializer(_unwrapper); + } + } + ser.acceptJsonFormatVisitor(visitor, _referredType); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + /** + * Helper method that encapsulates logic of retrieving and caching required + * serializer. + */ + private final JsonSerializer _findCachedSerializer(SerializerProvider provider, + Class type) throws JsonMappingException + { + JsonSerializer ser = _dynamicSerializers.serializerFor(type); + if (ser == null) { + ser = _findSerializer(provider, type, _property); + if (_unwrapper != null) { + ser = ser.unwrappingSerializer(_unwrapper); + } + _dynamicSerializers = _dynamicSerializers.newWith(type, ser); + } + return ser; + } + + private final JsonSerializer _findSerializer(SerializerProvider provider, + Class type, BeanProperty prop) throws JsonMappingException + { + return provider.findTypedValueSerializer(type, true, prop); + } + + private final JsonSerializer _findSerializer(SerializerProvider provider, + JavaType type, BeanProperty prop) throws JsonMappingException + { + return provider.findTypedValueSerializer(type, true, prop); + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,826 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; +import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator; +import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.Converter; +import com.fasterxml.jackson.databind.util.NameTransformer; + +/** + * Base class both for the standard bean serializer, and couple + * of variants that only differ in small details. + * Can be used for custom bean serializers as well, although that + * is not the primary design goal. + */ +@SuppressWarnings("serial") +public abstract class BeanSerializerBase + extends StdSerializer + implements ContextualSerializer, ResolvableSerializer, + JsonFormatVisitable, SchemaAware +{ + protected final static PropertyName NAME_FOR_OBJECT_REF = new PropertyName("#object-ref"); + + final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0]; + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Writers used for outputting actual property values + */ + final protected BeanPropertyWriter[] _props; + + /** + * Optional filters used to suppress output of properties that + * are only to be included in certain views + */ + final protected BeanPropertyWriter[] _filteredProps; + + /** + * Handler for {@link com.fasterxml.jackson.annotation.JsonAnyGetter} + * annotated properties + */ + final protected AnyGetterWriter _anyGetterWriter; + + /** + * Id of the bean property filter to use, if any; null if none. + */ + final protected Object _propertyFilterId; + + /** + * If using custom type ids (usually via getter, or field), this is the + * reference to that member. + */ + final protected AnnotatedMember _typeId; + + /** + * If this POJO can be alternatively serialized using just an object id + * to denote a reference to previously serialized object, + * this Object will handle details. + */ + final protected ObjectIdWriter _objectIdWriter; + + /** + * Requested shape from bean class annotations. + */ + final protected JsonFormat.Shape _serializationShape; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + /** + * Constructor used by {@link BeanSerializerBuilder} to create an + * instance + * + * @param type Nominal type of values handled by this serializer + * @param builder Builder for accessing other collected information + */ + protected BeanSerializerBase(JavaType type, BeanSerializerBuilder builder, + BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) + { + super(type); + _props = properties; + _filteredProps = filteredProperties; + if (builder == null) { // mostly for testing + _typeId = null; + _anyGetterWriter = null; + _propertyFilterId = null; + _objectIdWriter = null; + _serializationShape = null; + } else { + _typeId = builder.getTypeId(); + _anyGetterWriter = builder.getAnyGetter(); + _propertyFilterId = builder.getFilterId(); + _objectIdWriter = builder.getObjectIdWriter(); + JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(null); + _serializationShape = (format == null) ? null : format.getShape(); + } + } + + public BeanSerializerBase(BeanSerializerBase src, + BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) + { + super(src._handledType); + _props = properties; + _filteredProps = filteredProperties; + + _typeId = src._typeId; + _anyGetterWriter = src._anyGetterWriter; + _objectIdWriter = src._objectIdWriter; + _propertyFilterId = src._propertyFilterId; + _serializationShape = src._serializationShape; + } + + protected BeanSerializerBase(BeanSerializerBase src, + ObjectIdWriter objectIdWriter) + { + this(src, objectIdWriter, src._propertyFilterId); + } + + /** + * @since 2.3 + */ + protected BeanSerializerBase(BeanSerializerBase src, + ObjectIdWriter objectIdWriter, Object filterId) + { + super(src._handledType); + _props = src._props; + _filteredProps = src._filteredProps; + + _typeId = src._typeId; + _anyGetterWriter = src._anyGetterWriter; + _objectIdWriter = objectIdWriter; + _propertyFilterId = filterId; + _serializationShape = src._serializationShape; + } + + protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore) + { + super(src._handledType); + + // Bit clumsy, but has to do: + HashSet ignoredSet = ArrayBuilders.arrayToSet(toIgnore); + final BeanPropertyWriter[] propsIn = src._props; + final BeanPropertyWriter[] fpropsIn = src._filteredProps; + final int len = propsIn.length; + + ArrayList propsOut = new ArrayList(len); + ArrayList fpropsOut = (fpropsIn == null) ? null : new ArrayList(len); + + for (int i = 0; i < len; ++i) { + BeanPropertyWriter bpw = propsIn[i]; + // should be ignored? + if (ignoredSet.contains(bpw.getName())) { + continue; + } + propsOut.add(bpw); + if (fpropsIn != null) { + fpropsOut.add(fpropsIn[i]); + } + } + _props = propsOut.toArray(new BeanPropertyWriter[propsOut.size()]); + _filteredProps = (fpropsOut == null) ? null : fpropsOut.toArray(new BeanPropertyWriter[fpropsOut.size()]); + + _typeId = src._typeId; + _anyGetterWriter = src._anyGetterWriter; + _objectIdWriter = src._objectIdWriter; + _propertyFilterId = src._propertyFilterId; + _serializationShape = src._serializationShape; + } + + /** + * Mutant factory used for creating a new instance with different + * {@link ObjectIdWriter}. + * + * @since 2.0 + */ + public abstract BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter); + + /** + * Mutant factory used for creating a new instance with additional + * set of properties to ignore (from properties this instance otherwise has) + * + * @since 2.0 + */ + protected abstract BeanSerializerBase withIgnorals(String[] toIgnore); + + /** + * Mutant factory for creating a variant that output POJO as a + * JSON Array. Implementations may ignore this request if output + * as array is not possible (either at all, or reliably). + * + * @since 2.1 + */ + protected abstract BeanSerializerBase asArraySerializer(); + + /** + * Mutant factory used for creating a new instance with different + * filter id (used with JsonFilter annotation) + * + * @since 2.3 + */ + @Override + public abstract BeanSerializerBase withFilterId(Object filterId); + + /** + * Copy-constructor that is useful for sub-classes that just want to + * copy all super-class properties without modifications. + */ + protected BeanSerializerBase(BeanSerializerBase src) { + this(src, src._props, src._filteredProps); + } + + /** + * Copy-constructor that will also rename properties with given prefix + * (if it's non-empty) + */ + protected BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper) { + this(src, rename(src._props, unwrapper), rename(src._filteredProps, unwrapper)); + } + + private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props, + NameTransformer transformer) + { + if (props == null || props.length == 0 || transformer == null || transformer == NameTransformer.NOP) { + return props; + } + final int len = props.length; + BeanPropertyWriter[] result = new BeanPropertyWriter[len]; + for (int i = 0; i < len; ++i) { + BeanPropertyWriter bpw = props[i]; + if (bpw != null) { + result[i] = bpw.rename(transformer); + } + } + return result; + } + + /* + /********************************************************** + /* Post-constriction processing: resolvable, contextual + /********************************************************** + */ + + /** + * We need to implement {@link ResolvableSerializer} to be able to + * properly handle cyclic type references. + */ + @Override + public void resolve(SerializerProvider provider) + throws JsonMappingException + { + int filteredCount = (_filteredProps == null) ? 0 : _filteredProps.length; + for (int i = 0, len = _props.length; i < len; ++i) { + BeanPropertyWriter prop = _props[i]; + // let's start with null serializer resolution actually + if (!prop.willSuppressNulls() && !prop.hasNullSerializer()) { + JsonSerializer nullSer = provider.findNullValueSerializer(prop); + if (nullSer != null) { + prop.assignNullSerializer(nullSer); + // also: remember to replace filtered property too? (see [JACKSON-364]) + if (i < filteredCount) { + BeanPropertyWriter w2 = _filteredProps[i]; + if (w2 != null) { + w2.assignNullSerializer(nullSer); + } + } + } + } + + if (prop.hasSerializer()) { + continue; + } + // [databind#124]: allow use of converters + JsonSerializer ser = findConvertingSerializer(provider, prop); + if (ser == null) { + // Was the serialization type hard-coded? If so, use it + JavaType type = prop.getSerializationType(); + + // It not, we can use declared return type if and only if declared type is final: + // if not, we don't really know the actual type until we get the instance. + if (type == null) { + // 30-Oct-2015, tatu: Not sure why this was used +// type = provider.constructType(prop.getGenericPropertyType()); + // but this looks better + type = prop.getType(); + if (!type.isFinal()) { + if (type.isContainerType() || type.containedTypeCount() > 0) { + prop.setNonTrivialBaseType(type); + } + continue; + } + } + ser = provider.findValueSerializer(type, prop); + /* 04-Feb-2010, tatu: We may have stashed type serializer for content types + * too, earlier; if so, it's time to connect the dots here: + */ + if (type.isContainerType()) { + TypeSerializer typeSer = type.getContentType().getTypeHandler(); + if (typeSer != null) { + // for now, can do this only for standard containers... + if (ser instanceof ContainerSerializer) { + // ugly casts... but necessary + @SuppressWarnings("unchecked") + JsonSerializer ser2 = (JsonSerializer)((ContainerSerializer) ser).withValueTypeSerializer(typeSer); + ser = ser2; + } + } + } + } + prop.assignSerializer(ser); + // and maybe replace filtered property too? (see [JACKSON-364]) + if (i < filteredCount) { + BeanPropertyWriter w2 = _filteredProps[i]; + if (w2 != null) { + w2.assignSerializer(ser); + } + } + } + + // also, any-getter may need to be resolved + if (_anyGetterWriter != null) { + // 23-Feb-2015, tatu: Misleading, as this actually triggers call to contextualization... + _anyGetterWriter.resolve(provider); + } + } + + /** + * Helper method that can be used to see if specified property is annotated + * to indicate use of a converter for property value (in case of container types, + * it is container type itself, not key or content type). + * + * @since 2.2 + */ + protected JsonSerializer findConvertingSerializer(SerializerProvider provider, + BeanPropertyWriter prop) + throws JsonMappingException + { + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + if (intr != null) { + AnnotatedMember m = prop.getMember(); + if (m != null) { + Object convDef = intr.findSerializationConverter(m); + if (convDef != null) { + Converter conv = provider.converterInstance(prop.getMember(), convDef); + JavaType delegateType = conv.getOutputType(provider.getTypeFactory()); + // [databind#731]: Should skip if nominally java.lang.Object + JsonSerializer ser = delegateType.isJavaLangObject() ? null + : provider.findValueSerializer(delegateType, prop); + return new StdDelegatingSerializer(conv, delegateType, ser); + } + } + } + return null; + } + + @SuppressWarnings("incomplete-switch") + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) + throws JsonMappingException + { + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + final AnnotatedMember accessor = (property == null || intr == null) + ? null : property.getMember(); + final SerializationConfig config = provider.getConfig(); + + // Let's start with one big transmutation: Enums that are annotated + // to serialize as Objects may want to revert + JsonFormat.Shape shape = null; + if (accessor != null) { + JsonFormat.Value format = intr.findFormat((Annotated) accessor); + + if (format != null) { + shape = format.getShape(); + // or, alternatively, asked to revert "back to" other representations... + if (shape != _serializationShape) { + if (_handledType.isEnum()) { + switch (shape) { + case STRING: + case NUMBER: + case NUMBER_INT: + // 12-Oct-2014, tatu: May need to introspect full annotations... but + // for now, just do class ones + BeanDescription desc = config.introspectClassAnnotations(_handledType); + JsonSerializer ser = EnumSerializer.construct(_handledType, + provider.getConfig(), desc, format); + return provider.handlePrimaryContextualization(ser, property); + } + } + } + } + } + + ObjectIdWriter oiw = _objectIdWriter; + String[] ignorals = null; + Object newFilterId = null; + + // Then we may have an override for Object Id + if (accessor != null) { + ignorals = intr.findPropertiesToIgnore(accessor, true); + ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); + if (objectIdInfo == null) { + // no ObjectId override, but maybe ObjectIdRef? + if (oiw != null) { + objectIdInfo = intr.findObjectReferenceInfo(accessor, + new ObjectIdInfo(NAME_FOR_OBJECT_REF, null, null, null)); + oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId()); + } + } else { + /* Ugh: mostly copied from BeanSerializerBase: but can't easily + * change it to be able to move to SerializerProvider (where it + * really belongs) + */ + + // 2.1: allow modifications by "id ref" annotations as well: + objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); + ObjectIdGenerator gen; + Class implClass = objectIdInfo.getGeneratorType(); + JavaType type = provider.constructType(implClass); + JavaType idType = provider.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; + // Property-based generator is trickier + if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work + String propName = objectIdInfo.getPropertyName().getSimpleName(); + BeanPropertyWriter idProp = null; + + for (int i = 0, len = _props.length ;; ++i) { + if (i == len) { + throw new IllegalArgumentException("Invalid Object Id definition for "+_handledType.getName() + +": can not find property with name '"+propName+"'"); + } + BeanPropertyWriter prop = _props[i]; + if (propName.equals(prop.getName())) { + idProp = prop; + /* Let's force it to be the first property to output + * (although it may still get rearranged etc) + */ + if (i > 0) { // note: must shuffle both regular properties and filtered + System.arraycopy(_props, 0, _props, 1, i); + _props[0] = idProp; + if (_filteredProps != null) { + BeanPropertyWriter fp = _filteredProps[i]; + System.arraycopy(_filteredProps, 0, _filteredProps, 1, i); + _filteredProps[0] = fp; + } + } + break; + } + } + idType = idProp.getType(); + gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp); + oiw = ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId()); + } else { // other types need to be simpler + gen = provider.objectIdGeneratorInstance(accessor, objectIdInfo); + oiw = ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen, + objectIdInfo.getAlwaysAsId()); + } + } + + // Or change Filter Id in use? + Object filterId = intr.findFilterId(accessor); + if (filterId != null) { + // but only consider case of adding a new filter id (no removal via annotation) + if (_propertyFilterId == null || !filterId.equals(_propertyFilterId)) { + newFilterId = filterId; + } + } + } + // either way, need to resolve serializer: + BeanSerializerBase contextual = this; + if (oiw != null) { + JsonSerializer ser = provider.findValueSerializer(oiw.idType, property); + oiw = oiw.withSerializer(ser); + if (oiw != _objectIdWriter) { + contextual = contextual.withObjectIdWriter(oiw); + } + } + // And possibly add more properties to ignore + if (ignorals != null && ignorals.length != 0) { + contextual = contextual.withIgnorals(ignorals); + } + if (newFilterId != null) { + contextual = contextual.withFilterId(newFilterId); + } + if (shape == null) { + shape = _serializationShape; + } + if (shape == JsonFormat.Shape.ARRAY) { + return contextual.asArraySerializer(); + } + return contextual; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public Iterator properties() { + return Arrays.asList(_props).iterator(); + } + + /* + /********************************************************** + /* Partial JsonSerializer implementation + /********************************************************** + */ + + @Override + public boolean usesObjectId() { + return (_objectIdWriter != null); + } + + // Main serialization method left unimplemented + @Override + public abstract void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException; + + // Type-info-augmented case implemented as it does not usually differ between impls + @Override + public void serializeWithType(Object bean, JsonGenerator gen, + SerializerProvider provider, TypeSerializer typeSer) + throws IOException + { + if (_objectIdWriter != null) { + gen.setCurrentValue(bean); // [databind#631] + _serializeWithObjectId(bean, gen, provider, typeSer); + return; + } + + String typeStr = (_typeId == null) ? null : _customTypeId(bean); + if (typeStr == null) { + typeSer.writeTypePrefixForObject(bean, gen); + } else { + typeSer.writeCustomTypePrefixForObject(bean, gen, typeStr); + } + gen.setCurrentValue(bean); // [databind#631] + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + if (typeStr == null) { + typeSer.writeTypeSuffixForObject(bean, gen); + } else { + typeSer.writeCustomTypeSuffixForObject(bean, gen, typeStr); + } + } + + protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, + boolean startEndObject) throws IOException + { + final ObjectIdWriter w = _objectIdWriter; + WritableObjectId objectId = provider.findObjectId(bean, w.generator); + // If possible, write as id already + if (objectId.writeAsId(gen, provider, w)) { + return; + } + // If not, need to inject the id: + Object id = objectId.generateId(bean); + if (w.alwaysAsId) { + w.serializer.serialize(id, gen, provider); + return; + } + if (startEndObject) { + gen.writeStartObject(); + } + objectId.writeAsField(gen, provider, w); + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + if (startEndObject) { + gen.writeEndObject(); + } + } + + protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + final ObjectIdWriter w = _objectIdWriter; + WritableObjectId objectId = provider.findObjectId(bean, w.generator); + // If possible, write as id already + if (objectId.writeAsId(gen, provider, w)) { + return; + } + // If not, need to inject the id: + Object id = objectId.generateId(bean); + if (w.alwaysAsId) { + w.serializer.serialize(id, gen, provider); + return; + } + + _serializeObjectId(bean, gen, provider, typeSer, objectId); + } + + protected void _serializeObjectId(Object bean, JsonGenerator gen,SerializerProvider provider, + TypeSerializer typeSer, WritableObjectId objectId) throws IOException + { + final ObjectIdWriter w = _objectIdWriter; + String typeStr = (_typeId == null) ? null :_customTypeId(bean); + if (typeStr == null) { + typeSer.writeTypePrefixForObject(bean, gen); + } else { + typeSer.writeCustomTypePrefixForObject(bean, gen, typeStr); + } + objectId.writeAsField(gen, provider, w); + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + if (typeStr == null) { + typeSer.writeTypeSuffixForObject(bean, gen); + } else { + typeSer.writeCustomTypeSuffixForObject(bean, gen, typeStr); + } + } + + protected final String _customTypeId(Object bean) + { + final Object typeId = _typeId.getValue(bean); + if (typeId == null) { + return ""; + } + return (typeId instanceof String) ? (String) typeId : typeId.toString(); + } + + /* + /********************************************************** + /* Field serialization methods + /********************************************************** + */ + + protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final BeanPropertyWriter[] props; + if (_filteredProps != null && provider.getActiveView() != null) { + props = _filteredProps; + } else { + props = _props; + } + int i = 0; + try { + for (final int len = props.length; i < len; ++i) { + BeanPropertyWriter prop = props[i]; + if (prop != null) { // can have nulls in filtered list + prop.serializeAsField(bean, gen, provider); + } + } + if (_anyGetterWriter != null) { + _anyGetterWriter.getAndSerialize(bean, gen, provider); + } + } catch (Exception e) { + String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + wrapAndThrow(provider, e, bean, name); + } catch (StackOverflowError e) { + /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not + * have many stack frames to spare... just one or two; can't + * make many calls. + */ + // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly: + //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); + JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); + + String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + mapE.prependPath(new JsonMappingException.Reference(bean, name)); + throw mapE; + } + } + + /** + * Alternative serialization method that gets called when there is a + * {@link PropertyFilter} that needs to be called to determine + * which properties are to be serialized (and possibly how) + */ + protected void serializeFieldsFiltered(Object bean, JsonGenerator gen, + SerializerProvider provider) + throws IOException, JsonGenerationException + { + /* note: almost verbatim copy of "serializeFields"; copied (instead of merged) + * so that old method need not add check for existence of filter. + */ + final BeanPropertyWriter[] props; + if (_filteredProps != null && provider.getActiveView() != null) { + props = _filteredProps; + } else { + props = _props; + } + final PropertyFilter filter = findPropertyFilter(provider, _propertyFilterId, bean); + // better also allow missing filter actually.. + if (filter == null) { + serializeFields(bean, gen, provider); + return; + } + int i = 0; + try { + for (final int len = props.length; i < len; ++i) { + BeanPropertyWriter prop = props[i]; + if (prop != null) { // can have nulls in filtered list + filter.serializeAsField(bean, gen, provider, prop); + } + } + if (_anyGetterWriter != null) { + _anyGetterWriter.getAndFilter(bean, gen, provider, filter); + } + } catch (Exception e) { + String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + wrapAndThrow(provider, e, bean, name); + } catch (StackOverflowError e) { + // Minimize call depth since we are close to fail: + //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); + JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); + String name = (i == props.length) ? "[anySetter]" : props[i].getName(); + mapE.prependPath(new JsonMappingException.Reference(bean, name)); + throw mapE; + } + } + + @Deprecated + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = createSchemaNode("object", true); + // [JACKSON-813]: Add optional JSON Schema id attribute, if found + // NOTE: not optimal, does NOT go through AnnotationIntrospector etc: + JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class); + if (ann != null) { + String id = ann.id(); + if (id != null && id.length() > 0) { + o.put("id", id); + } + } + + //todo: should the classname go in the title? + //o.put("title", _className); + ObjectNode propertiesNode = o.objectNode(); + final PropertyFilter filter; + if (_propertyFilterId != null) { + filter = findPropertyFilter(provider, _propertyFilterId, null); + } else { + filter = null; + } + + for (int i = 0; i < _props.length; i++) { + BeanPropertyWriter prop = _props[i]; + if (filter == null) { + prop.depositSchemaProperty(propertiesNode, provider); + } else { + filter.depositSchemaProperty(prop, propertiesNode, provider); + } + + } + o.set("properties", propertiesNode); + return o; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + //deposit your output format + if (visitor == null) { + return; + } + JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint); + if (objectVisitor == null) { + return; + } + final SerializerProvider provider = visitor.getProvider(); + if (_propertyFilterId != null) { + PropertyFilter filter = findPropertyFilter(visitor.getProvider(), + _propertyFilterId, null); + for (int i = 0, end = _props.length; i < end; ++i) { + filter.depositSchemaProperty(_props[i], objectVisitor, provider); + } + } else { + Class view = ((_filteredProps == null) || (provider == null)) + ? null : provider.getActiveView(); + final BeanPropertyWriter[] props; + if (view != null) { + props = _filteredProps; + } else { + props = _props; + } + + for (int i = 0, end = props.length; i < end; ++i) { + BeanPropertyWriter prop = props[i]; + if (prop != null) { // may be filtered out unconditionally + prop.depositSchemaProperty(objectVisitor, provider); + } + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,56 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * Serializer used for primitive boolean, as well as java.util.Boolean + * wrapper type. + *

+ * Since this is one of "native" types, no type information is ever + * included on serialization (unlike for most scalar types as of 1.5) + */ +@JacksonStdImpl +public final class BooleanSerializer + extends NonTypedScalarSerializerBase +{ + private static final long serialVersionUID = 1L; + + /** + * Whether type serialized is primitive (boolean) or wrapper + * (java.lang.Boolean); if true, former, if false, latter. + */ + protected final boolean _forPrimitive; + + public BooleanSerializer(boolean forPrimitive) { + super(Boolean.class); + _forPrimitive = forPrimitive; + } + + @Override + public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeBoolean(value.booleanValue()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("boolean", !_forPrimitive); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + if (visitor != null) { + visitor.expectBooleanFormat(typeHint); + } + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ByteArraySerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,86 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Unlike other integral number array serializers, we do not just print out byte values + * as numbers. Instead, we assume that it would make more sense to output content + * as base64 encoded bytes (using default base64 encoding). + *

+ * NOTE: since it is NOT serialized as an array, can not use AsArraySerializer as base + *

+ * NOTE: since 2.6, has been a main-level class; earlier was embedded in + * {@link StdArraySerializers}. + */ +@JacksonStdImpl +public class ByteArraySerializer extends StdSerializer +{ + private static final long serialVersionUID = 1L; + + public ByteArraySerializer() { + super(byte[].class); + } + + @Override + public boolean isEmpty(SerializerProvider prov, byte[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public void serialize(byte[] value, JsonGenerator g, SerializerProvider provider) + throws IOException + { + g.writeBinary(provider.getConfig().getBase64Variant(), + value, 0, value.length); + } + + @Override + public void serializeWithType(byte[] value, JsonGenerator g, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException + { + typeSer.writeTypePrefixForScalar(value, g); + g.writeBinary(provider.getConfig().getBase64Variant(), + value, 0, value.length); + typeSer.writeTypeSuffixForScalar(value, g); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = createSchemaNode("array", true); + ObjectNode itemSchema = createSchemaNode("byte"); //binary values written as strings? + return o.set("items", itemSchema); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + // 14-Mar-2016, tatu: while logically (and within JVM) binary, gets encoded as Base64 String, + // let's try to indicate it is array of Bytes... difficult, thanks to JSON Schema's + // lackluster listing of types + // + // TODO: for 2.8, make work either as String/base64, or array of numbers, + // with a qualifier that can be used to determine it's byte[] + if (visitor != null) { + JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); + if (v2 != null) { + v2.itemsFormat(JsonFormatTypes.INTEGER); + } + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ByteBufferSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,33 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.*; +import java.nio.ByteBuffer; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream; + +@SuppressWarnings("serial") +public class ByteBufferSerializer extends StdScalarSerializer +{ + public ByteBufferSerializer() { super(ByteBuffer.class); } + + @Override + public void serialize(ByteBuffer bbuf, JsonGenerator gen, SerializerProvider provider) throws IOException + { + // first, simple case when wrapping an array... + if (bbuf.hasArray()) { + gen.writeBinary(bbuf.array(), 0, bbuf.limit()); + return; + } + // the other case is more complicated however. Best to handle with InputStream wrapper. + // But should we rewind it; and/or make a copy? + ByteBuffer copy = bbuf.asReadOnlyBuffer(); + if (copy.position() > 0) { + copy.rewind(); + } + InputStream in = new ByteBufferBackedInputStream(copy); + gen.writeBinary(in, copy.remaining()); + in.close(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,55 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.text.DateFormat; +import java.util.Calendar; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; + +/** + * Standard serializer for {@link java.util.Calendar}. + * As with other time/date types, is configurable to produce timestamps + * (standard Java 64-bit timestamp) or textual formats (usually ISO-8601). + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class CalendarSerializer + extends DateTimeSerializerBase +{ + public static final CalendarSerializer instance = new CalendarSerializer(); + + public CalendarSerializer() { this(null, null); } + + public CalendarSerializer(Boolean useTimestamp, DateFormat customFormat) { + super(Calendar.class, useTimestamp, customFormat); + } + + @Override + public CalendarSerializer withFormat(Boolean timestamp, DateFormat customFormat) { + return new CalendarSerializer(timestamp, customFormat); + } + + @Override + protected long _timestamp(Calendar value) { + return (value == null) ? 0L : value.getTimeInMillis(); + } + + @Override + public void serialize(Calendar value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + if (_asTimestamp(provider)) { + jgen.writeNumber(_timestamp(value)); + } else if (_customFormat != null) { + // 21-Feb-2011, tatu: not optimal, but better than alternatives: + synchronized (_customFormat) { + // _customformat cannot parse Calendar, so Date should be passed + jgen.writeString(_customFormat.format(value.getTime())); + } + } else { + provider.defaultSerializeDateValue(value.getTime(), jgen); + } + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ClassSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ClassSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ClassSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,42 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * Also: default bean access will not do much good with Class.class. But + * we can just serialize the class name and that should be enough. + */ +@SuppressWarnings("serial") +public class ClassSerializer + extends StdScalarSerializer> +{ + public ClassSerializer() { super(Class.class, false); } + + @Override + public void serialize(Class value, JsonGenerator g, SerializerProvider provider) throws IOException + { + g.writeString(value.getName()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitStringFormat(visitor, typeHint); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,188 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; + +/** + * Fallback serializer for cases where Collection is not known to be + * of type for which more specializer serializer exists (such as + * index-accessible List). + * If so, we will just construct an {@link java.util.Iterator} + * to iterate over elements. + */ +public class CollectionSerializer + extends AsArraySerializerBase> +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * @since 2.6 + */ + public CollectionSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts, + JsonSerializer valueSerializer) { + super(Collection.class, elemType, staticTyping, vts, valueSerializer); + } + + /** + * @deprecated since 2.6 + */ + @Deprecated // since 2.6 + public CollectionSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts, + BeanProperty property, JsonSerializer valueSerializer) { + // note: assumption is 'property' is always passed as null + this(elemType, staticTyping, vts, valueSerializer); + } + + public CollectionSerializer(CollectionSerializer src, + BeanProperty property, TypeSerializer vts, JsonSerializer valueSerializer, + Boolean unwrapSingle) { + super(src, property, vts, valueSerializer, unwrapSingle); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new CollectionSerializer(this, _property, vts, _elementSerializer, _unwrapSingle); + } + + @Override + public CollectionSerializer withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) { + return new CollectionSerializer(this, property, vts, elementSerializer, unwrapSingle); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public boolean isEmpty(SerializerProvider prov, Collection value) { + return (value == null) || value.isEmpty(); + } + + @Override + public boolean hasSingleElement(Collection value) { + Iterator it = value.iterator(); + if (!it.hasNext()) { + return false; + } + it.next(); + return !it.hasNext(); + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public final void serialize(Collection value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + final int len = value.size(); + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, jgen, provider); + return; + } + } + jgen.writeStartArray(len); + serializeContents(value, jgen, provider); + jgen.writeEndArray(); + } + + @Override + public void serializeContents(Collection value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + if (_elementSerializer != null) { + serializeContentsUsing(value, jgen, provider, _elementSerializer); + return; + } + Iterator it = value.iterator(); + if (!it.hasNext()) { + return; + } + PropertySerializerMap serializers = _dynamicSerializers; + final TypeSerializer typeSer = _valueTypeSerializer; + + int i = 0; + try { + do { + Object elem = it.next(); + if (elem == null) { + provider.defaultSerializeNull(jgen); + } else { + Class cc = elem.getClass(); + JsonSerializer serializer = serializers.serializerFor(cc); + if (serializer == null) { + if (_elementType.hasGenericTypes()) { + serializer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_elementType, cc), provider); + } else { + serializer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicSerializers; + } + if (typeSer == null) { + serializer.serialize(elem, jgen, provider); + } else { + serializer.serializeWithType(elem, jgen, provider, typeSer); + } + } + ++i; + } while (it.hasNext()); + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } + + public void serializeContentsUsing(Collection value, JsonGenerator jgen, SerializerProvider provider, + JsonSerializer ser) + throws IOException, JsonGenerationException + { + Iterator it = value.iterator(); + if (it.hasNext()) { + TypeSerializer typeSer = _valueTypeSerializer; + int i = 0; + do { + Object elem = it.next(); + try { + if (elem == null) { + provider.defaultSerializeNull(jgen); + } else { + if (typeSer == null) { + ser.serialize(elem, jgen, provider); + } else { + ser.serializeWithType(elem, jgen, provider, typeSer); + } + } + ++i; + } catch (Exception e) { + wrapAndThrow(provider, e, value, i); + } + } while (it.hasNext()); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/DateSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/DateSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/DateSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,58 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.text.DateFormat; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; + +/** + * For efficiency, we will serialize Dates as longs, instead of + * potentially more readable Strings. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class DateSerializer + extends DateTimeSerializerBase +{ + /** + * Default instance that is used when no contextual configuration + * is needed. + */ + public static final DateSerializer instance = new DateSerializer(); + + public DateSerializer() { + this(null, null); + } + + public DateSerializer(Boolean useTimestamp, DateFormat customFormat) { + super(Date.class, useTimestamp, customFormat); + } + + @Override + public DateSerializer withFormat(Boolean timestamp, DateFormat customFormat) { + return new DateSerializer(timestamp, customFormat); + } + + @Override + protected long _timestamp(Date value) { + return (value == null) ? 0L : value.getTime(); + } + + @Override + public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + if (_asTimestamp(provider)) { + gen.writeNumber(_timestamp(value)); + } else if (_customFormat != null) { + // 21-Feb-2011, tatu: not optimal, but better than alternatives: + synchronized (_customFormat) { + gen.writeString(_customFormat.format(value)); + } + } else { + provider.defaultSerializeDateValue(value, gen); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,158 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsonFormatVisitors.*; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.util.StdDateFormat; + +@SuppressWarnings("serial") +public abstract class DateTimeSerializerBase + extends StdScalarSerializer + implements ContextualSerializer +{ + /** + * Flag that indicates that serialization must be done as the + * Java timestamp, regardless of other settings. + */ + protected final Boolean _useTimestamp; + + /** + * Specific format to use, if not default format: non null value + * also indicates that serialization is to be done as JSON String, + * not numeric timestamp, unless {@link #_useTimestamp} is true. + */ + protected final DateFormat _customFormat; + + protected DateTimeSerializerBase(Class type, + Boolean useTimestamp, DateFormat customFormat) + { + super(type); + _useTimestamp = useTimestamp; + _customFormat = customFormat; + } + + public abstract DateTimeSerializerBase withFormat(Boolean timestamp, DateFormat customFormat); + + @Override + public JsonSerializer createContextual(SerializerProvider serializers, + BeanProperty property) throws JsonMappingException + { + if (property != null) { + JsonFormat.Value format = serializers.getAnnotationIntrospector().findFormat((Annotated)property.getMember()); + if (format != null) { + + // Simple case first: serialize as numeric timestamp? + JsonFormat.Shape shape = format.getShape(); + if (shape.isNumeric()) { + return withFormat(Boolean.TRUE, null); + } + + if ((shape == JsonFormat.Shape.STRING) || format.hasPattern() + || format.hasLocale() || format.hasTimeZone()) { + TimeZone tz = format.getTimeZone(); + final String pattern = format.hasPattern() + ? format.getPattern() + : StdDateFormat.DATE_FORMAT_STR_ISO8601; + final Locale loc = format.hasLocale() + ? format.getLocale() + : serializers.getLocale(); + SimpleDateFormat df = new SimpleDateFormat(pattern, loc); + if (tz == null) { + tz = serializers.getTimeZone(); + } + df.setTimeZone(tz); + return withFormat(Boolean.FALSE, df); + } + } + } + return this; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Deprecated + @Override + public boolean isEmpty(T value) { + // let's assume "null date" (timestamp 0) qualifies for empty + return (value == null) || (_timestamp(value) == 0L); + } + + @Override + public boolean isEmpty(SerializerProvider serializers, T value) { + // let's assume "null date" (timestamp 0) qualifies for empty + return (value == null) || (_timestamp(value) == 0L); + } + + protected abstract long _timestamp(T value); + + @Override + public JsonNode getSchema(SerializerProvider serializers, Type typeHint) { + //todo: (ryan) add a format for the date in the schema? + return createSchemaNode(_asTimestamp(serializers) ? "number" : "string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + _acceptJsonFormatVisitor(visitor, typeHint, _asTimestamp(visitor.getProvider())); + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers) + throws IOException; + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected boolean _asTimestamp(SerializerProvider serializers) + { + if (_useTimestamp != null) { + return _useTimestamp.booleanValue(); + } + if (_customFormat == null) { + if (serializers != null) { + return serializers.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + // 12-Jun-2014, tatu: Is it legal not to have provider? Was NPE:ing earlier so leave a check + throw new IllegalArgumentException("Null SerializerProvider passed for "+handledType().getName()); + } + return false; + } + + protected void _acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint, + boolean asNumber) throws JsonMappingException + { + if (asNumber) { + visitIntFormat(visitor, typeHint, + JsonParser.NumberType.LONG, JsonValueFormat.UTC_MILLISEC); + } else { + visitStringFormat(visitor, typeHint, JsonValueFormat.DATE_TIME); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,232 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.util.EnumValues; + +/** + * Standard serializer used for {@link java.lang.Enum} types. + *

+ * Based on {@link StdScalarSerializer} since the JSON value is + * scalar (String). + * + * @author tatu + */ +@JacksonStdImpl +public class EnumSerializer + extends StdScalarSerializer> + implements ContextualSerializer +{ + private static final long serialVersionUID = 1L; + + /** + * This map contains pre-resolved values (since there are ways + * to customize actual String constants to use) to use as + * serializations. + */ + protected final EnumValues _values; + + /** + * Flag that is set if we statically know serialization choice + * between index and textual format (null if it needs to be dynamically + * checked). + * + * @since 2.1 + */ + protected final Boolean _serializeAsIndex; + + /* + /********************************************************** + /* Construction, initialization + /********************************************************** + */ + + /** + * @deprecated Since 2.1 + */ + @Deprecated + public EnumSerializer(EnumValues v) { + this(v, null); + } + + public EnumSerializer(EnumValues v, Boolean serializeAsIndex) + { + super(v.getEnumClass(), false); + _values = v; + _serializeAsIndex = serializeAsIndex; + } + + /** + * Factory method used by {@link com.fasterxml.jackson.databind.ser.BasicSerializerFactory} + * for constructing serializer instance of Enum types. + * + * @since 2.1 + */ + @SuppressWarnings("unchecked") + public static EnumSerializer construct(Class enumClass, SerializationConfig config, + BeanDescription beanDesc, JsonFormat.Value format) + { + /* 08-Apr-2015, tatu: As per [databind#749], we can not statically determine + * between name() and toString(), need to construct `EnumValues` with names, + * handle toString() case dynamically (for example) + */ + EnumValues v = EnumValues.constructFromName(config, (Class>) enumClass); + Boolean serializeAsIndex = _isShapeWrittenUsingIndex(enumClass, format, true); + return new EnumSerializer(v, serializeAsIndex); + } + + /** + * To support some level of per-property configuration, we will need + * to make things contextual. We are limited to "textual vs index" + * choice here, however. + */ + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException + { + if (property != null) { + JsonFormat.Value format = prov.getAnnotationIntrospector().findFormat((Annotated) property.getMember()); + if (format != null) { + Boolean serializeAsIndex = _isShapeWrittenUsingIndex(property.getType().getRawClass(), format, false); + if (serializeAsIndex != _serializeAsIndex) { + return new EnumSerializer(_values, serializeAsIndex); + } + } + } + return this; + } + + /* + /********************************************************** + /* Extended API for Jackson databind core + /********************************************************** + */ + + public EnumValues getEnumValues() { return _values; } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public final void serialize(Enum en, JsonGenerator gen, SerializerProvider serializers) + throws IOException + { + // [JACKSON-684]: serialize as index? + if (_serializeAsIndex(serializers)) { + gen.writeNumber(en.ordinal()); + return; + } + // [databind#749]: or via toString()? + if (serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { + gen.writeString(en.toString()); + return; + } + gen.writeString(_values.serializedValueFor(en)); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + // [JACKSON-684]: serialize as index? + if (_serializeAsIndex(provider)) { + return createSchemaNode("integer", true); + } + ObjectNode objectNode = createSchemaNode("string", true); + if (typeHint != null) { + JavaType type = provider.constructType(typeHint); + if (type.isEnumType()) { + ArrayNode enumNode = objectNode.putArray("enum"); + for (SerializableString value : _values.values()) { + enumNode.add(value.getValue()); + } + } + } + return objectNode; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + SerializerProvider serializers = visitor.getProvider(); + if (_serializeAsIndex(serializers)) { + visitIntFormat(visitor, typeHint, JsonParser.NumberType.INT); + return; + } + JsonStringFormatVisitor stringVisitor = visitor.expectStringFormat(typeHint); + if (stringVisitor != null) { + Set enums = new LinkedHashSet(); + + // Use toString()? + if ((serializers != null) && + serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { + for (Enum e : _values.enums()) { + enums.add(e.toString()); + } + } else { + // No, serialize using name() or explicit overrides + for (SerializableString value : _values.values()) { + enums.add(value.getValue()); + } + } + stringVisitor.enumTypes(enums); + } + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + protected final boolean _serializeAsIndex(SerializerProvider serializers) + { + if (_serializeAsIndex != null) { + return _serializeAsIndex.booleanValue(); + } + return serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX); + } + + /** + * Helper method called to check whether + */ + protected static Boolean _isShapeWrittenUsingIndex(Class enumClass, + JsonFormat.Value format, boolean fromClass) + { + JsonFormat.Shape shape = (format == null) ? null : format.getShape(); + if (shape == null) { + return null; + } + if (shape == Shape.ANY || shape == Shape.SCALAR) { // i.e. "default", check dynamically + return null; + } + if (shape == Shape.STRING) { + return Boolean.FALSE; + } + // 01-Oct-2014, tatu: For convenience, consider "as-array" to also mean 'yes, use index') + if (shape.isNumeric() || (shape == Shape.ARRAY)) { + return Boolean.TRUE; + } + throw new IllegalArgumentException("Unsupported serialization shape ("+shape+") for Enum "+enumClass.getName() + +", not supported as " + + (fromClass? "class" : "property") + +" annotation"); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,96 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +@SuppressWarnings("serial") +public class EnumSetSerializer + extends AsArraySerializerBase>> +{ + /** + * @since 2.6 + */ + public EnumSetSerializer(JavaType elemType) { + super(EnumSet.class, elemType, true, null, null); + } + + /** + * @deprecated since 2.6 + */ + @Deprecated // since 2.6 + public EnumSetSerializer(JavaType elemType, BeanProperty property) { + this(elemType); + } + + public EnumSetSerializer(EnumSetSerializer src, + BeanProperty property, TypeSerializer vts, JsonSerializer valueSerializer, + Boolean unwrapSingle) { + super(src, property, vts, valueSerializer, unwrapSingle); + } + + @Override + public EnumSetSerializer _withValueTypeSerializer(TypeSerializer vts) { + // no typing for enums (always "hard" type) + return this; + } + + @Override + public EnumSetSerializer withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) { + return new EnumSetSerializer(this, property, vts, elementSerializer, unwrapSingle); + } + + @Override + public boolean isEmpty(SerializerProvider prov, EnumSet> value) { + return (value == null) || value.isEmpty(); + } + + @Override + public boolean hasSingleElement(EnumSet> value) { + return value.size() == 1; + } + + @Override + public final void serialize(EnumSet> value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + final int len = value.size(); + if (len == 1) { + if (((_unwrapSingle == null) + && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(EnumSet> value, JsonGenerator gen, + SerializerProvider provider) + throws IOException + { + JsonSerializer enumSer = _elementSerializer; + /* Need to dynamically find instance serializer; unfortunately + * that seems to be the only way to figure out type (no accessors + * to the enum class that set knows) + */ + for (Enum en : value) { + if (enumSer == null) { + /* 12-Jan-2010, tatu: Since enums can not be polymorphic, let's + * not bother with typed serializer variant here + */ + enumSer = provider.findValueSerializer(en.getDeclaringClass(), _property); + } + enumSer.serialize(en, gen, provider); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/FileSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/FileSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/FileSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,40 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * For now, File objects get serialized by just outputting + * absolute (but not canonical) name as String value + */ +@SuppressWarnings("serial") +public class FileSerializer + extends StdScalarSerializer +{ + public FileSerializer() { super(File.class); } + + @Override + public void serialize(File value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeString(value.getAbsolutePath()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitStringFormat(visitor, typeHint); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,44 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.net.InetAddress; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Simple serializer for {@link java.net.InetAddress}. Main complexity is + * with registration, since same serializer is to be used for sub-classes. + */ +@SuppressWarnings("serial") +public class InetAddressSerializer + extends StdScalarSerializer +{ + public InetAddressSerializer() { super(InetAddress.class); } + + @Override + public void serialize(InetAddress value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + // Ok: get textual description; choose "more specific" part + String str = value.toString().trim(); + int ix = str.indexOf('/'); + if (ix >= 0) { + if (ix == 0) { // missing host name; use address + str = str.substring(1); + } else { // otherwise use name + str = str.substring(0, ix); + } + } + jgen.writeString(str); + } + + @Override + public void serializeWithType(InetAddress value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException + { + // Better ensure we don't use specific sub-classes... + typeSer.writeTypePrefixForScalar(value, jgen, InetAddress.class); + serialize(value, jgen, provider); + typeSer.writeTypeSuffixForScalar(value, jgen); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/InetSocketAddressSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,50 @@ +package com.fasterxml.jackson.databind.ser.std; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +import java.io.IOException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; + +/** + * Simple serializer for {@link InetSocketAddress}. + */ +@SuppressWarnings("serial") +public class InetSocketAddressSerializer + extends StdScalarSerializer +{ + public InetSocketAddressSerializer() { super(InetSocketAddress.class); } + + @Override + public void serialize(InetSocketAddress value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + InetAddress addr = value.getAddress(); + String str = addr == null ? value.getHostName() : addr.toString().trim(); + int ix = str.indexOf('/'); + if (ix >= 0) { + if (ix == 0) { // missing host name; use address + str = addr instanceof Inet6Address + ? "[" + str.substring(1) + "]" // bracket IPv6 addresses with + : str.substring(1); + + } else { // otherwise use name + str = str.substring(0, ix); + } + } + + jgen.writeString(str + ":" + value.getPort()); + } + + @Override + public void serializeWithType(InetSocketAddress value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException + { + // Better ensure we don't use specific sub-classes... + typeSer.writeTypePrefixForScalar(value, jgen, InetSocketAddress.class); + serialize(value, jgen, provider); + typeSer.writeTypeSuffixForScalar(value, jgen); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,114 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; + +@JacksonStdImpl +@SuppressWarnings("serial") +public class IterableSerializer + extends AsArraySerializerBase> +{ + public IterableSerializer(JavaType elemType, boolean staticTyping, + TypeSerializer vts) { + super(Iterable.class, elemType, staticTyping, vts, null); + } + + public IterableSerializer(IterableSerializer src, BeanProperty property, + TypeSerializer vts, JsonSerializer valueSerializer, + Boolean unwrapSingle) { + super(src, property, vts, valueSerializer, unwrapSingle); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new IterableSerializer(this, _property, vts, _elementSerializer, _unwrapSingle); + } + + @Override + public IterableSerializer withResolved(BeanProperty property, + TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) { + return new IterableSerializer(this, property, vts, elementSerializer, unwrapSingle); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Iterable value) { + // Not really good way to implement this, but has to do for now: + return (value == null) || !value.iterator().hasNext(); + } + + @Override + public boolean hasSingleElement(Iterable value) { + // we can do it actually (fixed in 2.3.1) + if (value != null) { + Iterator it = value.iterator(); + if (it.hasNext()) { + it.next(); + if (!it.hasNext()) { + return true; + } + } + } + return false; + } + + @Override + public final void serialize(Iterable value, JsonGenerator gen, + SerializerProvider provider)throws IOException + { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + if (hasSingleElement(value)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(Iterable value, JsonGenerator jgen, + SerializerProvider provider) throws IOException + { + Iterator it = value.iterator(); + if (it.hasNext()) { + final TypeSerializer typeSer = _valueTypeSerializer; + JsonSerializer prevSerializer = null; + Class prevClass = null; + + do { + Object elem = it.next(); + if (elem == null) { + provider.defaultSerializeNull(jgen); + continue; + } + JsonSerializer currSerializer = _elementSerializer; + if (currSerializer == null) { + // Minor optimization to avoid most lookups: + Class cc = elem.getClass(); + if (cc == prevClass) { + currSerializer = prevSerializer; + } else { + currSerializer = provider.findValueSerializer(cc, _property); + prevSerializer = currSerializer; + prevClass = cc; + } + } + if (typeSer == null) { + currSerializer.serialize(elem, jgen, provider); + } else { + currSerializer.serializeWithType(elem, jgen, provider, typeSer); + } + } while (it.hasNext()); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,360 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.LinkedHashSet; +import java.util.Set; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.BeanSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +/** + * Serializer class that can serialize Object that have a + * {@link com.fasterxml.jackson.annotation.JsonValue} annotation to + * indicate that serialization should be done by calling the method + * annotated, and serializing result it returns. + *

+ * Implementation note: we will post-process resulting serializer + * (much like what is done with {@link BeanSerializer}) + * to figure out actual serializers for final types. + * This must be done from {@link #createContextual} method, and NOT from constructor; + * otherwise we could end up with an infinite loop. + */ +@SuppressWarnings("serial") +@JacksonStdImpl +public class JsonValueSerializer + extends StdSerializer + implements ContextualSerializer, JsonFormatVisitable, SchemaAware +{ + protected final Method _accessorMethod; + + protected final JsonSerializer _valueSerializer; + + protected final BeanProperty _property; + + /** + * This is a flag that is set in rare (?) cases where this serializer + * is used for "natural" types (boolean, int, String, double); and where + * we actually must force type information wrapping, even though + * one would not normally be added. + */ + protected final boolean _forceTypeInformation; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + // Added in 2.7.4 for forward-compatibility reasons; will be used by default in 2.8.0 + public JsonValueSerializer(AnnotatedMethod valueMethod, JsonSerializer ser) { + this(valueMethod.getAnnotated(), ser); + } + + /** + * @param ser Explicit serializer to use, if caller knows it (which + * occurs if and only if the "value method" was annotated with + * {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}), otherwise + * null + */ + @SuppressWarnings("unchecked") + public JsonValueSerializer(Method valueMethod, JsonSerializer ser) + { + super(valueMethod.getReturnType(), false); + _accessorMethod = valueMethod; + _valueSerializer = (JsonSerializer) ser; + _property = null; + _forceTypeInformation = true; // gets reconsidered when we are contextualized + } + + @SuppressWarnings("unchecked") + public JsonValueSerializer(JsonValueSerializer src, BeanProperty property, + JsonSerializer ser, boolean forceTypeInfo) + { + super(_notNullClass(src.handledType())); + _accessorMethod = src._accessorMethod; + _valueSerializer = (JsonSerializer) ser; + _property = property; + _forceTypeInformation = forceTypeInfo; + } + + @SuppressWarnings("unchecked") + private final static Class _notNullClass(Class cls) { + return (cls == null) ? Object.class : (Class) cls; + } + + public JsonValueSerializer withResolved(BeanProperty property, + JsonSerializer ser, boolean forceTypeInfo) + { + if (_property == property && _valueSerializer == ser + && forceTypeInfo == _forceTypeInformation) { + return this; + } + return new JsonValueSerializer(this, property, ser, forceTypeInfo); + } + + /* + /********************************************************** + /* Post-processing + /********************************************************** + */ + + /** + * We can try to find the actual serializer for value, if we can + * statically figure out what the result type must be. + */ + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) + throws JsonMappingException + { + JsonSerializer ser = _valueSerializer; + if (ser == null) { + /* Can only assign serializer statically if the declared type is final: + * if not, we don't really know the actual type until we get the instance. + */ + // 10-Mar-2010, tatu: Except if static typing is to be used + if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) + || Modifier.isFinal(_accessorMethod.getReturnType().getModifiers())) { + JavaType t = provider.constructType(_accessorMethod.getGenericReturnType()); + // false -> no need to cache + /* 10-Mar-2010, tatu: Ideally we would actually separate out type + * serializer from value serializer; but, alas, there's no access + * to serializer factory at this point... + */ + // 05-Sep-2013, tatu: I _think_ this can be considered a primary property... + ser = provider.findPrimaryPropertySerializer(t, property); + /* 09-Dec-2010, tatu: Turns out we must add special handling for + * cases where "native" (aka "natural") type is being serialized, + * using standard serializer + */ + boolean forceTypeInformation = isNaturalTypeWithStdHandling(t.getRawClass(), ser); + return withResolved(property, ser, forceTypeInformation); + } + } else { + // 05-Sep-2013, tatu: I _think_ this can be considered a primary property... + ser = provider.handlePrimaryContextualization(ser, property); + return withResolved(property, ser, _forceTypeInformation); + } + return this; + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov) throws IOException + { + try { + Object value = _accessorMethod.invoke(bean); + if (value == null) { + prov.defaultSerializeNull(jgen); + return; + } + JsonSerializer ser = _valueSerializer; + if (ser == null) { + Class c = value.getClass(); + /* 10-Mar-2010, tatu: Ideally we would actually separate out type + * serializer from value serializer; but, alas, there's no access + * to serializer factory at this point... + */ + // let's cache it, may be needed soon again + ser = prov.findTypedValueSerializer(c, true, _property); + } + ser.serialize(value, jgen, prov); + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + Throwable t = e; + // Need to unwrap this specific type, to see infinite recursion... + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors shouldn't be wrapped (and often can't, as well) + if (t instanceof Error) { + throw (Error) t; + } + // let's try to indicate the path best we can... + throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()"); + } + } + + @Override + public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer0) throws IOException + { + // Regardless of other parts, first need to find value to serialize: + Object value = null; + try { + value = _accessorMethod.invoke(bean); + // and if we got null, can also just write it directly + if (value == null) { + provider.defaultSerializeNull(jgen); + return; + } + JsonSerializer ser = _valueSerializer; + if (ser == null) { // already got a serializer? fabulous, that be easy... +// ser = provider.findTypedValueSerializer(value.getClass(), true, _property); + ser = provider.findValueSerializer(value.getClass(), _property); + } else { + /* 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do + * this (note: type is for the wrapper type, not enclosed value!) + */ + if (_forceTypeInformation) { + typeSer0.writeTypePrefixForScalar(bean, jgen); + ser.serialize(value, jgen, provider); + typeSer0.writeTypeSuffixForScalar(bean, jgen); + return; + } + } + /* 13-Feb-2013, tatu: Turns out that work-around should NOT be required + * at all; it would not lead to correct behavior (as per #167). + */ + // and then redirect type id lookups +// TypeSerializer typeSer = new TypeSerializerWrapper(typeSer0, bean); + ser.serializeWithType(value, jgen, provider, typeSer0); + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + Throwable t = e; + // Need to unwrap this specific type, to see infinite recursion... + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors shouldn't be wrapped (and often can't, as well) + if (t instanceof Error) { + throw (Error) t; + } + // let's try to indicate the path best we can... + throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()"); + } + } + + @SuppressWarnings("deprecation") + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + if (_valueSerializer instanceof SchemaAware) { + return ((SchemaAware)_valueSerializer).getSchema(provider, null); + } + return com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + /* 27-Apr-2015, tatu: First things first; for JSON Schema introspection, + * Enums are special, and unfortunately we will need to add special + * handling here (see https://github.com/FasterXML/jackson-module-jsonSchema/issues/57 + * for details). + */ + Class decl = (typeHint == null) ? null : typeHint.getRawClass(); + if (decl == null) { + decl = _accessorMethod.getDeclaringClass(); + } + if ((decl != null) && (decl.isEnum())) { + if (_acceptJsonFormatVisitorForEnum(visitor, typeHint, decl)) { + return; + } + } + + JsonSerializer ser = _valueSerializer; + if (ser == null) { + if (typeHint == null) { + if (_property != null) { + typeHint = _property.getType(); + } + if (typeHint == null) { + typeHint = visitor.getProvider().constructType(_handledType); + } + } + ser = visitor.getProvider().findTypedValueSerializer(typeHint, false, _property); + if (ser == null) { + visitor.expectAnyFormat(typeHint); + return; + } + } + ser.acceptJsonFormatVisitor(visitor, null); + } + + /** + * Overridable helper method used for special case handling of schema information for + * Enums + * + * @return True if method handled callbacks; false if not; in latter case caller will + * send default callbacks + * + * @since 2.6 + */ + protected boolean _acceptJsonFormatVisitorForEnum(JsonFormatVisitorWrapper visitor, + JavaType typeHint, Class enumType) + throws JsonMappingException + { + // Copied from EnumSerializer#acceptJsonFormatVisitor + JsonStringFormatVisitor stringVisitor = visitor.expectStringFormat(typeHint); + if (stringVisitor != null) { + Set enums = new LinkedHashSet(); + for (Object en : enumType.getEnumConstants()) { + try { + enums.add(String.valueOf(_accessorMethod.invoke(en))); + } catch (Exception e) { + Throwable t = e; + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + if (t instanceof Error) { + throw (Error) t; + } + throw JsonMappingException.wrapWithPath(t, en, _accessorMethod.getName() + "()"); + } + } + stringVisitor.enumTypes(enums); + } + return true; + + } + + protected boolean isNaturalTypeWithStdHandling(Class rawType, JsonSerializer ser) + { + // First: do we have a natural type being handled? + if (rawType.isPrimitive()) { + if (rawType != Integer.TYPE && rawType != Boolean.TYPE && rawType != Double.TYPE) { + return false; + } + } else { + if (rawType != String.class && + rawType != Integer.class && rawType != Boolean.class && rawType != Double.class) { + return false; + } + } + return isDefaultSerializer(ser); + } + + /* + /********************************************************** + /* Other methods + /********************************************************** + */ + + @Override + public String toString() { + return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/MapProperty.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/MapProperty.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/MapProperty.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,157 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.PropertyMetadata; +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.PropertyWriter; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Helper class needed to support flexible filtering of Map properties + * with generic JSON Filter functionality. Since {@link java.util.Map}s + * are not handled as a collection of properties by Jackson (unlike POJOs), + * bit more wrapping is required. + */ +public class MapProperty extends PropertyWriter +{ + private static final long serialVersionUID = 1L; + + protected final TypeSerializer _typeSerializer; + + protected final BeanProperty _property; + + protected Object _key; + + protected JsonSerializer _keySerializer, _valueSerializer; + + public MapProperty(TypeSerializer typeSer, BeanProperty prop) + { + super((prop == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : prop.getMetadata()); + _typeSerializer = typeSer; + _property = prop; + } + + /** + * Initialization method that needs to be called before passing + * property to filter. + */ + public void reset(Object key, + JsonSerializer keySer, JsonSerializer valueSer) + { + _key = key; + _keySerializer = keySer; + _valueSerializer = valueSer; + } + + @Override + public String getName() { + if (_key instanceof String) { + return (String) _key; + } + return String.valueOf(_key); + } + + @Override + public PropertyName getFullName() { + return new PropertyName(getName()); + } + + @Override + public A getAnnotation(Class acls) { + return (_property == null) ? null : _property.getAnnotation(acls); + } + + @Override + public A getContextAnnotation(Class acls) { + return (_property == null) ? null : _property.getContextAnnotation(acls); + } + + @Override + public void serializeAsField(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException + { + _keySerializer.serialize(_key, gen, provider); + if (_typeSerializer == null) { + _valueSerializer.serialize(value, gen, provider); + } else { + _valueSerializer.serializeWithType(value, gen, provider, _typeSerializer); + } + } + + @Override + public void serializeAsOmittedField(Object value, JsonGenerator gen, + SerializerProvider provider) throws Exception + { + if (!gen.canOmitFields()) { + gen.writeOmittedField(getName()); + } + } + + @Override + public void serializeAsElement(Object value, JsonGenerator gen, + SerializerProvider provider) throws Exception + { + if (_typeSerializer == null) { + _valueSerializer.serialize(value, gen, provider); + } else { + _valueSerializer.serializeWithType(value, gen, provider, _typeSerializer); + } + } + + @Override + public void serializeAsPlaceholder(Object value, JsonGenerator gen, + SerializerProvider provider) throws Exception + { + gen.writeNull(); + } + + /* + /********************************************************** + /* Rest of BeanProperty, nop + /********************************************************** + */ + + @Override + public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, + SerializerProvider provider) + throws JsonMappingException + { + if (_property != null) { + _property.depositSchemaProperty(objectVisitor, provider); + } + } + + @Override + @Deprecated + public void depositSchemaProperty(ObjectNode propertiesNode, + SerializerProvider provider) throws JsonMappingException { + // nothing to do here + } + + @Override + public JavaType getType() { + return (_property == null) ? TypeFactory.unknownType() : _property.getType(); + } + + @Override + public PropertyName getWrapperName() { + return (_property == null) ? null : _property.getWrapperName(); + } + + @Override + public AnnotatedMember getMember() { + return (_property == null) ? null : _property.getMember(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/MapSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/MapSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/MapSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,939 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.PropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ArrayBuilders; + +/** + * Standard serializer implementation for serializing {link java.util.Map} types. + *

+ * Note: about the only configurable setting currently is ability to filter out + * entries with specified names. + */ +@JacksonStdImpl +public class MapSerializer + extends ContainerSerializer> + implements ContextualSerializer +{ + private static final long serialVersionUID = 1L; + + protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType(); + + /** + * Map-valued property being serialized with this instance + */ + protected final BeanProperty _property; + + /** + * Set of entries to omit during serialization, if any + */ + protected final HashSet _ignoredEntries; + + /** + * Whether static types should be used for serialization of values + * or not (if not, dynamic runtime type is used) + */ + protected final boolean _valueTypeIsStatic; + + /** + * Declared type of keys + */ + protected final JavaType _keyType; + + /** + * Declared type of contained values + */ + protected final JavaType _valueType; + + /** + * Key serializer to use, if it can be statically determined + */ + protected JsonSerializer _keySerializer; + + /** + * Value serializer to use, if it can be statically determined + */ + protected JsonSerializer _valueSerializer; + + /** + * Type identifier serializer used for values, if any. + */ + protected final TypeSerializer _valueTypeSerializer; + + /** + * If value type can not be statically determined, mapping from + * runtime value types to serializers are stored in this object. + */ + protected PropertySerializerMap _dynamicValueSerializers; + + /** + * Id of the property filter to use, if any; null if none. + * + * @since 2.3 + */ + protected final Object _filterId; + + /** + * Flag set if output is forced to be sorted by keys (usually due + * to annotation). + * + * @since 2.4 + */ + protected final boolean _sortKeys; + + /** + * Value that indicates suppression mechanism to use for values contained; + * either one of values of {@link com.fasterxml.jackson.annotation.JsonInclude.Include}, + * or actual object to compare against ("default value"). + * Note that inclusion value for Map instance itself is handled by caller (POJO + * property that refers to the Map value). + * + * @since 2.5 + */ + protected final Object _suppressableValue; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * @since 2.5 + */ + @SuppressWarnings("unchecked") + protected MapSerializer(HashSet ignoredEntries, + JavaType keyType, JavaType valueType, boolean valueTypeIsStatic, + TypeSerializer vts, + JsonSerializer keySerializer, JsonSerializer valueSerializer) + { + super(Map.class, false); + _ignoredEntries = ignoredEntries; + _keyType = keyType; + _valueType = valueType; + _valueTypeIsStatic = valueTypeIsStatic; + _valueTypeSerializer = vts; + _keySerializer = (JsonSerializer) keySerializer; + _valueSerializer = (JsonSerializer) valueSerializer; + _dynamicValueSerializers = PropertySerializerMap.emptyForProperties(); + _property = null; + _filterId = null; + _sortKeys = false; + _suppressableValue = null; + } + + /** + * @since 2.5 + */ + protected void _ensureOverride() { + if (getClass() != MapSerializer.class) { + throw new IllegalStateException("Missing override in class "+getClass().getName()); + } + } + + @SuppressWarnings("unchecked") + protected MapSerializer(MapSerializer src, BeanProperty property, + JsonSerializer keySerializer, JsonSerializer valueSerializer, + HashSet ignored) + { + super(Map.class, false); + _ignoredEntries = ignored; + _keyType = src._keyType; + _valueType = src._valueType; + _valueTypeIsStatic = src._valueTypeIsStatic; + _valueTypeSerializer = src._valueTypeSerializer; + _keySerializer = (JsonSerializer) keySerializer; + _valueSerializer = (JsonSerializer) valueSerializer; + _dynamicValueSerializers = src._dynamicValueSerializers; + _property = property; + _filterId = src._filterId; + _sortKeys = src._sortKeys; + _suppressableValue = src._suppressableValue; + } + + @Deprecated // since 2.5 + protected MapSerializer(MapSerializer src, TypeSerializer vts) { + this(src, vts, src._suppressableValue); + } + + /** + * @since 2.5 + */ + protected MapSerializer(MapSerializer src, TypeSerializer vts, + Object suppressableValue) + { + super(Map.class, false); + _ignoredEntries = src._ignoredEntries; + _keyType = src._keyType; + _valueType = src._valueType; + _valueTypeIsStatic = src._valueTypeIsStatic; + _valueTypeSerializer = vts; + _keySerializer = src._keySerializer; + _valueSerializer = src._valueSerializer; + _dynamicValueSerializers = src._dynamicValueSerializers; + _property = src._property; + _filterId = src._filterId; + _sortKeys = src._sortKeys; + // 05-Jun-2015, tatu: For referential, this is same as NON_EMPTY; for others, NON_NULL, so: + if (suppressableValue == JsonInclude.Include.NON_ABSENT) { + suppressableValue = _valueType.isReferenceType() ? + JsonInclude.Include.NON_EMPTY : JsonInclude.Include.NON_NULL; + } + _suppressableValue = suppressableValue; + } + + protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys) + { + super(Map.class, false); + _ignoredEntries = src._ignoredEntries; + _keyType = src._keyType; + _valueType = src._valueType; + _valueTypeIsStatic = src._valueTypeIsStatic; + _valueTypeSerializer = src._valueTypeSerializer; + _keySerializer = src._keySerializer; + _valueSerializer = src._valueSerializer; + _dynamicValueSerializers = src._dynamicValueSerializers; + _property = src._property; + _filterId = filterId; + _sortKeys = sortKeys; + _suppressableValue = src._suppressableValue; + } + + @Override + public MapSerializer _withValueTypeSerializer(TypeSerializer vts) { + if (_valueTypeSerializer == vts) { + return this; + } + _ensureOverride(); + return new MapSerializer(this, vts, null); + } + + /** + * @since 2.4 + */ + public MapSerializer withResolved(BeanProperty property, + JsonSerializer keySerializer, JsonSerializer valueSerializer, + HashSet ignored, boolean sortKeys) + { + _ensureOverride(); + MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored); + if (sortKeys != ser._sortKeys) { + ser = new MapSerializer(ser, _filterId, sortKeys); + } + return ser; + } + + @Override + public MapSerializer withFilterId(Object filterId) { + if (_filterId == filterId) { + return this; + } + _ensureOverride(); + return new MapSerializer(this, filterId, _sortKeys); + } + + /** + * Mutant factory for constructing an instance with different inclusion strategy + * for content (Map values). + * + * @since 2.5 + */ + public MapSerializer withContentInclusion(Object suppressableValue) { + if (suppressableValue == _suppressableValue) { + return this; + } + _ensureOverride(); + return new MapSerializer(this, _valueTypeSerializer, suppressableValue); + } + + /** + * @since 2.3 + */ + public static MapSerializer construct(String[] ignoredList, JavaType mapType, + boolean staticValueType, TypeSerializer vts, + JsonSerializer keySerializer, JsonSerializer valueSerializer, + Object filterId) + { + HashSet ignoredEntries = (ignoredList == null || ignoredList.length == 0) + ? null : ArrayBuilders.arrayToSet(ignoredList); + + JavaType keyType, valueType; + + if (mapType == null) { + keyType = valueType = UNSPECIFIED_TYPE; + } else { + keyType = mapType.getKeyType(); + valueType = mapType.getContentType(); + } + // If value type is final, it's same as forcing static value typing: + if (!staticValueType) { + staticValueType = (valueType != null && valueType.isFinal()); + } else { + // also: Object.class can not be handled as static, ever + if (valueType.getRawClass() == Object.class) { + staticValueType = false; + } + } + MapSerializer ser = new MapSerializer(ignoredEntries, keyType, valueType, staticValueType, vts, + keySerializer, valueSerializer); + if (filterId != null) { + ser = ser.withFilterId(filterId); + } + return ser; + } + + /* + /********************************************************** + /* Post-processing (contextualization) + /********************************************************** + */ + + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) + throws JsonMappingException + { + /* 29-Sep-2012, tatu: Actually, we need to do much more contextual + * checking here since we finally know for sure the property, + * and it may have overrides + */ + JsonSerializer ser = null; + JsonSerializer keySer = null; + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember(); + Object suppressableValue = _suppressableValue; + + // First: if we have a property, may have property-annotation overrides + if ((propertyAcc != null) && (intr != null)) { + Object serDef = intr.findKeySerializer(propertyAcc); + if (serDef != null) { + keySer = provider.serializerInstance(propertyAcc, serDef); + } + serDef = intr.findContentSerializer(propertyAcc); + if (serDef != null) { + ser = provider.serializerInstance(propertyAcc, serDef); + } + } + + if (property != null) { + JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), Map.class); + JsonInclude.Include incl = inclV.getContentInclusion(); + if ((incl != null) && (incl != JsonInclude.Include.USE_DEFAULTS)) { + suppressableValue = incl; + } + } + if (ser == null) { + ser = _valueSerializer; + } + // [databind#124]: May have a content converter + ser = findConvertingContentSerializer(provider, property, ser); + if (ser == null) { + // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated, + // we can consider it a static case as well. + // 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho + if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) { + ser = provider.findValueSerializer(_valueType, property); + } + } else { + ser = provider.handleSecondaryContextualization(ser, property); + } + if (keySer == null) { + keySer = _keySerializer; + } + if (keySer == null) { + keySer = provider.findKeySerializer(_keyType, property); + } else { + keySer = provider.handleSecondaryContextualization(keySer, property); + } + HashSet ignored = _ignoredEntries; + boolean sortKeys = false; + if (intr != null && propertyAcc != null) { + String[] moreToIgnore = intr.findPropertiesToIgnore(propertyAcc, true); + if (moreToIgnore != null) { + ignored = (ignored == null) ? new HashSet() : new HashSet(ignored); + for (String str : moreToIgnore) { + ignored.add(str); + } + } + Boolean b = intr.findSerializationSortAlphabetically(propertyAcc); + sortKeys = (b != null) && b.booleanValue(); + } + MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys); + if (suppressableValue != _suppressableValue) { + mser = mser.withContentInclusion(suppressableValue); + } + + // [databind#307]: allow filtering + if (property != null) { + AnnotatedMember m = property.getMember(); + if (m != null) { + Object filterId = intr.findFilterId(m); + if (filterId != null) { + mser = mser.withFilterId(filterId); + } + } + } + return mser; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _valueType; + } + + @Override + public JsonSerializer getContentSerializer() { + return _valueSerializer; + } + + @Override + public boolean isEmpty(SerializerProvider prov, Map value) + { + if (value == null || value.isEmpty()) { + return true; + } + // 05-Nove-2015, tatu: Simple cases are cheap, but for recursive + // emptiness checking we actually need to see if values are empty as well. + Object supp = this._suppressableValue; + + if ((supp == null) || (supp == JsonInclude.Include.ALWAYS)) { + return false; + } + JsonSerializer valueSer = _valueSerializer; + if (valueSer != null) { + for (Object elemValue : value.values()) { + if ((elemValue != null) && !valueSer.isEmpty(prov, elemValue)) { + return false; + } + } + return true; + } + // But if not statically known, try this: + PropertySerializerMap serializers = _dynamicValueSerializers; + for (Object elemValue : value.values()) { + if (elemValue == null) { + continue; + } + Class cc = elemValue.getClass(); + // 05-Nov-2015, tatu: Let's not worry about generic types here, actually; + // unlikely to make any difference, but does add significant overhead + valueSer = serializers.serializerFor(cc); + if (valueSer == null) { + try { + valueSer = _findAndAddDynamic(serializers, cc, prov); + } catch (JsonMappingException e) { // Ugh... can not just throw as-is, so... + // 05-Nov-2015, tatu: For now, probably best not to assume empty then + return false; + } + serializers = _dynamicValueSerializers; + } + if (!valueSer.isEmpty(prov, elemValue)) { + return false; + } + } + return true; + } + + @Override + public boolean hasSingleElement(Map value) { + return (value.size() == 1); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Accessor for currently assigned key serializer. Note that + * this may return null during construction of MapSerializer: + * depedencies are resolved during {@link #createContextual} method + * (which can be overridden by custom implementations), but for some + * dynamic types, it is possible that serializer is only resolved + * during actual serialization. + * + * @since 2.0 + */ + public JsonSerializer getKeySerializer() { + return _keySerializer; + } + + /* + /********************************************************** + /* JsonSerializer implementation + /********************************************************** + */ + + @Override + public void serialize(Map value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + gen.writeStartObject(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + if (!value.isEmpty()) { + Object suppressableValue = _suppressableValue; + if (suppressableValue == JsonInclude.Include.ALWAYS) { + suppressableValue = null; + } else if (suppressableValue == null) { + if (!provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) { + suppressableValue = JsonInclude.Include.NON_NULL; + } + } + if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) { + value = _orderEntries(value); + } + if (_filterId != null) { + serializeFilteredFields(value, gen, provider, + findPropertyFilter(provider, _filterId, value), suppressableValue); + } else if (suppressableValue != null) { + serializeOptionalFields(value, gen, provider, suppressableValue); + } else if (_valueSerializer != null) { + serializeFieldsUsing(value, gen, provider, _valueSerializer); + } else { + serializeFields(value, gen, provider); + } + } + gen.writeEndObject(); + } + + @Override + public void serializeWithType(Map value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException + { + typeSer.writeTypePrefixForObject(value, gen); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(value); + if (!value.isEmpty()) { + Object suppressableValue = _suppressableValue; + if (suppressableValue == JsonInclude.Include.ALWAYS) { + suppressableValue = null; + } else if (suppressableValue == null) { + if (!provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) { + suppressableValue = JsonInclude.Include.NON_NULL; + } + } + if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) { + value = _orderEntries(value); + } + if (_filterId != null) { + serializeFilteredFields(value, gen, provider, + findPropertyFilter(provider, _filterId, value), suppressableValue); + } else if (suppressableValue != null) { + serializeOptionalFields(value, gen, provider, suppressableValue); + } else if (_valueSerializer != null) { + serializeFieldsUsing(value, gen, provider, _valueSerializer); + } else { + serializeFields(value, gen, provider); + } + } + typeSer.writeTypeSuffixForObject(value, gen); + } + + /* + /********************************************************** + /* Secondary serialization methods + /********************************************************** + */ + + /** + * General-purpose serialization for contents, where we do not necessarily know + * the value serialization, but + * we do know that no value suppression is needed (which simplifies processing a bit) + */ + public void serializeFields(Map value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + // If value type needs polymorphic type handling, some more work needed: + if (_valueTypeSerializer != null) { + serializeTypedFields(value, gen, provider, null); + return; + } + final JsonSerializer keySerializer = _keySerializer; + final HashSet ignored = _ignoredEntries; + + PropertySerializerMap serializers = _dynamicValueSerializers; + + for (Map.Entry entry : value.entrySet()) { + Object valueElem = entry.getValue(); + // First, serialize key + Object keyElem = entry.getKey(); + + if (keyElem == null) { + provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider); + } else { + // One twist: is entry ignorable? If so, skip + if (ignored != null && ignored.contains(keyElem)) continue; + keySerializer.serialize(keyElem, gen, provider); + } + + // And then value + if (valueElem == null) { + provider.defaultSerializeNull(gen); + continue; + } + JsonSerializer serializer = _valueSerializer; + if (serializer == null) { + Class cc = valueElem.getClass(); + serializer = serializers.serializerFor(cc); + if (serializer == null) { + if (_valueType.hasGenericTypes()) { + serializer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_valueType, cc), provider); + } else { + serializer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicValueSerializers; + } + } + try { + serializer.serialize(valueElem, gen, provider); + } catch (Exception e) { + // Add reference information + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + + /** + * Serialization method called when exclusion filtering needs to be applied. + */ + public void serializeOptionalFields(Map value, JsonGenerator gen, SerializerProvider provider, + Object suppressableValue) + throws IOException + { + // If value type needs polymorphic type handling, some more work needed: + if (_valueTypeSerializer != null) { + serializeTypedFields(value, gen, provider, suppressableValue); + return; + } + final HashSet ignored = _ignoredEntries; + PropertySerializerMap serializers = _dynamicValueSerializers; + + for (Map.Entry entry : value.entrySet()) { + // First find key serializer + final Object keyElem = entry.getKey(); + JsonSerializer keySerializer; + if (keyElem == null) { + keySerializer = provider.findNullKeySerializer(_keyType, _property); + } else { + if (ignored != null && ignored.contains(keyElem)) continue; + keySerializer = _keySerializer; + } + + // Then value serializer + final Object valueElem = entry.getValue(); + JsonSerializer valueSer; + if (valueElem == null) { + if (suppressableValue != null) { // all suppressions include null-suppression + continue; + } + valueSer = provider.getDefaultNullValueSerializer(); + } else { + valueSer = _valueSerializer; + if (valueSer == null) { + Class cc = valueElem.getClass(); + valueSer = serializers.serializerFor(cc); + if (valueSer == null) { + if (_valueType.hasGenericTypes()) { + valueSer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_valueType, cc), provider); + } else { + valueSer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicValueSerializers; + } + } + // also may need to skip non-empty values: + if ((suppressableValue == JsonInclude.Include.NON_EMPTY) + && valueSer.isEmpty(provider, valueElem)) { + continue; + } + } + // and then serialize, if all went well + try { + keySerializer.serialize(keyElem, gen, provider); + valueSer.serialize(valueElem, gen, provider); + } catch (Exception e) { + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + + /** + * Method called to serialize fields, when the value type is statically known, + * so that value serializer is passed and does not need to be fetched from + * provider. + */ + public void serializeFieldsUsing(Map value, JsonGenerator gen, SerializerProvider provider, + JsonSerializer ser) + throws IOException + { + final JsonSerializer keySerializer = _keySerializer; + final HashSet ignored = _ignoredEntries; + final TypeSerializer typeSer = _valueTypeSerializer; + + for (Map.Entry entry : value.entrySet()) { + Object keyElem = entry.getKey(); + if (ignored != null && ignored.contains(keyElem)) continue; + + if (keyElem == null) { + provider.findNullKeySerializer(_keyType, _property).serialize(null, gen, provider); + } else { + keySerializer.serialize(keyElem, gen, provider); + } + final Object valueElem = entry.getValue(); + if (valueElem == null) { + provider.defaultSerializeNull(gen); + } else { + try { + if (typeSer == null) { + ser.serialize(valueElem, gen, provider); + } else { + ser.serializeWithType(valueElem, gen, provider, typeSer); + } + } catch (Exception e) { + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + } + + /** + * Helper method used when we have a JSON Filter to use for potentially + * filtering out Map entries. + * + * @since 2.5 + */ + public void serializeFilteredFields(Map value, JsonGenerator gen, SerializerProvider provider, + PropertyFilter filter, + Object suppressableValue) // since 2.5 + throws IOException + { + final HashSet ignored = _ignoredEntries; + + PropertySerializerMap serializers = _dynamicValueSerializers; + final MapProperty prop = new MapProperty(_valueTypeSerializer, _property); + + for (Map.Entry entry : value.entrySet()) { + // First, serialize key; unless ignorable by key + final Object keyElem = entry.getKey(); + if (ignored != null && ignored.contains(keyElem)) continue; + + JsonSerializer keySerializer; + if (keyElem == null) { + keySerializer = provider.findNullKeySerializer(_keyType, _property); + } else { + keySerializer = _keySerializer; + } + // or by value; nulls often suppressed + final Object valueElem = entry.getValue(); + + JsonSerializer valueSer; + // And then value + if (valueElem == null) { + if (suppressableValue != null) { // all suppressions include null-suppression + continue; + } + valueSer = provider.getDefaultNullValueSerializer(); + } else { + valueSer = _valueSerializer; + if (valueSer == null) { + Class cc = valueElem.getClass(); + valueSer = serializers.serializerFor(cc); + if (valueSer == null) { + if (_valueType.hasGenericTypes()) { + valueSer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_valueType, cc), provider); + } else { + valueSer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicValueSerializers; + } + } + // also may need to skip non-empty values: + if ((suppressableValue == JsonInclude.Include.NON_EMPTY) + && valueSer.isEmpty(provider, valueElem)) { + continue; + } + } + // and with that, ask filter to handle it + prop.reset(keyElem, keySerializer, valueSer); + try { + filter.serializeAsField(valueElem, gen, provider, prop); + } catch (Exception e) { + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + + @Deprecated // since 2.5 + public void serializeFilteredFields(Map value, JsonGenerator gen, SerializerProvider provider, + PropertyFilter filter) throws IOException { + serializeFilteredFields(value, gen, provider, filter, + provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES) ? null : JsonInclude.Include.NON_NULL); + } + + /** + * @since 2.5 + */ + public void serializeTypedFields(Map value, JsonGenerator gen, SerializerProvider provider, + Object suppressableValue) // since 2.5 + throws IOException + { + final HashSet ignored = _ignoredEntries; + PropertySerializerMap serializers = _dynamicValueSerializers; + + for (Map.Entry entry : value.entrySet()) { + Object keyElem = entry.getKey(); + JsonSerializer keySerializer; + if (keyElem == null) { + keySerializer = provider.findNullKeySerializer(_keyType, _property); + } else { + // One twist: is entry ignorable? If so, skip + if (ignored != null && ignored.contains(keyElem)) continue; + keySerializer = _keySerializer; + } + final Object valueElem = entry.getValue(); + + // And then value + JsonSerializer valueSer; + if (valueElem == null) { + if (suppressableValue != null) { // all suppression include null suppression + continue; + } + valueSer = provider.getDefaultNullValueSerializer(); + } else { + valueSer = _valueSerializer; + Class cc = valueElem.getClass(); + valueSer = serializers.serializerFor(cc); + if (valueSer == null) { + if (_valueType.hasGenericTypes()) { + valueSer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_valueType, cc), provider); + } else { + valueSer = _findAndAddDynamic(serializers, cc, provider); + } + serializers = _dynamicValueSerializers; + } + // also may need to skip non-empty values: + if ((suppressableValue == JsonInclude.Include.NON_EMPTY) + && valueSer.isEmpty(provider, valueElem)) { + continue; + } + } + keySerializer.serialize(keyElem, gen, provider); + try { + valueSer.serializeWithType(valueElem, gen, provider, _valueTypeSerializer); + } catch (Exception e) { + String keyDesc = ""+keyElem; + wrapAndThrow(provider, e, value, keyDesc); + } + } + } + + @Deprecated // since 2.5 + protected void serializeTypedFields(Map value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + serializeTypedFields(value, gen, provider, + provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES) ? null : JsonInclude.Include.NON_NULL); + } + + /* + /********************************************************** + /* Schema related functionality + /********************************************************** + */ + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = createSchemaNode("object", true); + //(ryan) even though it's possible to statically determine the "value" type of the map, + // there's no way to statically determine the keys, so the "Entries" can't be determined. + return o; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + JsonMapFormatVisitor v2 = (visitor == null) ? null : visitor.expectMapFormat(typeHint); + if (v2 != null) { + v2.keyFormat(_keySerializer, _keyType); + JsonSerializer valueSer = _valueSerializer; + if (valueSer == null) { + valueSer = _findAndAddDynamic(_dynamicValueSerializers, + _valueType, visitor.getProvider()); + } + v2.valueFormat(valueSer, _valueType); + } + } + + /* + /********************************************************** + /* Internal helper methods + /********************************************************** + */ + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + JavaType type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + if (map != result.map) { + _dynamicValueSerializers = result.map; + } + return result.serializer; + } + + protected Map _orderEntries(Map input) + { + // minor optimization: may already be sorted? + if (input instanceof SortedMap) { + return input; + } + return new TreeMap(input); + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Intermediate base class for limited number of scalar types + * that should never include type information. These are "native" + * types that are default mappings for corresponding JSON scalar + * types: {@link java.lang.String}, {@link java.lang.Integer}, + * {@link java.lang.Double} and {@link java.lang.Boolean}. + */ +@SuppressWarnings("serial") +public abstract class NonTypedScalarSerializerBase + extends StdScalarSerializer +{ + protected NonTypedScalarSerializerBase(Class t) { + super(t); + } + + protected NonTypedScalarSerializerBase(Class t, boolean bogus) { + super(t, bogus); + } + + @Override + public final void serializeWithType(T value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + // no type info, just regular serialization + serialize(value, gen, provider); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NullSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NullSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NullSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,56 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.lang.reflect.Type; +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * This is a simple dummy serializer that will just output literal + * JSON null value whenever serialization is requested. + * Used as the default "null serializer" (which is used for serializing + * null object references unless overridden), as well as for some + * more exotic types (java.lang.Void). + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class NullSerializer + extends StdSerializer +{ + public final static NullSerializer instance = new NullSerializer(); + + private NullSerializer() { super(Object.class); } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeNull(); + } + + /** + * Although this method should rarely get called, for convenience we should override + * it, and handle it same way as "natural" types: by serializing exactly as is, + * without type decorations. The most common possible use case is that of delegation + * by JSON filter; caller can not know what kind of serializer it gets handed. + */ + @Override + public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) + throws IOException + { + gen.writeNull(); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { + return createSchemaNode("null"); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + visitor.expectNullFormat(typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,85 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * As a fallback, we may need to use this serializer for other + * types of {@link Number}s: both custom types and "big" numbers + * like {@link BigInteger} and {@link BigDecimal}. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class NumberSerializer + extends StdScalarSerializer +{ + /** + * Static instance that is only to be used for {@link java.lang.Number}. + */ + public final static NumberSerializer instance = new NumberSerializer(Number.class); + + protected final boolean _isInt; + + /** + * @since 2.5 + */ + public NumberSerializer(Class rawType) { + super(rawType, false); + // since this will NOT be constructed for Integer or Long, only case is: + _isInt = (rawType == BigInteger.class); + } + + @Override + public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException + { + // should mostly come in as one of these two: + if (value instanceof BigDecimal) { + g.writeNumber((BigDecimal) value); + } else if (value instanceof BigInteger) { + g.writeNumber((BigInteger) value); + + // These should not occur, as more specific methods should have been called; but + // just in case let's cover all bases: + } else if (value instanceof Long) { + g.writeNumber(value.longValue()); + } else if (value instanceof Double) { + g.writeNumber(value.doubleValue()); + } else if (value instanceof Float) { + g.writeNumber(value.floatValue()); + } else if (value instanceof Integer || value instanceof Byte || value instanceof Short) { + g.writeNumber(value.intValue()); // doesn't need to be cast to smaller numbers + } else { + // We'll have to use fallback "untyped" number write method + g.writeNumber(value.toString()); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode(_isInt ? "integer" : "number", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + if (_isInt) { + visitIntFormat(visitor, typeHint, JsonParser.NumberType.BIG_INTEGER); + } else { + Class h = handledType(); + if (h == BigDecimal.class) { + visitFloatFormat(visitor, typeHint, JsonParser.NumberType.BIG_DECIMAL); + } else { + // otherwise bit unclear what to call... but let's try: + /*JsonNumberFormatVisitor v2 =*/ visitor.expectNumberFormat(typeHint); + } + } + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,237 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +/** + * Container class for serializers used for handling standard JDK-provided + * types. + */ +@SuppressWarnings("serial") +public class NumberSerializers { + protected NumberSerializers() { + } + + public static void addAll(Map> allDeserializers) { + final JsonSerializer intS = new IntegerSerializer(); + allDeserializers.put(Integer.class.getName(), intS); + allDeserializers.put(Integer.TYPE.getName(), intS); + allDeserializers.put(Long.class.getName(), LongSerializer.instance); + allDeserializers.put(Long.TYPE.getName(), LongSerializer.instance); + allDeserializers.put(Byte.class.getName(), IntLikeSerializer.instance); + allDeserializers.put(Byte.TYPE.getName(), IntLikeSerializer.instance); + allDeserializers.put(Short.class.getName(), ShortSerializer.instance); + allDeserializers.put(Short.TYPE.getName(), ShortSerializer.instance); + + // Numbers, limited length floating point + allDeserializers.put(Float.class.getName(), FloatSerializer.instance); + allDeserializers.put(Float.TYPE.getName(), FloatSerializer.instance); + allDeserializers.put(Double.class.getName(), DoubleSerializer.instance); + allDeserializers.put(Double.TYPE.getName(), DoubleSerializer.instance); + } + + /* + /********************************************************** + /* Shared base class + /********************************************************** + */ + + protected abstract static class Base extends StdScalarSerializer + implements ContextualSerializer { + protected final JsonParser.NumberType _numberType; + protected final String _schemaType; + protected final boolean _isInt; + + protected Base(Class cls, JsonParser.NumberType numberType, + String schemaType) { + super(cls, false); + _numberType = numberType; + _schemaType = schemaType; + _isInt = (numberType == JsonParser.NumberType.INT) + || (numberType == JsonParser.NumberType.LONG) + || (numberType == JsonParser.NumberType.BIG_INTEGER); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode(_schemaType, true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, + JavaType typeHint) throws JsonMappingException + { + if (_isInt) { + visitIntFormat(visitor, typeHint, _numberType); + } else { + visitFloatFormat(visitor, typeHint, _numberType); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, + BeanProperty property) throws JsonMappingException { + if (property != null) { + AnnotatedMember m = property.getMember(); + if (m != null) { + JsonFormat.Value format = prov.getAnnotationIntrospector() + .findFormat(m); + if (format != null) { + switch (format.getShape()) { + case STRING: + return ToStringSerializer.instance; + default: + } + } + } + } + return this; + } + } + + /* + * /********************************************************** /* Concrete + * serializers, numerics + * /********************************************************** + */ + + @JacksonStdImpl + public final static class ShortSerializer extends Base { + final static ShortSerializer instance = new ShortSerializer(); + + public ShortSerializer() { + super(Short.class, JsonParser.NumberType.INT, "number"); + } + + @Override + public void serialize(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeNumber(((Short) value).shortValue()); + } + } + + /** + * This is the special serializer for regular {@link java.lang.Integer}s + * (and primitive ints) + *

+ * Since this is one of "native" types, no type information is ever included + * on serialization (unlike for most scalar types) + *

+ * NOTE: as of 2.6, generic signature changed to Object, to avoid generation + * of bridge methods. + */ + @JacksonStdImpl + public final static class IntegerSerializer extends Base { + public IntegerSerializer() { + super(Integer.class, JsonParser.NumberType.INT, "integer"); + } + + @Override + public void serialize(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeNumber(((Integer) value).intValue()); + } + + // IMPORTANT: copied from `NonTypedScalarSerializerBase` + @Override + public void serializeWithType(Object value, JsonGenerator gen, + SerializerProvider provider, TypeSerializer typeSer) + throws IOException { + // no type info, just regular serialization + serialize(value, gen, provider); + } + } + + /** + * Similar to {@link IntegerSerializer}, but will not cast to Integer: + * instead, cast is to {@link java.lang.Number}, and conversion is by + * calling {@link java.lang.Number#intValue}. + */ + @JacksonStdImpl + public final static class IntLikeSerializer extends Base { + final static IntLikeSerializer instance = new IntLikeSerializer(); + + public IntLikeSerializer() { + super(Number.class, JsonParser.NumberType.INT, "integer"); + } + + @Override + public void serialize(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeNumber(((Number) value).intValue()); + } + } + + @JacksonStdImpl + public final static class LongSerializer extends Base { + final static LongSerializer instance = new LongSerializer(); + + public LongSerializer() { + super(Long.class, JsonParser.NumberType.LONG, "number"); + } + + @Override + public void serialize(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeNumber(((Long) value).longValue()); + } + } + + @JacksonStdImpl + public final static class FloatSerializer extends Base { + final static FloatSerializer instance = new FloatSerializer(); + + public FloatSerializer() { + super(Float.class, JsonParser.NumberType.FLOAT, "number"); + } + + @Override + public void serialize(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeNumber(((Float) value).floatValue()); + } + } + + /** + * This is the special serializer for regular {@link java.lang.Double}s (and + * primitive doubles) + *

+ * Since this is one of "native" types, no type information is ever included + * on serialization (unlike for most scalar types as of 1.5) + */ + @JacksonStdImpl + public final static class DoubleSerializer extends Base { + final static DoubleSerializer instance = new DoubleSerializer(); + + public DoubleSerializer() { + super(Double.class, JsonParser.NumberType.DOUBLE, "number"); + } + + @Override + public void serialize(Object value, JsonGenerator gen, + SerializerProvider provider) throws IOException { + gen.writeNumber(((Double) value).doubleValue()); + } + + // IMPORTANT: copied from `NonTypedScalarSerializerBase` + @Override + public void serializeWithType(Object value, JsonGenerator gen, + SerializerProvider provider, TypeSerializer typeSer) + throws IOException { + // no type info, just regular serialization + serialize(value, gen, provider); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,412 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.type.ArrayType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Generic serializer for Object arrays (Object[]). + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class ObjectArraySerializer + extends ArraySerializerBase + implements ContextualSerializer +{ + /** + * Whether we are using static typing (using declared types, ignoring + * runtime type) or not for elements. + */ + protected final boolean _staticTyping; + + /** + * Declared type of element entries + */ + protected final JavaType _elementType; + + /** + * Type serializer to use for values, if any. + */ + protected final TypeSerializer _valueTypeSerializer; + + /** + * Value serializer to use, if it can be statically determined. + */ + protected JsonSerializer _elementSerializer; + + /** + * If element type can not be statically determined, mapping from + * runtime type to serializer is handled using this object + */ + protected PropertySerializerMap _dynamicSerializers; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public ObjectArraySerializer(JavaType elemType, boolean staticTyping, + TypeSerializer vts, JsonSerializer elementSerializer) + { + super(Object[].class); + _elementType = elemType; + _staticTyping = staticTyping; + _valueTypeSerializer = vts; + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + _elementSerializer = elementSerializer; + } + + public ObjectArraySerializer(ObjectArraySerializer src, TypeSerializer vts) + { + super(src); + _elementType = src._elementType; + _valueTypeSerializer = vts; + _staticTyping = src._staticTyping; + _dynamicSerializers = src._dynamicSerializers; + _elementSerializer = src._elementSerializer; + } + + @SuppressWarnings("unchecked") + public ObjectArraySerializer(ObjectArraySerializer src, + BeanProperty property, TypeSerializer vts, JsonSerializer elementSerializer, + Boolean unwrapSingle) + { + super(src, property, unwrapSingle); + _elementType = src._elementType; + _valueTypeSerializer = vts; + _staticTyping = src._staticTyping; + _dynamicSerializers = src._dynamicSerializers; + _elementSerializer = (JsonSerializer) elementSerializer; + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { + return new ObjectArraySerializer(this, prop, + _valueTypeSerializer, _elementSerializer, unwrapSingle); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new ObjectArraySerializer(_elementType, _staticTyping, vts, _elementSerializer); + } + + public ObjectArraySerializer withResolved(BeanProperty prop, + TypeSerializer vts, JsonSerializer ser, Boolean unwrapSingle) { + if ((_property == prop) && (ser == _elementSerializer) + && (_valueTypeSerializer == vts) && (_unwrapSingle == unwrapSingle)) { + return this; + } + return new ObjectArraySerializer(this, prop, vts, ser, unwrapSingle); + } + + /* + /********************************************************** + /* Post-processing + /********************************************************** + */ + + @Override + public JsonSerializer createContextual(SerializerProvider provider, + BeanProperty property) + throws JsonMappingException + { + TypeSerializer vts = _valueTypeSerializer; + if (vts != null) { + vts = vts.forProperty(property); + } + JsonSerializer ser = null; + Boolean unwrapSingle = null; + + // First: if we have a property, may have property-annotation overrides + if (property != null) { + AnnotatedMember m = property.getMember(); + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + if (m != null) { + Object serDef = intr.findContentSerializer(m); + if (serDef != null) { + ser = provider.serializerInstance(m, serDef); + } + } + JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType); + if (format != null) { + unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); + } + } + if (ser == null) { + ser = _elementSerializer; + } + // #124: May have a content converter + ser = findConvertingContentSerializer(provider, property, ser); + if (ser == null) { + // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated, + // we can consider it a static case as well. + if (_elementType != null) { + if (_staticTyping && !_elementType.isJavaLangObject()) { + ser = provider.findValueSerializer(_elementType, property); + } + } + } else { + ser = provider.handleSecondaryContextualization(ser, property); + } + return withResolved(property, vts, ser, unwrapSingle); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _elementType; + } + + @Override + public JsonSerializer getContentSerializer() { + return _elementSerializer; + } + + @Override + public boolean isEmpty(SerializerProvider prov, Object[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(Object[] value) { + return (value.length == 1); + } + + /* + /********************************************************** + /* Actual serialization + /********************************************************** + */ + + @Override + public final void serialize(Object[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(Object[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 0) { + return; + } + if (_elementSerializer != null) { + serializeContentsUsing(value, gen, provider, _elementSerializer); + return; + } + if (_valueTypeSerializer != null) { + serializeTypedContents(value, gen, provider); + return; + } + int i = 0; + Object elem = null; + try { + PropertySerializerMap serializers = _dynamicSerializers; + for (; i < len; ++i) { + elem = value[i]; + if (elem == null) { + provider.defaultSerializeNull(gen); + continue; + } + Class cc = elem.getClass(); + JsonSerializer serializer = serializers.serializerFor(cc); + if (serializer == null) { + // To fix [JACKSON-508] + if (_elementType.hasGenericTypes()) { + serializer = _findAndAddDynamic(serializers, + provider.constructSpecializedType(_elementType, cc), provider); + } else { + serializer = _findAndAddDynamic(serializers, cc, provider); + } + } + serializer.serialize(elem, gen, provider); + } + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + // [JACKSON-55] Need to add reference information + /* 05-Mar-2009, tatu: But one nasty edge is when we get + * StackOverflow: usually due to infinite loop. But that gets + * hidden within an InvocationTargetException... + */ + Throwable t = e; + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + if (t instanceof Error) { + throw (Error) t; + } + throw JsonMappingException.wrapWithPath(t, elem, i); + } + } + + public void serializeContentsUsing(Object[] value, JsonGenerator jgen, SerializerProvider provider, + JsonSerializer ser) throws IOException + { + final int len = value.length; + final TypeSerializer typeSer = _valueTypeSerializer; + + int i = 0; + Object elem = null; + try { + for (; i < len; ++i) { + elem = value[i]; + if (elem == null) { + provider.defaultSerializeNull(jgen); + continue; + } + if (typeSer == null) { + ser.serialize(elem, jgen, provider); + } else { + ser.serializeWithType(elem, jgen, provider, typeSer); + } + } + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + Throwable t = e; + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + if (t instanceof Error) { + throw (Error) t; + } + throw JsonMappingException.wrapWithPath(t, elem, i); + } + } + + public void serializeTypedContents(Object[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + final int len = value.length; + final TypeSerializer typeSer = _valueTypeSerializer; + int i = 0; + Object elem = null; + try { + PropertySerializerMap serializers = _dynamicSerializers; + for (; i < len; ++i) { + elem = value[i]; + if (elem == null) { + provider.defaultSerializeNull(jgen); + continue; + } + Class cc = elem.getClass(); + JsonSerializer serializer = serializers.serializerFor(cc); + if (serializer == null) { + serializer = _findAndAddDynamic(serializers, cc, provider); + } + serializer.serializeWithType(elem, jgen, provider, typeSer); + } + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + Throwable t = e; + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + if (t instanceof Error) { + throw (Error) t; + } + throw JsonMappingException.wrapWithPath(t, elem, i); + } + } + + @SuppressWarnings("deprecation") + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode o = createSchemaNode("array", true); + if (typeHint != null) { + JavaType javaType = provider.constructType(typeHint); + if (javaType.isArrayType()) { + Class componentType = ((ArrayType) javaType).getContentType().getRawClass(); + // 15-Oct-2010, tatu: We can't serialize plain Object.class; but what should it produce here? Untyped? + if (componentType == Object.class) { + o.set("items", com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode()); + } else { + JsonSerializer ser = provider.findValueSerializer(componentType, _property); + JsonNode schemaNode = (ser instanceof SchemaAware) ? + ((SchemaAware) ser).getSchema(provider, null) : + com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); + o.set("items", schemaNode); + } + } + } + return o; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + JsonArrayFormatVisitor arrayVisitor = visitor.expectArrayFormat(typeHint); + if (arrayVisitor != null) { + TypeFactory tf = visitor.getProvider().getTypeFactory(); + JavaType contentType = tf.moreSpecificType(_elementType, typeHint.getContentType()); + if (contentType == null) { + throw JsonMappingException.from(visitor.getProvider(), "Could not resolve type"); + } + JsonSerializer valueSer = _elementSerializer; + if (valueSer == null) { + valueSer = visitor.getProvider().findValueSerializer(contentType, _property); + } + arrayVisitor.itemsFormat(valueSer, contentType); + } + } + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicSerializers = result.map; + } + return result.serializer; + } + + protected final JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + JavaType type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSecondarySerializer(type, provider, _property); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicSerializers = result.map; + } + return result.serializer; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/RawSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/RawSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/RawSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,56 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.lang.reflect.Type; +import java.io.IOException; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * This is a simple dummy serializer that will just output raw values by calling + * toString() on value to serialize. + */ +@SuppressWarnings("serial") +public class RawSerializer + extends StdSerializer +{ + /** + * Constructor takes in expected type of values; but since caller + * typically can not really provide actual type parameter, we will + * just take wild card and coerce type. + */ + public RawSerializer(Class cls) { + super(cls, false); + } + + @Override + public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeRawValue(value.toString()); + } + + @Override + public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException + { + typeSer.writeTypePrefixForScalar(value, jgen); + serialize(value, jgen, provider); + typeSer.writeTypeSuffixForScalar(value, jgen); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + // type not really known, but since it is a JSON string: + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + // type not really known, but since it is a JSON string: + visitStringFormat(visitor, typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,121 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Generic handler for types that implement {@link JsonSerializable}. + *

+ * Note: given that this is used for anything that implements + * interface, can not be checked for direct class equivalence. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class SerializableSerializer + extends StdSerializer +{ + public final static SerializableSerializer instance = new SerializableSerializer(); + + // Ugh. Should NOT need this... + private final static AtomicReference _mapperReference = new AtomicReference(); + + protected SerializableSerializer() { super(JsonSerializable.class); } + + @Override + public boolean isEmpty(SerializerProvider serializers, JsonSerializable value) { + if (value instanceof JsonSerializable.Base) { + return ((JsonSerializable.Base) value).isEmpty(serializers); + } + return false; + } + + @Override + public void serialize(JsonSerializable value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + value.serialize(gen, serializers); + } + + @Override + public final void serializeWithType(JsonSerializable value, JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) throws IOException { + value.serializeWithType(gen, serializers, typeSer); + } + + @Override + @SuppressWarnings("deprecation") + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + ObjectNode objectNode = createObjectNode(); + String schemaType = "any"; + String objectProperties = null; + String itemDefinition = null; + if (typeHint != null) { + Class rawClass = TypeFactory.rawClass(typeHint); + if (rawClass.isAnnotationPresent(JsonSerializableSchema.class)) { + JsonSerializableSchema schemaInfo = rawClass.getAnnotation(JsonSerializableSchema.class); + schemaType = schemaInfo.schemaType(); + if (!JsonSerializableSchema.NO_VALUE.equals(schemaInfo.schemaObjectPropertiesDefinition())) { + objectProperties = schemaInfo.schemaObjectPropertiesDefinition(); + } + if (!JsonSerializableSchema.NO_VALUE.equals(schemaInfo.schemaItemDefinition())) { + itemDefinition = schemaInfo.schemaItemDefinition(); + } + } + } + /* 19-Mar-2012, tatu: geez, this is butt-ugly abonimation of code... + * really, really should not require back ref to an ObjectMapper. + */ + objectNode.put("type", schemaType); + if (objectProperties != null) { + try { + objectNode.set("properties", _getObjectMapper().readTree(objectProperties)); + } catch (IOException e) { + throw JsonMappingException.from(provider, + "Failed to parse @JsonSerializableSchema.schemaObjectPropertiesDefinition value"); + } + } + if (itemDefinition != null) { + try { + objectNode.set("items", _getObjectMapper().readTree(itemDefinition)); + } catch (IOException e) { + throw JsonMappingException.from(provider, + "Failed to parse @JsonSerializableSchema.schemaItemDefinition value"); + } + } + // always optional, no need to specify: + //objectNode.put("required", false); + return objectNode; + } + + private final static synchronized ObjectMapper _getObjectMapper() + { + ObjectMapper mapper = _mapperReference.get(); + if (mapper == null) { + mapper = new ObjectMapper(); + _mapperReference.set(mapper); + } + return mapper; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitor.expectAnyFormat(typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.text.DateFormat; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * Compared to regular {@link java.util.Date} serialization, we do use String + * representation here. Why? Basically to truncate of time part, since + * that should not be used by plain SQL date. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class SqlDateSerializer + extends DateTimeSerializerBase +{ + public SqlDateSerializer() { + /* 12-Apr-2014, tatu: for now, pass explicit 'false' to mean 'not using timestamp', + * for backwards compatibility; this differs from other Date/Calendar types. + */ + this(Boolean.FALSE); + } + + protected SqlDateSerializer(Boolean useTimestamp) { + super(java.sql.Date.class, useTimestamp, null); + } + + @Override + public SqlDateSerializer withFormat(Boolean timestamp, DateFormat customFormat) { + return new SqlDateSerializer(timestamp); + } + + @Override + protected long _timestamp(java.sql.Date value) { + return (value == null) ? 0L : value.getTime(); + } + + @Override + public void serialize(java.sql.Date value, JsonGenerator gen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + if (_asTimestamp(provider)) { + gen.writeNumber(_timestamp(value)); + } else { + gen.writeString(value.toString()); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + //todo: (ryan) add a format for the date in the schema? + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + _acceptJsonFormatVisitor(visitor, typeHint, _useTimestamp); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,36 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat; + +@JacksonStdImpl +@SuppressWarnings("serial") +public class SqlTimeSerializer + extends StdScalarSerializer +{ + public SqlTimeSerializer() { super(java.sql.Time.class); } + + @Override + public void serialize(java.sql.Time value, JsonGenerator g, SerializerProvider provider) throws IOException + { + g.writeString(value.toString()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitStringFormat(visitor, typeHint, JsonValueFormat.DATE_TIME); + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,135 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.lang.reflect.Type; +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +/** + * Intermediate base class for Lists, Collections and Arrays + * that contain static (non-dynamic) value types. + */ +@SuppressWarnings("serial") +public abstract class StaticListSerializerBase> + extends StdSerializer + implements ContextualSerializer +{ + protected final JsonSerializer _serializer; + + /** + * Setting for specific local override for "unwrap single element arrays": + * true for enable unwrapping, false for preventing it, `null` for using + * global configuration. + * + * @since 2.6 + */ + protected final Boolean _unwrapSingle; + + protected StaticListSerializerBase(Class cls) { + super(cls, false); + _serializer = null; + _unwrapSingle = null; + } + + /** + * @since 2.6 + */ + @SuppressWarnings("unchecked") + protected StaticListSerializerBase(StaticListSerializerBase src, + JsonSerializer ser, Boolean unwrapSingle) { + super(src); + _serializer = (JsonSerializer) ser; + _unwrapSingle = unwrapSingle; + } + + /** + * @since 2.6 + */ + public abstract JsonSerializer _withResolved(BeanProperty prop, + JsonSerializer ser, Boolean unwrapSingle); + + /* + /********************************************************** + /* Post-processing + /********************************************************** + */ + + @Override + public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer ser = null; + Boolean unwrapSingle = null; + + if (property != null) { + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + AnnotatedMember m = property.getMember(); + if (m != null) { + Object serDef = intr.findContentSerializer(m); + if (serDef != null) { + ser = provider.serializerInstance(m, serDef); + } + } + JsonFormat.Value format = property.findPropertyFormat(provider.getConfig(), _handledType); + if (format != null) { + unwrapSingle = format.getFeature(JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); + } + } + if (ser == null) { + ser = _serializer; + } + // [databind#124]: May have a content converter + ser = findConvertingContentSerializer(provider, property, ser); + if (ser == null) { + ser = provider.findValueSerializer(String.class, property); + } else { + ser = provider.handleSecondaryContextualization(ser, property); + } + // Optimization: default serializer just writes String, so we can avoid a call: + if (isDefaultSerializer(ser)) { + ser = null; + } + // note: will never have TypeSerializer, because Strings are "natural" type + if ((ser == _serializer) && (unwrapSingle == _unwrapSingle)) { + return this; + } + return _withResolved(property, ser, unwrapSingle); + } + + @Deprecated // since 2.5 + @Override + public boolean isEmpty(T value) { + return isEmpty(null, value); + } + + @Override + public boolean isEmpty(SerializerProvider provider, T value) { + return (value == null) || (value.size() == 0); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("array", true).set("items", contentSchema()); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + acceptContentVisitor(visitor.expectArrayFormat(typeHint)); + } + + /* + /********************************************************** + /* Abstract methods for sub-classes to implement + /********************************************************** + */ + + protected abstract JsonNode contentSchema(); + + protected abstract void acceptContentVisitor(JsonArrayFormatVisitor visitor) + throws JsonMappingException; +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,703 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.HashMap; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.ContainerSerializer; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Dummy container class to group standard homogenous array serializer implementations + * (primitive arrays and String array). + */ +@SuppressWarnings("serial") +public class StdArraySerializers +{ + protected final static HashMap> _arraySerializers = + new HashMap>(); + static { + // Arrays of various types (including common object types) + _arraySerializers.put(boolean[].class.getName(), new StdArraySerializers.BooleanArraySerializer()); + _arraySerializers.put(byte[].class.getName(), new ByteArraySerializer()); + _arraySerializers.put(char[].class.getName(), new StdArraySerializers.CharArraySerializer()); + _arraySerializers.put(short[].class.getName(), new StdArraySerializers.ShortArraySerializer()); + _arraySerializers.put(int[].class.getName(), new StdArraySerializers.IntArraySerializer()); + _arraySerializers.put(long[].class.getName(), new StdArraySerializers.LongArraySerializer()); + _arraySerializers.put(float[].class.getName(), new StdArraySerializers.FloatArraySerializer()); + _arraySerializers.put(double[].class.getName(), new StdArraySerializers.DoubleArraySerializer()); + } + + protected StdArraySerializers() { } + + /** + * Accessor for checking to see if there is a standard serializer for + * given primitive value type. + */ + public static JsonSerializer findStandardImpl(Class cls) { + return _arraySerializers.get(cls.getName()); + } + + /* + **************************************************************** + /* Intermediate base classes + **************************************************************** + */ + + /** + * Intermediate base class used for cases where we may add + * type information (excludes boolean/int/double arrays). + */ + protected abstract static class TypedPrimitiveArraySerializer + extends ArraySerializerBase + { + /** + * Type serializer to use for values, if any. + */ + protected final TypeSerializer _valueTypeSerializer; + + protected TypedPrimitiveArraySerializer(Class cls) { + super(cls); + _valueTypeSerializer = null; + } + + protected TypedPrimitiveArraySerializer(TypedPrimitiveArraySerializer src, + BeanProperty prop, TypeSerializer vts, Boolean unwrapSingle) { + super(src, prop, unwrapSingle); + _valueTypeSerializer = vts; + } + } + + /* + /**************************************************************** + /* Concrete serializers, arrays + /**************************************************************** + */ + + @JacksonStdImpl + public static class BooleanArraySerializer + extends ArraySerializerBase + { + // as above, assuming no one re-defines primitive/wrapper types + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Boolean.class); + + public BooleanArraySerializer() { super(boolean[].class); } + + protected BooleanArraySerializer(BooleanArraySerializer src, + BeanProperty prop, Boolean unwrapSingle) { + super(src, prop, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { + return new BooleanArraySerializer(this, prop, unwrapSingle); + } + + /** + * Booleans never add type info; hence, even if type serializer is suggested, + * we'll ignore it... + */ + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return this; + } + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary + return null; + } + + @Override + public boolean isEmpty(SerializerProvider prov, boolean[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(boolean[] value) { + return (value.length == 1); + } + + @Override + public final void serialize(boolean[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, jgen, provider); + return; + } + } + jgen.writeStartArray(len); + serializeContents(value, jgen, provider); + jgen.writeEndArray(); + } + + @Override + public void serializeContents(boolean[] value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + for (int i = 0, len = value.length; i < len; ++i) { + jgen.writeBoolean(value[i]); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = createSchemaNode("array", true); + o.set("items", createSchemaNode("boolean")); + return o; + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.BOOLEAN); + } + } + + @JacksonStdImpl + public static class ShortArraySerializer extends TypedPrimitiveArraySerializer + { + // as above, assuming no one re-defines primitive/wrapper types + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Short.TYPE); + + public ShortArraySerializer() { super(short[].class); } + public ShortArraySerializer(ShortArraySerializer src, BeanProperty prop, + TypeSerializer vts, Boolean unwrapSingle) { + super(src, prop, vts, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop,Boolean unwrapSingle) { + return new ShortArraySerializer(this, prop, _valueTypeSerializer, unwrapSingle); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new ShortArraySerializer(this, _property, vts, _unwrapSingle); + } + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary + return null; + } + + @Override + public boolean isEmpty(SerializerProvider prov, short[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(short[] value) { + return (value.length == 1); + } + + @Override + public final void serialize(short[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, jgen, provider); + return; + } + } + jgen.writeStartArray(len); + serializeContents(value, jgen, provider); + jgen.writeEndArray(); + } + + @SuppressWarnings("cast") + @Override + public void serializeContents(short[] value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + if (_valueTypeSerializer != null) { + for (int i = 0, len = value.length; i < len; ++i) { + _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Short.TYPE); + jgen.writeNumber(value[i]); + _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen); + } + return; + } + for (int i = 0, len = value.length; i < len; ++i) { + jgen.writeNumber((int)value[i]); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + //no "short" type defined by json + ObjectNode o = createSchemaNode("array", true); + return o.set("items", createSchemaNode("integer")); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.INTEGER); + } + } + + /** + * Character arrays are different from other integral number arrays in that + * they are most likely to be textual data, and should be written as + * Strings, not arrays of entries. + *

+ * NOTE: since it is NOT serialized as an array, can not use AsArraySerializer as base + */ + @JacksonStdImpl + public static class CharArraySerializer extends StdSerializer + { + public CharArraySerializer() { super(char[].class); } + + @Override + public boolean isEmpty(SerializerProvider prov, char[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public void serialize(char[] value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + // [JACKSON-289] allows serializing as 'sparse' char array too: + if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) { + jgen.writeStartArray(value.length); + _writeArrayContents(jgen, value); + jgen.writeEndArray(); + } else { + jgen.writeString(value, 0, value.length); + } + } + + @Override + public void serializeWithType(char[] value, JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonGenerationException + { + // [JACKSON-289] allows serializing as 'sparse' char array too: + if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) { + typeSer.writeTypePrefixForArray(value, jgen); + _writeArrayContents(jgen, value); + typeSer.writeTypeSuffixForArray(value, jgen); + } else { // default is to write as simple String + typeSer.writeTypePrefixForScalar(value, jgen); + jgen.writeString(value, 0, value.length); + typeSer.writeTypeSuffixForScalar(value, jgen); + } + } + + private final void _writeArrayContents(JsonGenerator jgen, char[] value) + throws IOException, JsonGenerationException + { + for (int i = 0, len = value.length; i < len; ++i) { + jgen.writeString(value, i, 1); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + ObjectNode o = createSchemaNode("array", true); + ObjectNode itemSchema = createSchemaNode("string"); + itemSchema.put("type", "string"); + return o.set("items", itemSchema); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.STRING); + } + } + + @JacksonStdImpl + public static class IntArraySerializer extends ArraySerializerBase + { + // as above, assuming no one re-defines primitive/wrapper types + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Integer.TYPE); + + public IntArraySerializer() { super(int[].class); } + + /** + * @since 2.6 + */ + protected IntArraySerializer(IntArraySerializer src, + BeanProperty prop, Boolean unwrapSingle) { + super(src, prop, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { + return new IntArraySerializer(this, prop, unwrapSingle); + } + + /** + * Ints never add type info; hence, even if type serializer is suggested, + * we'll ignore it... + */ + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return this; + } + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary + return null; + } + + @Override + public boolean isEmpty(SerializerProvider prov, int[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(int[] value) { + return (value.length == 1); + } + + @Override + public final void serialize(int[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, jgen, provider); + return; + } + } + jgen.writeStartArray(len); + serializeContents(value, jgen, provider); + jgen.writeEndArray(); + } + + @Override + public void serializeContents(int[] value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + for (int i = 0, len = value.length; i < len; ++i) { + jgen.writeNumber(value[i]); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("array", true).set("items", createSchemaNode("integer")); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.INTEGER); + } + } + + @JacksonStdImpl + public static class LongArraySerializer extends TypedPrimitiveArraySerializer + { + // as above, assuming no one re-defines primitive/wrapper types + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Long.TYPE); + + public LongArraySerializer() { super(long[].class); } + public LongArraySerializer(LongArraySerializer src, BeanProperty prop, + TypeSerializer vts, Boolean unwrapSingle) { + super(src, prop, vts, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop,Boolean unwrapSingle) { + return new LongArraySerializer(this, prop, _valueTypeSerializer, unwrapSingle); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new LongArraySerializer(this, _property, vts, _unwrapSingle); + } + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary + return null; + } + + @Override + public boolean isEmpty(SerializerProvider prov, long[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(long[] value) { + return (value.length == 1); + } + + @Override + public final void serialize(long[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, jgen, provider); + return; + } + } + jgen.writeStartArray(len); + serializeContents(value, jgen, provider); + jgen.writeEndArray(); + } + + @Override + public void serializeContents(long[] value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + if (_valueTypeSerializer != null) { + for (int i = 0, len = value.length; i < len; ++i) { + _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Long.TYPE); + jgen.writeNumber(value[i]); + _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen); + } + return; + } + + for (int i = 0, len = value.length; i < len; ++i) { + jgen.writeNumber(value[i]); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + return createSchemaNode("array", true) + .set("items", createSchemaNode("number", true)); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.NUMBER); + } + } + + @JacksonStdImpl + public static class FloatArraySerializer extends TypedPrimitiveArraySerializer + { + // as above, assuming no one re-defines primitive/wrapper types + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Float.TYPE); + + public FloatArraySerializer() { + super(float[].class); + } + public FloatArraySerializer(FloatArraySerializer src, BeanProperty prop, + TypeSerializer vts, Boolean unwrapSingle) { + super(src, prop, vts, unwrapSingle); + } + + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return new FloatArraySerializer(this, _property, vts, _unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop,Boolean unwrapSingle) { + return new FloatArraySerializer(this, prop, _valueTypeSerializer, unwrapSingle); + } + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary + return null; + } + + @Override + public boolean isEmpty(SerializerProvider prov, float[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(float[] value) { + return (value.length == 1); + } + + @Override + public final void serialize(float[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(float[] value, JsonGenerator gen, SerializerProvider provider) + throws IOException, JsonGenerationException + { + if (_valueTypeSerializer != null) { + for (int i = 0, len = value.length; i < len; ++i) { + _valueTypeSerializer.writeTypePrefixForScalar(null, gen, Float.TYPE); + gen.writeNumber(value[i]); + _valueTypeSerializer.writeTypeSuffixForScalar(null, gen); + } + return; + } + for (int i = 0, len = value.length; i < len; ++i) { + gen.writeNumber(value[i]); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("array", true).set("items", createSchemaNode("number")); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.NUMBER); + } + } + + @JacksonStdImpl + public static class DoubleArraySerializer extends ArraySerializerBase + { + // as above, assuming no one re-defines primitive/wrapper types + private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Double.TYPE); + + public DoubleArraySerializer() { super(double[].class); } + + /** + * @since 2.6 + */ + protected DoubleArraySerializer(DoubleArraySerializer src, + BeanProperty prop, Boolean unwrapSingle) { + super(src, prop, unwrapSingle); + } + + @Override + public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { + return new DoubleArraySerializer(this, prop, unwrapSingle); + } + + /** + * Doubles never add type info; hence, even if type serializer is suggested, + * we'll ignore it... + */ + @Override + public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return this; + } + + @Override + public JavaType getContentType() { + return VALUE_TYPE; + } + + @Override + public JsonSerializer getContentSerializer() { + // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary + return null; + } + + @Override + public boolean isEmpty(SerializerProvider prov, double[] value) { + return (value == null) || (value.length == 0); + } + + @Override + public boolean hasSingleElement(double[] value) { + return (value.length == 1); + } + + @Override + public final void serialize(double[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + final int len = value.length; + if (len == 1) { + if (((_unwrapSingle == null) && + provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) + || (_unwrapSingle == Boolean.TRUE)) { + serializeContents(value, gen, provider); + return; + } + } + gen.writeStartArray(len); + serializeContents(value, gen, provider); + gen.writeEndArray(); + } + + @Override + public void serializeContents(double[] value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + for (int i = 0, len = value.length; i < len; ++i) { + gen.writeNumber(value[i]); + } + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("array", true).set("items", createSchemaNode("number")); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitArrayFormat(visitor, typeHint, JsonFormatTypes.NUMBER); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,278 @@ +package com.fasterxml.jackson.databind.ser.std; + +import com.fasterxml.jackson.core.JsonGenerator; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.ResolvableSerializer; +import com.fasterxml.jackson.databind.util.Converter; + +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * Serializer implementation where given Java type is first converted + * to an intermediate "delegate type" (using a configured + * {@link Converter}, and then this delegate value is serialized by Jackson. + *

+ * Note that although types may be related, they must not be same; trying + * to do this will result in an exception. + * + * @since 2.1 + */ +@SuppressWarnings("serial") +public class StdDelegatingSerializer + extends StdSerializer + implements ContextualSerializer, ResolvableSerializer, + JsonFormatVisitable, SchemaAware +{ + protected final Converter _converter; + + /** + * Fully resolved delegate type, with generic information if any available. + */ + protected final JavaType _delegateType; + + /** + * Underlying serializer for type T. + */ + protected final JsonSerializer _delegateSerializer; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + @SuppressWarnings("unchecked") + public StdDelegatingSerializer(Converter converter) + { + super(Object.class); + _converter = (Converter)converter; + _delegateType = null; + _delegateSerializer = null; + } + + @SuppressWarnings("unchecked") + public StdDelegatingSerializer(Class cls, Converter converter) + { + super(cls, false); + _converter = (Converter)converter; + _delegateType = null; + _delegateSerializer = null; + } + + @SuppressWarnings("unchecked") + public StdDelegatingSerializer(Converter converter, + JavaType delegateType, JsonSerializer delegateSerializer) + { + super(delegateType); + _converter = converter; + _delegateType = delegateType; + _delegateSerializer = (JsonSerializer) delegateSerializer; + } + + /** + * Method used for creating resolved contextual instances. Must be + * overridden when sub-classing. + */ + protected StdDelegatingSerializer withDelegate(Converter converter, + JavaType delegateType, JsonSerializer delegateSerializer) + { + if (getClass() != StdDelegatingSerializer.class) { + throw new IllegalStateException("Sub-class "+getClass().getName()+" must override 'withDelegate'"); + } + return new StdDelegatingSerializer(converter, delegateType, delegateSerializer); + } + + /* + /********************************************************** + /* Contextualization + /********************************************************** + */ + + @Override + public void resolve(SerializerProvider provider) throws JsonMappingException + { + if ((_delegateSerializer != null) + && (_delegateSerializer instanceof ResolvableSerializer)) { + ((ResolvableSerializer) _delegateSerializer).resolve(provider); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) + throws JsonMappingException + { + JsonSerializer delSer = _delegateSerializer; + JavaType delegateType = _delegateType; + + if (delSer == null) { + // Otherwise, need to locate serializer to delegate to. For that we need type information... + if (delegateType == null) { + delegateType = _converter.getOutputType(provider.getTypeFactory()); + } + /* 02-Apr-2015, tatu: For "dynamic case", where type is only specified as + * java.lang.Object (or missing generic), [databind#731] + */ + if (!delegateType.isJavaLangObject()) { + delSer = provider.findValueSerializer(delegateType); + } + } + if (delSer instanceof ContextualSerializer) { + delSer = provider.handleSecondaryContextualization(delSer, property); + } + if (delSer == _delegateSerializer && delegateType == _delegateType) { + return this; + } + return withDelegate(_converter, delegateType, delSer); + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + protected Converter getConverter() { + return _converter; + } + + @Override + public JsonSerializer getDelegatee() { + return _delegateSerializer; + } + + /* + /********************************************************** + /* Serialization + /********************************************************** + */ + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + Object delegateValue = convertValue(value); + // should we accept nulls? + if (delegateValue == null) { + provider.defaultSerializeNull(gen); + return; + } + // 02-Apr-2015, tatu: As per [databind#731] may need to do dynamic lookup + JsonSerializer ser = _delegateSerializer; + if (ser == null) { + ser = _findSerializer(delegateValue, provider); + } + ser.serialize(delegateValue, gen, provider); + } + + @Override + public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + /* 03-Oct-2012, tatu: This is actually unlikely to work ok... but for now, + * let's give it a chance? + */ + Object delegateValue = convertValue(value); + JsonSerializer ser = _delegateSerializer; + if (ser == null) { + ser = _findSerializer(value, provider); + } + ser.serializeWithType(delegateValue, gen, provider, typeSer); + } + + @Override + @Deprecated // since 2.5 + public boolean isEmpty(Object value) { + return isEmpty(null, value); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Object value) + { + Object delegateValue = convertValue(value); + if (_delegateSerializer == null) { // best we can do for now, too costly to look up + return (value == null); + } + return _delegateSerializer.isEmpty(prov, delegateValue); + } + + /* + /********************************************************** + /* Schema functionality + /********************************************************** + */ + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + if (_delegateSerializer instanceof SchemaAware) { + return ((SchemaAware) _delegateSerializer).getSchema(provider, typeHint); + } + return super.getSchema(provider, typeHint); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint, + boolean isOptional) throws JsonMappingException + { + if (_delegateSerializer instanceof SchemaAware) { + return ((SchemaAware) _delegateSerializer).getSchema(provider, typeHint, isOptional); + } + return super.getSchema(provider, typeHint); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + /* 03-Sep-2012, tatu: Not sure if this can be made to really work + * properly... but for now, try this: + */ + // 02-Apr-2015, tatu: For dynamic case, very little we can do + if (_delegateSerializer != null) { + _delegateSerializer.acceptJsonFormatVisitor(visitor, typeHint); + } + } + + /* + /********************************************************** + /* Overridable methods + /********************************************************** + */ + + /** + * Method called to convert from source Java value into delegate + * value (which will be serialized using standard Jackson serializer for delegate type) + *

+ * The default implementation uses configured {@link Converter} to do + * conversion. + * + * @param value Value to convert + * + * @return Result of conversion + */ + protected Object convertValue(Object value) { + return _converter.convert(value); + } + + /** + * Helper method used for locating serializer to use in dynamic use case, where + * actual type value gets converted to is not specified beyond basic + * {@link java.lang.Object}, and where serializer needs to be located dynamically + * based on actual value type. + * + * @since 2.6 + */ + protected JsonSerializer _findSerializer(Object value, SerializerProvider serializers) + throws JsonMappingException + { + // NOTE: will NOT call contextualization + return serializers.findValueSerializer(value.getClass()); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,140 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.*; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.atomic.*; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.ser.BasicSerializerFactory; + +/** + * Class that providers access to serializers user for non-structured JDK types that + * are serializer as scalars; some using basic {@link ToStringSerializer}, + * others explicit serializers. + */ +@SuppressWarnings("serial") +public class StdJdkSerializers +{ + /** + * Method called by {@link BasicSerializerFactory} to access + * all serializers this class provides. + */ + public static Collection, Object>> all() + { + HashMap,Object> sers = new HashMap,Object>(); + + // First things that 'toString()' can handle + final ToStringSerializer sls = ToStringSerializer.instance; + + sers.put(java.net.URL.class, sls); + sers.put(java.net.URI.class, sls); + + sers.put(Currency.class, sls); + sers.put(UUID.class, new UUIDSerializer()); + sers.put(java.util.regex.Pattern.class, sls); + sers.put(Locale.class, sls); + + // then atomic types (note: AtomicReference defined elsewhere) + sers.put(AtomicBoolean.class, AtomicBooleanSerializer.class); + sers.put(AtomicInteger.class, AtomicIntegerSerializer.class); + sers.put(AtomicLong.class, AtomicLongSerializer.class); + + // then other types that need specialized serializers + sers.put(File.class, FileSerializer.class); + sers.put(Class.class, ClassSerializer.class); + + // And then some stranger types... not 100% they are needed but: + sers.put(Void.class, NullSerializer.instance); + sers.put(Void.TYPE, NullSerializer.instance); + + // 09-Jan-2015, tatu: As per [databind#1073], let's try to guard against possibility + // of some environments missing `java.sql.` types + try { + // note: timestamps are very similar to java.util.Date, thus serialized as such + sers.put(java.sql.Timestamp.class, DateSerializer.instance); + + // leave some of less commonly used ones as lazy, no point in proactive construction + sers.put(java.sql.Date.class, SqlDateSerializer.class); + sers.put(java.sql.Time.class, SqlTimeSerializer.class); + } catch (NoClassDefFoundError e) { + // nothing much we can do here; could log, but probably not useful for now. + } + + return sers.entrySet(); + } + + /* + /********************************************************** + /* Serializers for atomic types + /********************************************************** + */ + + public static class AtomicBooleanSerializer + extends StdScalarSerializer + { + public AtomicBooleanSerializer() { super(AtomicBoolean.class, false); } + + @Override + public void serialize(AtomicBoolean value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { + gen.writeBoolean(value.get()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("boolean", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + visitor.expectBooleanFormat(typeHint); + } + } + + public static class AtomicIntegerSerializer + extends StdScalarSerializer + { + public AtomicIntegerSerializer() { super(AtomicInteger.class, false); } + + @Override + public void serialize(AtomicInteger value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { + gen.writeNumber(value.get()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("integer", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + visitIntFormat(visitor, typeHint, JsonParser.NumberType.INT); + } + } + + public static class AtomicLongSerializer + extends StdScalarSerializer + { + public AtomicLongSerializer() { super(AtomicLong.class, false); } + + @Override + public void serialize(AtomicLong value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { + gen.writeNumber(value.get()); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("integer", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitIntFormat(visitor, typeHint, JsonParser.NumberType.LONG); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,58 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Date; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * Specialized serializer that can be used as the generic key + * serializer, when serializing {@link java.util.Map}s to JSON + * Objects. + */ +@SuppressWarnings("serial") +public class StdKeySerializer extends StdSerializer +{ + public StdKeySerializer() { super(Object.class); } + + @Override + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + String str; + Class cls = value.getClass(); + + if (cls == String.class) { + str = (String) value; + } else if (cls.isEnum()) { + // 24-Sep-2015, tatu: Minor improvement over older (2.6.2 and before) code: at least + // use name/toString() variation for as per configuration + Enum en = (Enum) value; + + if (provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { + str = en.toString(); + } else { + str = en.name(); + } + } else if (value instanceof Date) { + provider.defaultSerializeDateKey((Date) value, g); + return; + } else if (cls == Class.class) { + str = ((Class) value).getName(); + } else { + str = value.toString(); + } + g.writeFieldName(str); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { + return createSchemaNode("string"); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + visitStringFormat(visitor, typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,205 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; + +@SuppressWarnings("serial") +public class StdKeySerializers +{ + protected final static JsonSerializer DEFAULT_KEY_SERIALIZER = new StdKeySerializer(); + + protected final static JsonSerializer DEFAULT_STRING_SERIALIZER = new StringKeySerializer(); + + private StdKeySerializers() { } + + /** + * @param config Serialization configuration in use, may be needed in choosing + * serializer to use + * @param rawKeyType Type of key values to serialize + * @param useDefault If no match is found, should we return fallback deserializer + * (true), or null (false)? + */ + public static JsonSerializer getStdKeySerializer(SerializationConfig config, + Class rawKeyType, boolean useDefault) + { + // 24-Sep-2015, tatu: Important -- should ONLY consider types for which `@JsonValue` + // can not be used, since caller has not yet checked for that annotation + // This is why Enum types are not handled here quite yet + + // [databind#943: Use a dynamic key serializer if we are not given actual + // type declaration + if ((rawKeyType == null) || (rawKeyType == Object.class)) { + return new Dynamic(); + } + if (rawKeyType == String.class) { + return DEFAULT_STRING_SERIALIZER; + } + if (rawKeyType.isPrimitive() || Number.class.isAssignableFrom(rawKeyType)) { + return DEFAULT_KEY_SERIALIZER; + } + if (rawKeyType == Class.class) { + return new Default(Default.TYPE_CLASS, rawKeyType); + } + if (Date.class.isAssignableFrom(rawKeyType)) { + return new Default(Default.TYPE_DATE, rawKeyType); + } + if (Calendar.class.isAssignableFrom(rawKeyType)) { + return new Default(Default.TYPE_CALENDAR, rawKeyType); + } + // other JDK types we know convert properly with 'toString()'? + if (rawKeyType == java.util.UUID.class) { + return new Default(Default.TYPE_TO_STRING, rawKeyType); + } + return useDefault ? DEFAULT_KEY_SERIALIZER : null; + } + + /** + * Method called if no specified key serializer was located; will return a + * "default" key serializer. + * + * @since 2.7 + */ + public static JsonSerializer getFallbackKeySerializer(SerializationConfig config, + Class rawKeyType) { + if (rawKeyType != null) { + // 29-Sep-2015, tatu: Odd case here, of `Enum`, which we may get for `EnumMap`; not sure + // if that is a bug or feature. Regardless, it seems to require dynamic handling + // (compared to getting actual fully typed Enum). + // Note that this might even work from the earlier point, but let's play it safe for now + if (rawKeyType == Enum.class) { + return new Dynamic(); + } + if (rawKeyType.isEnum()) { + return new Default(Default.TYPE_ENUM, rawKeyType); + } + } + return DEFAULT_KEY_SERIALIZER; + } + + /** + * @deprecated since 2.7 + */ + @Deprecated + public static JsonSerializer getDefault() { + return DEFAULT_KEY_SERIALIZER; + } + + /* + /********************************************************** + /* Standard implementations used + /********************************************************** + */ + + /** + * This is a "chameleon" style multi-type key serializer for simple + * standard JDK types. + *

+ * TODO: Should (but does not yet) support re-configuring format used for + * {@link java.util.Date} and {@link java.util.Calendar} key serializers, + * as well as alternative configuration of Enum key serializers. + */ + public static class Default extends StdSerializer { + final static int TYPE_DATE = 1; + final static int TYPE_CALENDAR = 2; + final static int TYPE_CLASS = 3; + final static int TYPE_ENUM = 4; + final static int TYPE_TO_STRING = 5; + + protected final int _typeId; + + public Default(int typeId, Class type) { + super(type, false); + _typeId = typeId; + } + + @Override + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + switch (_typeId) { + case TYPE_DATE: + provider.defaultSerializeDateKey((Date)value, g); + break; + case TYPE_CALENDAR: + provider.defaultSerializeDateKey(((Calendar) value).getTimeInMillis(), g); + break; + case TYPE_CLASS: + g.writeFieldName(((Class)value).getName()); + break; + case TYPE_ENUM: + { + String str = provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) + ? value.toString() : ((Enum) value).name(); + g.writeFieldName(str); + } + break; + case TYPE_TO_STRING: + default: + g.writeFieldName(value.toString()); + } + } + } + + /** + * Key serializer used when key type is not known statically, and actual key + * serializer needs to be dynamically located. + */ + public static class Dynamic extends StdSerializer + { + // Important: MUST be transient, to allow serialization of key serializer itself + protected transient PropertySerializerMap _dynamicSerializers; + + public Dynamic() { + super(String.class, false); + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + } + + Object readResolve() { + // Since it's transient, and since JDK serialization by-passes ctor, need this: + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + return this; + } + + @Override + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) + throws IOException + { + Class cls = value.getClass(); + PropertySerializerMap m = _dynamicSerializers; + JsonSerializer ser = m.serializerFor(cls); + if (ser == null) { + ser = _findAndAddDynamic(m, cls, provider); + } + ser.serialize(value, g, provider); + } + + protected JsonSerializer _findAndAddDynamic(PropertySerializerMap map, + Class type, SerializerProvider provider) throws JsonMappingException + { + PropertySerializerMap.SerializerAndMapResult result = + // null -> for now we won't keep ref or pass BeanProperty; could change + map.findAndAddKeySerializer(type, provider, null); + // did we get a new map of serializers? If so, start using it + if (map != result.map) { + _dynamicSerializers = result.map; + } + return result.serializer; + } + } + + /** + * Simple and fast key serializer when keys are Strings. + */ + public static class StringKeySerializer extends StdSerializer + { + public StringKeySerializer() { super(String.class, false); } + + @Override + public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException { + g.writeFieldName((String) value); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +@SuppressWarnings("serial") +public abstract class StdScalarSerializer + extends StdSerializer +{ + protected StdScalarSerializer(Class t) { + super(t); + } + + /** + * Alternate constructor that is (alas!) needed to work + * around kinks of generic type handling + */ + @SuppressWarnings("unchecked") + protected StdScalarSerializer(Class t, boolean dummy) { + super((Class) t); + } + + /** + * Default implementation will write type prefix, call regular serialization + * method (since assumption is that value itself does not need JSON + * Array or Object start/end markers), and then write type suffix. + * This should work for most cases; some sub-classes may want to + * change this behavior. + */ + @Override + public void serializeWithType(T value, JsonGenerator g, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + typeSer.writeTypePrefixForScalar(value, g); + serialize(value, g, provider); + typeSer.writeTypeSuffixForScalar(value, g); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + throws JsonMappingException + { + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + // 13-Sep-2013, tatu: Let's assume it's usually a String, right? + visitStringFormat(visitor, typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StdSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,524 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonParser.NumberType; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsonFormatVisitors.*; +import com.fasterxml.jackson.databind.jsonschema.SchemaAware; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.PropertyFilter; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.Converter; + +/** + * Base class used by all standard serializers, and can also + * be used for custom serializers (in fact, this is the recommended + * base class to use). + * Provides convenience methods for implementing {@link SchemaAware} + */ +public abstract class StdSerializer + extends JsonSerializer + implements JsonFormatVisitable, SchemaAware, java.io.Serializable +{ + /** + * Unique key we use to store a temporary lock, to prevent infinite recursion + * when resolving content converters (see [databind#357]). + *

+ * NOTE: may need to revisit this if nested content converters are needed; if so, + * may need to create per-call lock object. But let's start with a simpler + * solution for now. + * + * @since 2.7 + */ + private final static Object CONVERTING_CONTENT_CONVERTER_LOCK = new Object(); + + private static final long serialVersionUID = 1L; + + /** + * Nominal type supported, usually declared type of + * property for which serializer is used. + */ + protected final Class _handledType; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected StdSerializer(Class t) { + _handledType = t; + } + + @SuppressWarnings("unchecked") + protected StdSerializer(JavaType type) { + _handledType = (Class) type.getRawClass(); + } + + /** + * Alternate constructor that is (alas!) needed to work + * around kinks of generic type handling + */ + @SuppressWarnings("unchecked") + protected StdSerializer(Class t, boolean dummy) { + _handledType = (Class) t; + } + + /** + * @since 2.6 + */ + @SuppressWarnings("unchecked") + protected StdSerializer(StdSerializer src) { + _handledType = (Class) src._handledType; + } + + /* + /********************************************************** + /* Accessors + /********************************************************** + */ + + @Override + public Class handledType() { return _handledType; } + + /* + /********************************************************** + /* Serialization + /********************************************************** + */ + + @Override + public abstract void serialize(T value, JsonGenerator gen, SerializerProvider provider) + throws IOException; + + /* + /********************************************************** + /* Type introspection API, partial/default implementation + /********************************************************** + */ + + /** + * Default implementation specifies no format. This behavior is usually + * overriden by custom serializers. + */ + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + visitor.expectAnyFormat(typeHint); + } + + /** + * Default implementation simply claims type is "string"; usually + * overriden by custom serializers. + */ + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException + { + return createSchemaNode("string"); + } + + /** + * Default implementation simply claims type is "string"; usually + * overriden by custom serializers. + */ + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional) + throws JsonMappingException + { + ObjectNode schema = (ObjectNode) getSchema(provider, typeHint); + if (!isOptional) { + schema.put("required", !isOptional); + } + return schema; + } + + /* + /********************************************************** + /* Helper methods for JSON Schema generation + /********************************************************** + */ + + protected ObjectNode createObjectNode() { + return JsonNodeFactory.instance.objectNode(); + } + + protected ObjectNode createSchemaNode(String type) + { + ObjectNode schema = createObjectNode(); + schema.put("type", type); + return schema; + } + + protected ObjectNode createSchemaNode(String type, boolean isOptional) + { + ObjectNode schema = createSchemaNode(type); + if (!isOptional) { + schema.put("required", !isOptional); + } + return schema; + } + + /** + * Helper method that calls necessary visit method(s) to indicate that the + * underlying JSON type is JSON String. + * + * @since 2.7 + */ + protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException { + if (visitor != null) { + /*JsonStringFormatVisitor v2 =*/ visitor.expectStringFormat(typeHint); + } + } + + /** + * Helper method that calls necessary visit method(s) to indicate that the + * underlying JSON type is JSON String, but that there is a more refined + * logical type + * + * @since 2.7 + */ + protected void visitStringFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, + JsonValueFormat format) + throws JsonMappingException + { + if (visitor != null) { + JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint); + if (v2 != null) { + v2.format(format); + } + } + } + + /** + * Helper method that calls necessary visit method(s) to indicate that the + * underlying JSON type is JSON Integer number. + * + * @since 2.7 + */ + protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, + NumberType numberType) + throws JsonMappingException + { + if (visitor != null) { + JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint); + if (v2 != null) { + if (numberType != null) { + v2.numberType(numberType); + } + } + } + } + + /** + * Helper method that calls necessary visit method(s) to indicate that the + * underlying JSON type is JSON Integer number, but that there is also a further + * format restriction involved. + * + * @since 2.7 + */ + protected void visitIntFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, + NumberType numberType, JsonValueFormat format) + throws JsonMappingException + { + if (visitor != null) { + JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint); + if (v2 != null) { + if (numberType != null) { + v2.numberType(numberType); + } + if (format != null) { + v2.format(format); + } + } + } + } + + /** + * Helper method that calls necessary visit method(s) to indicate that the + * underlying JSON type is a floating-point JSON number. + * + * @since 2.7 + */ + protected void visitFloatFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, + NumberType numberType) + throws JsonMappingException + { + if (visitor != null) { + JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint); + if (v2 != null) { + v2.numberType(numberType); + } + } + } + + /** + * @since 2.7 + */ + protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, + JsonSerializer itemSerializer, JavaType itemType) + throws JsonMappingException + { + if (visitor != null) { + JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); + if (v2 != null) { + if (itemSerializer != null) { + v2.itemsFormat(itemSerializer, itemType); + } + } + } + } + + /** + * @since 2.7 + */ + protected void visitArrayFormat(JsonFormatVisitorWrapper visitor, JavaType typeHint, + JsonFormatTypes itemType) + throws JsonMappingException + { + if (visitor != null) { + JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); + if (v2 != null) { + v2.itemsFormat(itemType); + } + } + } + + /* + /********************************************************** + /* Helper methods for exception handling + /********************************************************** + */ + + /** + * Method that will modify caught exception (passed in as argument) + * as necessary to include reference information, and to ensure it + * is a subtype of {@link IOException}, or an unchecked exception. + *

+ * Rules for wrapping and unwrapping are bit complicated; essentially: + *

    + *
  • Errors are to be passed as is (if uncovered via unwrapping) + *
  • "Plain" IOExceptions (ones that are not of type + * {@link JsonMappingException} are to be passed as is + *
+ */ + public void wrapAndThrow(SerializerProvider provider, + Throwable t, Object bean, String fieldName) + throws IOException + { + /* 05-Mar-2009, tatu: But one nasty edge is when we get + * StackOverflow: usually due to infinite loop. But that + * usually gets hidden within an InvocationTargetException... + */ + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors and "plain" IOExceptions to be passed as is + if (t instanceof Error) { + throw (Error) t; + } + // Ditto for IOExceptions... except for mapping exceptions! + boolean wrap = (provider == null) || provider.isEnabled(SerializationFeature.WRAP_EXCEPTIONS); + if (t instanceof IOException) { + if (!wrap || !(t instanceof JsonMappingException)) { + throw (IOException) t; + } + } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + } + // Need to add reference information + throw JsonMappingException.wrapWithPath(t, bean, fieldName); + } + + public void wrapAndThrow(SerializerProvider provider, + Throwable t, Object bean, int index) + throws IOException + { + while (t instanceof InvocationTargetException && t.getCause() != null) { + t = t.getCause(); + } + // Errors are to be passed as is + if (t instanceof Error) { + throw (Error) t; + } + // Ditto for IOExceptions... except for mapping exceptions! + boolean wrap = (provider == null) || provider.isEnabled(SerializationFeature.WRAP_EXCEPTIONS); + if (t instanceof IOException) { + if (!wrap || !(t instanceof JsonMappingException)) { + throw (IOException) t; + } + } else if (!wrap) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + } + // Need to add reference information + throw JsonMappingException.wrapWithPath(t, bean, index); + } + + /* + /********************************************************** + /* Helper methods, accessing annotation-based configuration + /********************************************************** + */ + + /** + * Helper method that can be used to see if specified property has annotation + * indicating that a converter is to be used for contained values (contents + * of structured types; array/List/Map values) + * + * @param existingSerializer (optional) configured content + * serializer if one already exists. + * + * @since 2.2 + */ + protected JsonSerializer findConvertingContentSerializer(SerializerProvider provider, + BeanProperty prop, JsonSerializer existingSerializer) + throws JsonMappingException + { + /* 19-Oct-2014, tatu: As per [databind#357], need to avoid infinite loop + * when applying contextual content converter; this is not ideal way, + * but should work for most cases. + */ + Object ob = provider.getAttribute(CONVERTING_CONTENT_CONVERTER_LOCK); + if (ob != null) { + if (ob == Boolean.TRUE) { // just to ensure it's value we added. + return existingSerializer; + } + } + + final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); + if (intr != null && prop != null) { + AnnotatedMember m = prop.getMember(); + if (m != null) { + provider.setAttribute(CONVERTING_CONTENT_CONVERTER_LOCK, Boolean.TRUE); + Object convDef; + try { + convDef = intr.findSerializationContentConverter(m); + } finally { + provider.setAttribute(CONVERTING_CONTENT_CONVERTER_LOCK, null); + } + if (convDef != null) { + Converter conv = provider.converterInstance(prop.getMember(), convDef); + JavaType delegateType = conv.getOutputType(provider.getTypeFactory()); + // [databind#731]: Should skip if nominally java.lang.Object + if ((existingSerializer == null) && !delegateType.isJavaLangObject()) { + existingSerializer = provider.findValueSerializer(delegateType); + } + return new StdDelegatingSerializer(conv, delegateType, existingSerializer); + } + } + } + return existingSerializer; + } + + /** + * Helper method used to locate filter that is needed, based on filter id + * this serializer was constructed with. + * + * @since 2.3 + */ + protected PropertyFilter findPropertyFilter(SerializerProvider provider, + Object filterId, Object valueToFilter) + throws JsonMappingException + { + FilterProvider filters = provider.getFilterProvider(); + // Not ok to miss the provider, if a filter is declared to be needed. + if (filters == null) { + throw JsonMappingException.from(provider, + "Can not resolve PropertyFilter with id '"+filterId+"'; no FilterProvider configured"); + } + PropertyFilter filter = filters.findPropertyFilter(filterId, valueToFilter); + // But whether unknown ids are ok just depends on filter provider; if we get null that's fine + return filter; + } + + /** + * Helper method that may be used to find if this deserializer has specific + * {@link JsonFormat} settings, either via property, or through type-specific + * defaulting. + * + * @param typeForDefaults Type (erased) used for finding default format settings, if any + * + * @since 2.7 + */ + protected JsonFormat.Value findFormatOverrides(SerializerProvider provider, + BeanProperty prop, Class typeForDefaults) + { + if (prop != null) { + return prop.findPropertyFormat(provider.getConfig(), typeForDefaults); + } + // even without property or AnnotationIntrospector, may have type-specific defaults + return provider.getDefaultPropertyFormat(typeForDefaults); + } + + /** + * Convenience method that uses {@link #findFormatOverrides} to find possible + * defaults and/of overrides, and then calls JsonFormat.Value.getFeature(...) + * to find whether that feature has been specifically marked as enabled or disabled. + * + * @param typeForDefaults Type (erased) used for finding default format settings, if any + * + * @since 2.7 + */ + protected Boolean findFormatFeature(SerializerProvider provider, + BeanProperty prop, Class typeForDefaults, JsonFormat.Feature feat) + { + JsonFormat.Value format = findFormatOverrides(provider, prop, typeForDefaults); + if (format != null) { + return format.getFeature(feat); + } + return null; + } + + /** + * Convenience method for finding out possibly configured content value serializer. + * + * @since 2.7.4 + */ + protected JsonSerializer findAnnotatedContentSerializer(SerializerProvider serializers, + BeanProperty property) + throws JsonMappingException + { + if (property != null) { + // First: if we have a property, may have property-annotation overrides + AnnotatedMember m = property.getMember(); + final AnnotationIntrospector intr = serializers.getAnnotationIntrospector(); + if (m != null) { + Object serDef = intr.findContentSerializer(m); + if (serDef != null) { + return serializers.serializerInstance(m, serDef); + } + } + } + return null; + } + + /* + /********************************************************** + /* Helper methods, other + /********************************************************** + */ + + /** + * Method that can be called to determine if given serializer is the default + * serializer Jackson uses; as opposed to a custom serializer installed by + * a module or calling application. Determination is done using + * {@link JacksonStdImpl} annotation on serializer class. + */ + protected boolean isDefaultSerializer(JsonSerializer serializer) { + return ClassUtil.isJacksonStdImpl(serializer); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StringSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StringSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/StringSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; + +/** + * This is the special serializer for regular {@link java.lang.String}s. + *

+ * Since this is one of "native" types, no type information is ever + * included on serialization (unlike for most scalar types as of 1.5) + */ +@JacksonStdImpl +public final class StringSerializer +// NOTE: generic parameter changed from String to Object in 2.6, to avoid +// use of bridge methods + extends NonTypedScalarSerializerBase +{ + private static final long serialVersionUID = 1L; + + public StringSerializer() { super(String.class, false); } + + /** + * For Strings, both null and Empty String qualify for emptiness. + */ + @Override + @Deprecated + public boolean isEmpty(Object value) { + String str = (String) value; + return (str == null) || (str.length() == 0); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Object value) { + String str = (String) value; + return (str == null) || (str.length() == 0); + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString((String) value); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) { + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { + visitStringFormat(visitor, typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,27 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.TimeZone; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +@SuppressWarnings("serial") +public class TimeZoneSerializer extends StdScalarSerializer +{ + public TimeZoneSerializer() { super(TimeZone.class); } + + @Override + public void serialize(TimeZone value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeString(value.getID()); + } + + @Override + public void serializeWithType(TimeZone value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { + // Better ensure we don't use specific sub-classes: + typeSer.writeTypePrefixForScalar(value, jgen, TimeZone.class); + serialize(value, jgen, provider); + typeSer.writeTypeSuffixForScalar(value, jgen); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,102 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Simple general purpose serializer, useful for any + * type for which {@link Object#toString} returns the desired JSON + * value. + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class ToStringSerializer + extends StdSerializer +{ + /** + * Singleton instance to use. + */ + public final static ToStringSerializer instance = new ToStringSerializer(); + + /** + *

+ * Note: usually you should NOT create new instances, but instead use + * {@link #instance} which is stateless and fully thread-safe. However, + * there are cases where constructor is needed; for example, + * when using explicit serializer annotations like + * {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}. + */ + public ToStringSerializer() { super(Object.class); } + + /** + * Sometimes it may actually make sense to retain actual handled type, so... + * + * @since 2.5 + */ + public ToStringSerializer(Class handledType) { + super(handledType, false); + } + + @Override + @Deprecated + public boolean isEmpty(Object value) { + return isEmpty(null, value); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Object value) { + if (value == null) { + return true; + } + String str = value.toString(); + return str.isEmpty(); + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + gen.writeString(value.toString()); + } + + /* 01-Mar-2011, tatu: We were serializing as "raw" String; but generally that + * is not what we want, since lack of type information would imply real + * String type. + */ + /** + * Default implementation will write type prefix, call regular serialization + * method (since assumption is that value itself does not need JSON + * Array or Object start/end markers), and then write type suffix. + * This should work for most cases; some sub-classes may want to + * change this behavior. + */ + @Override + public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException + { + typeSer.writeTypePrefixForScalar(value, gen); + serialize(value, gen, provider); + typeSer.writeTypeSuffixForScalar(value, gen); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { + return createSchemaNode("string", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException + { + visitStringFormat(visitor, typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,71 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * We also want to directly support serialization of {@link TokenBuffer}; + * and since it is part of core package, it can not implement + * {@link com.fasterxml.jackson.databind.JsonSerializable} + * (which is only included in the mapper package) + */ +@JacksonStdImpl +@SuppressWarnings("serial") +public class TokenBufferSerializer + extends StdSerializer +{ + public TokenBufferSerializer() { super(TokenBuffer.class); } + + @Override + public void serialize(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider) + throws IOException + { + value.serialize(jgen); + } + + /** + * Implementing typed output for contents of a TokenBuffer is very tricky, + * since we do not know for sure what its contents might look like (or, rather, + * we do know when serializing, but not necessarily when deserializing!) + * One possibility would be to check the current token, and use that to + * determine if we would output JSON Array, Object or scalar value. + *

+ * Note that we just claim it is scalar; this should work ok and is simpler + * than doing introspection on both serialization and deserialization. + */ + @Override + public final void serializeWithType(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider, + TypeSerializer typeSer) throws IOException + { + typeSer.writeTypePrefixForScalar(value, jgen); + serialize(value, jgen, provider); + typeSer.writeTypeSuffixForScalar(value, jgen); + } + + @Override + public JsonNode getSchema(SerializerProvider provider, Type typeHint) + { + // Not 100% sure what we should say here: type is basically not known. + // This seems like closest approximation + return createSchemaNode("any", true); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + throws JsonMappingException + { + // Not 100% sure what we should say here: type is basically not known. + // This seems like closest approximation + visitor.expectAnyFormat(typeHint); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/ser/std/UUIDSerializer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,112 @@ +package com.fasterxml.jackson.databind.ser.std; + +import java.io.IOException; +import java.util.UUID; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.util.TokenBuffer; + +/** + * Specialized {@link JsonSerializer} to output {@link java.util.UUID}s. + * Beyond optimized access and writing of textual representation (which + * is the default handling in most cases), it will alternatively + * allow serialization using raw binary output (as 16-byte block) + * if underlying data format has efficient means to access that. + */ +@SuppressWarnings("serial") +public class UUIDSerializer + extends StdScalarSerializer +{ + final static char[] HEX_CHARS = "0123456789abcdef".toCharArray(); + + public UUIDSerializer() { super(UUID.class); } + + @Override + public boolean isEmpty(SerializerProvider prov, UUID value) + { + if (value == null) { + return true; + } + // Null UUID is empty, so... + if (value.getLeastSignificantBits() == 0L + && value.getMostSignificantBits() == 0L) { + return true; + } + return false; + } + + @Override + public void serialize(UUID value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + // First: perhaps we could serialize it as raw binary data? + if (gen.canWriteBinaryNatively()) { + /* 07-Dec-2013, tatu: One nasty case; that of TokenBuffer. While it can + * technically retain binary data, we do not want to do use binary + * with it, as that results in UUIDs getting converted to Base64 for + * most conversions. + */ + if (!(gen instanceof TokenBuffer)) { + gen.writeBinary(_asBytes(value)); + return; + } + } + + // UUID.toString() works ok functionally, but we can make it go much faster + // (by 4x with micro-benchmark) + + final char[] ch = new char[36]; + final long msb = value.getMostSignificantBits(); + _appendInt((int) (msb >> 32), ch, 0); + ch[8] = '-'; + int i = (int) msb; + _appendShort(i >>> 16, ch, 9); + ch[13] = '-'; + _appendShort(i, ch, 14); + ch[18] = '-'; + + final long lsb = value.getLeastSignificantBits(); + _appendShort((int) (lsb >>> 48), ch, 19); + ch[23] = '-'; + _appendShort((int) (lsb >>> 32), ch, 24); + _appendInt((int) lsb, ch, 28); + + gen.writeString(ch, 0, 36); + } + + private static void _appendInt(int bits, char[] ch, int offset) + { + _appendShort(bits >> 16, ch, offset); + _appendShort(bits, ch, offset+4); + } + + private static void _appendShort(int bits, char[] ch, int offset) + { + ch[offset] = HEX_CHARS[(bits >> 12) & 0xF]; + ch[++offset] = HEX_CHARS[(bits >> 8) & 0xF]; + ch[++offset] = HEX_CHARS[(bits >> 4) & 0xF]; + ch[++offset] = HEX_CHARS[bits & 0xF]; + + } + + private final static byte[] _asBytes(UUID uuid) + { + byte[] buffer = new byte[16]; + long hi = uuid.getMostSignificantBits(); + long lo = uuid.getLeastSignificantBits(); + _appendInt((int) (hi >> 32), buffer, 0); + _appendInt((int) hi, buffer, 4); + _appendInt((int) (lo >> 32), buffer, 8); + _appendInt((int) lo, buffer, 12); + return buffer; + } + + private final static void _appendInt(int value, byte[] buffer, int offset) + { + buffer[offset] = (byte) (value >> 24); + buffer[++offset] = (byte) (value >> 16); + buffer[++offset] = (byte) (value >> 8); + buffer[++offset] = (byte) value; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ArrayType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ArrayType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ArrayType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,218 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.Array; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Array types represent Java arrays, both primitive and object valued. + * Further, Object-valued arrays can have element type of any other + * legal {@link JavaType}. + */ +public final class ArrayType + extends TypeBase +{ + private static final long serialVersionUID = 1L; + + /** + * Type of elements in the array. + */ + protected final JavaType _componentType; + + /** + * We will also keep track of shareable instance of empty array, + * since it usually needs to be constructed any way; and because + * it is essentially immutable and thus can be shared. + */ + protected final Object _emptyArray; + + protected ArrayType(JavaType componentType, TypeBindings bindings, Object emptyInstance, + Object valueHandler, Object typeHandler, boolean asStatic) + { + // No super-class, interfaces, for now + super(emptyInstance.getClass(), bindings, null, null, + componentType.hashCode(), + valueHandler, typeHandler, asStatic); + _componentType = componentType; + _emptyArray = emptyInstance; + } + + public static ArrayType construct(JavaType componentType, TypeBindings bindings) { + return construct(componentType, bindings, null, null); + } + + public static ArrayType construct(JavaType componentType, TypeBindings bindings, + Object valueHandler, Object typeHandler) { + // Figuring out raw class for generic array is actually bit tricky... + Object emptyInstance = Array.newInstance(componentType.getRawClass(), 0); + return new ArrayType(componentType, bindings, emptyInstance, valueHandler, typeHandler, false); + } + + @Override + public JavaType withContentType(JavaType contentType) { + Object emptyInstance = Array.newInstance(contentType.getRawClass(), 0); + return new ArrayType(contentType, _bindings, emptyInstance, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public ArrayType withTypeHandler(Object h) + { + if (h == _typeHandler) { + return this; + } + return new ArrayType(_componentType, _bindings, _emptyArray, _valueHandler, h, _asStatic); + } + + @Override + public ArrayType withContentTypeHandler(Object h) + { + if (h == _componentType.getTypeHandler()) { + return this; + } + return new ArrayType(_componentType.withTypeHandler(h), _bindings, _emptyArray, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public ArrayType withValueHandler(Object h) { + if (h == _valueHandler) { + return this; + } + return new ArrayType(_componentType, _bindings, _emptyArray, h, _typeHandler,_asStatic); + } + + @Override + public ArrayType withContentValueHandler(Object h) { + if (h == _componentType.getValueHandler()) { + return this; + } + return new ArrayType(_componentType.withValueHandler(h), _bindings, _emptyArray, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public ArrayType withStaticTyping() { + if (_asStatic) { + return this; + } + return new ArrayType(_componentType.withStaticTyping(), _bindings, + _emptyArray, _valueHandler, _typeHandler, true); + } + + /* + /********************************************************** + /* Methods for narrowing conversions + /********************************************************** + */ + + /** + * Handling of narrowing conversions for arrays is trickier: for now, + * it is not even allowed. + */ + @Override + @Deprecated // since 2.7 + protected JavaType _narrow(Class subclass) { + return _reportUnsupported(); + } + + // Should not be called, as array types in Java are not extensible; but + // let's not freak out even if it is called? + @Override + public JavaType refine(Class contentClass, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return null; + } + + private JavaType _reportUnsupported() { + throw new UnsupportedOperationException("Can not narrow or widen array types"); + } + + /* + /********************************************************** + /* Overridden methods + /********************************************************** + */ + + @Override + public boolean isArrayType() { return true; } + + /** + * For some odd reason, modifiers for array classes would + * claim they are abstract types. Not so, at least for our + * purposes. + */ + @Override + public boolean isAbstract() { return false; } + + /** + * For some odd reason, modifiers for array classes would + * claim they are abstract types. Not so, at least for our + * purposes. + */ + @Override + public boolean isConcrete() { return true; } + + @Override + public boolean hasGenericTypes() { + // arrays are not parameterized, but element type may be: + return _componentType.hasGenericTypes(); + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + @Override + public boolean isContainerType() { return true; } + + @Override + public JavaType getContentType() { return _componentType; } + + @Override + public Object getContentValueHandler() { + return _componentType.getValueHandler(); + } + + @Override + public Object getContentTypeHandler() { + return _componentType.getTypeHandler(); + } + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) { + sb.append('['); + return _componentType.getGenericSignature(sb); + } + + @Override + public StringBuilder getErasedSignature(StringBuilder sb) { + sb.append('['); + return _componentType.getErasedSignature(sb); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public String toString() + { + return "[array type, component type: "+_componentType+"]"; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + + ArrayType other = (ArrayType) o; + return _componentType.equals(other._componentType); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ClassKey.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ClassKey.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ClassKey.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,97 @@ +package com.fasterxml.jackson.databind.type; + +/** + * Key class, used as an efficient and accurate key + * for locating per-class values, such as + * {@link com.fasterxml.jackson.databind.JsonSerializer}s. + *

+ * The reason for having a separate key class instead of + * directly using {@link Class} as key is mostly + * to allow for redefining hashCode method -- + * for some strange reason, {@link Class} does not + * redefine {@link Object#hashCode} and thus uses identity + * hash, which is pretty slow. This makes key access using + * {@link Class} unnecessarily slow. + *

+ * Note: since class is not strictly immutable, caller must + * know what it is doing, if changing field values. + */ +public final class ClassKey + implements Comparable, + java.io.Serializable // since 2.1 +{ + private static final long serialVersionUID = 1L; + + private String _className; + + private Class _class; + + /** + * Let's cache hash code straight away, since we are + * almost certain to need it. + */ + private int _hashCode; + + public ClassKey() + { + _class = null; + _className = null; + _hashCode = 0; + } + + public ClassKey(Class clz) + { + _class = clz; + _className = clz.getName(); + _hashCode = _className.hashCode(); + } + + public void reset(Class clz) + { + _class = clz; + _className = clz.getName(); + _hashCode = _className.hashCode(); + } + + /* + /********************************************************** + /* Comparable + /********************************************************** + */ + + @Override + public int compareTo(ClassKey other) + { + // Just need to sort by name, ok to collide (unless used in TreeMap/Set!) + return _className.compareTo(other._className); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + ClassKey other = (ClassKey) o; + + /* Is it possible to have different Class object for same name + class loader combo? + * Let's assume answer is no: if this is wrong, will need to uncomment following functionality + */ + /* + return (other._className.equals(_className)) + && (other._class.getClassLoader() == _class.getClassLoader()); + */ + return other._class == _class; + } + + @Override public int hashCode() { return _hashCode; } + + @Override public String toString() { return _className; } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ClassStack.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ClassStack.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ClassStack.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,86 @@ +package com.fasterxml.jackson.databind.type; + +import java.util.ArrayList; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Simple helper class used to keep track of 'call stack' for classes being referenced + * (as well as unbound variables) + * + * @since 2.7 + */ +public final class ClassStack +{ + protected final ClassStack _parent; + protected final Class _current; + + private ArrayList _selfRefs; + + public ClassStack(Class rootType) { + this(null, rootType); + } + + private ClassStack(ClassStack parent, Class curr) { + _parent = parent; + _current = curr; + } + + /** + * @return New stack frame, if addition is ok; null if not + */ + public ClassStack child(Class cls) { + return new ClassStack(this, cls); + } + + /** + * Method called to indicate that there is a self-reference from + * deeper down in stack pointing into type this stack frame represents. + */ + public void addSelfReference(ResolvedRecursiveType ref) + { + if (_selfRefs == null) { + _selfRefs = new ArrayList(); + } + _selfRefs.add(ref); + } + + /** + * Method called when type that this stack frame represents is + * fully resolved, allowing self-references to be completed + * (if there are any) + */ + public void resolveSelfReferences(JavaType resolved) + { + if (_selfRefs != null) { + for (ResolvedRecursiveType ref : _selfRefs) { + ref.setReference(resolved); + } + } + } + + public ClassStack find(Class cls) + { + if (_current == cls) return this; + for (ClassStack curr = _parent; curr != null; curr = curr._parent) { + if (curr._current == cls) { + return curr; + } + } + return null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[ClassStack (self-refs: ") + .append((_selfRefs == null) ? "0" : String.valueOf(_selfRefs.size())) + .append(')') + ; + for (ClassStack curr = this; curr != null; curr = curr._parent) { + sb.append(' ').append(curr._current.getName()); + } + sb.append(']'); + return sb.toString(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/CollectionLikeType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/CollectionLikeType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/CollectionLikeType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,244 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.TypeVariable; +import java.util.Collection; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Type that represents things that act similar to {@link java.util.Collection}; + * but may or may not be instances of that interface. + * This specifically allows framework to check for configuration and annotation + * settings used for Map types, and pass these to custom handlers that may be more + * familiar with actual type. + */ +public class CollectionLikeType extends TypeBase +{ + private static final long serialVersionUID = 1L; + + /** + * Type of elements in collection + */ + protected final JavaType _elementType; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + protected CollectionLikeType(Class collT, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType elemT, + Object valueHandler, Object typeHandler, boolean asStatic) + { + super(collT, bindings, superClass, superInts, + elemT.hashCode(), valueHandler, typeHandler, asStatic); + _elementType = elemT; + } + + /** + * @since 2.7 + */ + protected CollectionLikeType(TypeBase base, JavaType elemT) + { + super(base); + _elementType = elemT; + } + + /** + * @since 2.7 + */ + public static CollectionLikeType construct(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType elemT) { + return new CollectionLikeType(rawType, bindings, superClass, superInts, elemT, + null, null, false); + } + + /** + * @deprecated Since 2.7, use {@link #upgradeFrom} for constructing instances, given + * pre-resolved {@link SimpleType}. + */ + @Deprecated // since 2.7 + public static CollectionLikeType construct(Class rawType, JavaType elemT) { + // First: may need to fabricate TypeBindings (needed for refining into + // concrete collection types, as per [databind#1102]) + TypeVariable[] vars = rawType.getTypeParameters(); + TypeBindings bindings; + if ((vars == null) || (vars.length != 1)) { + bindings = TypeBindings.emptyBindings(); + } else { + bindings = TypeBindings.create(rawType, elemT); + } + return new CollectionLikeType(rawType, bindings, + _bogusSuperClass(rawType), null, + elemT, null, null, false); + } + + /** + * Factory method that can be used to "upgrade" a basic type into collection-like + * one; usually done via {@link TypeModifier} + * + * @since 2.7 + */ + public static CollectionLikeType upgradeFrom(JavaType baseType, JavaType elementType) { + // 19-Oct-2015, tatu: Not sure if and how other types could be used as base; + // will cross that bridge if and when need be + if (baseType instanceof TypeBase) { + return new CollectionLikeType((TypeBase) baseType, elementType); + } + throw new IllegalArgumentException("Can not upgrade from an instance of "+baseType.getClass()); + } + + @Override + @Deprecated // since 2.7 + protected JavaType _narrow(Class subclass) { + return new CollectionLikeType(subclass, _bindings, + _superClass, _superInterfaces, _elementType, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public JavaType withContentType(JavaType contentType) { + if (_elementType == contentType) { + return this; + } + return new CollectionLikeType(_class, _bindings, _superClass, _superInterfaces, + contentType, _valueHandler, _typeHandler, _asStatic); + } + + @Override + public CollectionLikeType withTypeHandler(Object h) { + return new CollectionLikeType(_class, _bindings, + _superClass, _superInterfaces, _elementType, _valueHandler, h, _asStatic); + } + + @Override + public CollectionLikeType withContentTypeHandler(Object h) + { + return new CollectionLikeType(_class, _bindings, + _superClass, _superInterfaces, _elementType.withTypeHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public CollectionLikeType withValueHandler(Object h) { + return new CollectionLikeType(_class, _bindings, + _superClass, _superInterfaces, _elementType, h, _typeHandler, _asStatic); + } + + @Override + public CollectionLikeType withContentValueHandler(Object h) { + return new CollectionLikeType(_class, _bindings, + _superClass, _superInterfaces, _elementType.withValueHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public CollectionLikeType withStaticTyping() { + if (_asStatic) { + return this; + } + return new CollectionLikeType(_class, _bindings, + _superClass, _superInterfaces, _elementType.withStaticTyping(), + _valueHandler, _typeHandler, true); + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return new CollectionLikeType(rawType, bindings, + superClass, superInterfaces, _elementType, + _valueHandler, _typeHandler, _asStatic); + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + @Override + public boolean isContainerType() { return true; } + + @Override + public boolean isCollectionLikeType() { return true; } + + @Override + public JavaType getContentType() { return _elementType; } + + @Override + public Object getContentValueHandler() { + return _elementType.getValueHandler(); + } + + @Override + public Object getContentTypeHandler() { + return _elementType.getTypeHandler(); + } + + @Override + public StringBuilder getErasedSignature(StringBuilder sb) { + return _classSignature(_class, sb, true); + } + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) { + _classSignature(_class, sb, false); + sb.append('<'); + _elementType.getGenericSignature(sb); + sb.append(">;"); + return sb; + } + + @Override + protected String buildCanonicalName() { + StringBuilder sb = new StringBuilder(); + sb.append(_class.getName()); + if (_elementType != null) { + sb.append('<'); + sb.append(_elementType.toCanonical()); + sb.append('>'); + } + return sb.toString(); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + /** + * Method that can be used for checking whether this type is a + * "real" Collection type; meaning whether it represents a parameterized + * subtype of {@link java.util.Collection} or just something that acts + * like one. + */ + public boolean isTrueCollectionType() { + return Collection.class.isAssignableFrom(_class); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + + CollectionLikeType other = (CollectionLikeType) o; + return (_class == other._class) && _elementType.equals(other._elementType); + } + + @Override + public String toString() + { + return "[collection-like type; class "+_class.getName()+", contains "+_elementType+"]"; + } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/CollectionType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/CollectionType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/CollectionType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,136 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.TypeVariable; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Type that represents Java Collection types (Lists, Sets). + */ +public final class CollectionType + extends CollectionLikeType +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + private CollectionType(Class collT, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType elemT, + Object valueHandler, Object typeHandler, boolean asStatic) + { + super(collT, bindings, superClass, superInts, elemT, valueHandler, typeHandler, asStatic); + } + + /** + * @since 2.7 + */ + protected CollectionType(TypeBase base, JavaType elemT) { + super(base, elemT); + } + + /** + * @since 2.7 + */ + public static CollectionType construct(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType elemT) { + return new CollectionType(rawType, bindings, superClass, superInts, elemT, + null, null, false); + } + + /** + * @deprecated Since 2.7, remove from 2.8 + */ + @Deprecated // since 2.7 + public static CollectionType construct(Class rawType, JavaType elemT) { + // First: may need to fabricate TypeBindings (needed for refining into + // concrete collection types, as per [databind#1102]) + TypeVariable[] vars = rawType.getTypeParameters(); + TypeBindings bindings; + if ((vars == null) || (vars.length != 1)) { + bindings = TypeBindings.emptyBindings(); + } else { + bindings = TypeBindings.create(rawType, elemT); + } + return new CollectionType(rawType, bindings, + // !!! TODO: Wrong, does have supertypes, but: + _bogusSuperClass(rawType), null, elemT, + null, null, false); + } + + @Deprecated // since 2.7 + @Override + protected JavaType _narrow(Class subclass) { + return new CollectionType(subclass, _bindings, + _superClass, _superInterfaces, _elementType, null, null, _asStatic); + } + + @Override + public JavaType withContentType(JavaType contentType) { + if (_elementType == contentType) { + return this; + } + return new CollectionType(_class, _bindings, _superClass, _superInterfaces, + contentType, _valueHandler, _typeHandler, _asStatic); + } + + @Override + public CollectionType withTypeHandler(Object h) { + return new CollectionType(_class, _bindings, + _superClass, _superInterfaces, _elementType, _valueHandler, h, _asStatic); + } + + @Override + public CollectionType withContentTypeHandler(Object h) + { + return new CollectionType(_class, _bindings, + _superClass, _superInterfaces, _elementType.withTypeHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public CollectionType withValueHandler(Object h) { + return new CollectionType(_class, _bindings, + _superClass, _superInterfaces, _elementType, h, _typeHandler, _asStatic); + } + + @Override + public CollectionType withContentValueHandler(Object h) { + return new CollectionType(_class, _bindings, + _superClass, _superInterfaces, _elementType.withValueHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public CollectionType withStaticTyping() { + if (_asStatic) { + return this; + } + return new CollectionType(_class, _bindings, + _superClass, _superInterfaces, _elementType.withStaticTyping(), + _valueHandler, _typeHandler, true); + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return new CollectionType(rawType, bindings, + superClass, superInterfaces, _elementType, + _valueHandler, _typeHandler, _asStatic); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public String toString() + { + return "[collection type; class "+_class.getName()+", contains "+_elementType+"]"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/MapLikeType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/MapLikeType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/MapLikeType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,278 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.TypeVariable; +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Type that represents Map-like types; things that consist of key/value pairs + * but that do not necessarily implement {@link java.util.Map}, but that do not + * have enough introspection functionality to allow for some level of generic + * handling. This specifically allows framework to check for configuration and + * annotation settings used for Map types, and pass these to custom handlers + * that may be more familiar with actual type. + */ +public class MapLikeType extends TypeBase { + private static final long serialVersionUID = 1L; + + /** + * Type of keys of Map. + */ + protected final JavaType _keyType; + + /** + * Type of values of Map. + */ + protected final JavaType _valueType; + + /* + * /********************************************************** /* Life-cycle + * /********************************************************** + */ + + protected MapLikeType(Class mapType, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType keyT, + JavaType valueT, Object valueHandler, Object typeHandler, + boolean asStatic) { + super(mapType, bindings, superClass, superInts, keyT.hashCode() + ^ valueT.hashCode(), valueHandler, typeHandler, asStatic); + _keyType = keyT; + _valueType = valueT; + } + + /** + * @since 2.7 + */ + protected MapLikeType(TypeBase base, JavaType keyT, JavaType valueT) { + super(base); + _keyType = keyT; + _valueType = valueT; + } + + /** + * Factory method that can be used to "upgrade" a basic type into + * collection-like one; usually done via {@link TypeModifier} + * + * @since 2.7 + */ + public static MapLikeType upgradeFrom(JavaType baseType, JavaType keyT, + JavaType valueT) { + // 19-Oct-2015, tatu: Not sure if and how other types could be used as + // base; + // will cross that bridge if and when need be + if (baseType instanceof TypeBase) { + return new MapLikeType((TypeBase) baseType, keyT, valueT); + } + throw new IllegalArgumentException( + "Can not upgrade from an instance of " + baseType.getClass()); + } + + @Deprecated + // since 2.7; remove from 2.8 + public static MapLikeType construct(Class rawType, JavaType keyT, + JavaType valueT) { + // First: may need to fabricate TypeBindings (needed for refining into + // concrete collection types, as per [databind#1102]) + TypeVariable[] vars = rawType.getTypeParameters(); + TypeBindings bindings; + if ((vars == null) || (vars.length != 2)) { + bindings = TypeBindings.emptyBindings(); + } else { + bindings = TypeBindings.create(rawType, keyT, valueT); + } + return new MapLikeType(rawType, bindings, _bogusSuperClass(rawType), + null, keyT, valueT, null, null, false); + } + + @Deprecated + // since 2.7 + @Override + protected JavaType _narrow(Class subclass) { + return new MapLikeType(subclass, _bindings, _superClass, + _superInterfaces, _keyType, _valueType, _valueHandler, + _typeHandler, _asStatic); + } + + /** + * @since 2.7 + */ + public MapLikeType withKeyType(JavaType keyType) { + if (keyType == _keyType) { + return this; + } + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, keyType, _valueType, _valueHandler, + _typeHandler, _asStatic); + } + + @Override + public JavaType withContentType(JavaType contentType) { + if (_valueType == contentType) { + return this; + } + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType, contentType, _valueHandler, + _typeHandler, _asStatic); + } + + @Override + public MapLikeType withTypeHandler(Object h) { + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType, _valueType, _valueHandler, h, + _asStatic); + } + + @Override + public MapLikeType withContentTypeHandler(Object h) { + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType, _valueType.withTypeHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapLikeType withValueHandler(Object h) { + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType, _valueType, h, _typeHandler, + _asStatic); + } + + @Override + public MapLikeType withContentValueHandler(Object h) { + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType, _valueType.withValueHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapLikeType withStaticTyping() { + if (_asStatic) { + return this; + } + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType, _valueType.withStaticTyping(), + _valueHandler, _typeHandler, true); + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return new MapLikeType(rawType, bindings, superClass, superInterfaces, + _keyType, _valueType, _valueHandler, _typeHandler, _asStatic); + } + + @Override + protected String buildCanonicalName() { + StringBuilder sb = new StringBuilder(); + sb.append(_class.getName()); + if (_keyType != null) { + sb.append('<'); + sb.append(_keyType.toCanonical()); + sb.append(','); + sb.append(_valueType.toCanonical()); + sb.append('>'); + } + return sb.toString(); + } + + /* + * /********************************************************** /* Public API + * /********************************************************** + */ + + @Override + public boolean isContainerType() { + return true; + } + + @Override + public boolean isMapLikeType() { + return true; + } + + @Override + public JavaType getKeyType() { + return _keyType; + } + + @Override + public JavaType getContentType() { + return _valueType; + } + + @Override + public Object getContentValueHandler() { + return _valueType.getValueHandler(); + } + + @Override + public Object getContentTypeHandler() { + return _valueType.getTypeHandler(); + } + + @Override + public StringBuilder getErasedSignature(StringBuilder sb) { + return _classSignature(_class, sb, true); + } + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) { + _classSignature(_class, sb, false); + sb.append('<'); + _keyType.getGenericSignature(sb); + _valueType.getGenericSignature(sb); + sb.append(">;"); + return sb; + } + + /* + * /********************************************************** /* Extended + * API /********************************************************** + */ + + public MapLikeType withKeyTypeHandler(Object h) { + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType.withTypeHandler(h), _valueType, + _valueHandler, _typeHandler, _asStatic); + } + + public MapLikeType withKeyValueHandler(Object h) { + return new MapLikeType(_class, _bindings, _superClass, + _superInterfaces, _keyType.withValueHandler(h), _valueType, + _valueHandler, _typeHandler, _asStatic); + } + + /** + * Method that can be used for checking whether this type is a "real" + * Collection type; meaning whether it represents a parameterized subtype of + * {@link java.util.Collection} or just something that acts like one. + */ + public boolean isTrueMapType() { + return Map.class.isAssignableFrom(_class); + } + + /* + * /********************************************************** /* Standard + * methods /********************************************************** + */ + + @Override + public String toString() { + return "[map-like type; class " + _class.getName() + ", " + _keyType + + " -> " + _valueType + "]"; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (o == null) + return false; + if (o.getClass() != getClass()) + return false; + + MapLikeType other = (MapLikeType) o; + return (_class == other._class) && _keyType.equals(other._keyType) + && _valueType.equals(other._valueType); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/MapType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/MapType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/MapType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,163 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.TypeVariable; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Type that represents "true" Java Map types. + */ +public final class MapType extends MapLikeType +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + private MapType(Class mapType, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType keyT, JavaType valueT, + Object valueHandler, Object typeHandler, boolean asStatic) { + super(mapType, bindings, superClass, superInts, + keyT, valueT, valueHandler, typeHandler, asStatic); + } + + /** + * @since 2.7 + */ + protected MapType(TypeBase base, JavaType keyT, JavaType valueT) { + super(base, keyT, valueT); + } + + /** + * @since 2.7 + */ + public static MapType construct(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, + JavaType keyT, JavaType valueT) { + return new MapType(rawType, bindings, superClass, superInts, keyT, valueT, null, null, false); + } + + @Deprecated // since 2.7 + public static MapType construct(Class rawType, JavaType keyT, JavaType valueT) + { + // First: may need to fabricate TypeBindings (needed for refining into + // concrete collection types, as per [databind#1102]) + TypeVariable[] vars = rawType.getTypeParameters(); + TypeBindings bindings; + if ((vars == null) || (vars.length != 2)) { + bindings = TypeBindings.emptyBindings(); + } else { + bindings = TypeBindings.create(rawType, keyT, valueT); + } + // !!! TODO: Wrong, does have supertypes + return new MapType(rawType, bindings, _bogusSuperClass(rawType), null, + keyT, valueT, null, null, false); + } + + @Deprecated // since 2.7 + @Override + protected JavaType _narrow(Class subclass) { + return new MapType(subclass, _bindings, + _superClass, _superInterfaces, _keyType, _valueType, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapType withTypeHandler(Object h) { + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType, _valueType, _valueHandler, h, _asStatic); + } + + @Override + public MapType withContentTypeHandler(Object h) + { + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType, _valueType.withTypeHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapType withValueHandler(Object h) { + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType, _valueType, h, _typeHandler, _asStatic); + } + + @Override + public MapType withContentValueHandler(Object h) { + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType, _valueType.withValueHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapType withStaticTyping() { + if (_asStatic) { + return this; + } + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType.withStaticTyping(), _valueType.withStaticTyping(), + _valueHandler, _typeHandler, true); + } + + @Override + public JavaType withContentType(JavaType contentType) { + if (_valueType == contentType) { + return this; + } + return new MapType(_class, _bindings, _superClass, _superInterfaces, + _keyType, contentType, _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapType withKeyType(JavaType keyType) { + if (keyType == _keyType) { + return this; + } + return new MapType(_class, _bindings, _superClass, _superInterfaces, + keyType, _valueType, _valueHandler, _typeHandler, _asStatic); + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return new MapType(rawType, bindings, + superClass, superInterfaces, _keyType, _valueType, + _valueHandler, _typeHandler, _asStatic); + } + + /* + /********************************************************** + /* Extended API + /********************************************************** + */ + + @Override + public MapType withKeyTypeHandler(Object h) + { + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType.withTypeHandler(h), _valueType, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public MapType withKeyValueHandler(Object h) { + return new MapType(_class, _bindings, + _superClass, _superInterfaces, _keyType.withValueHandler(h), _valueType, + _valueHandler, _typeHandler, _asStatic); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public String toString() + { + return "[map type; class "+_class.getName()+", "+_keyType+" -> "+_valueType+"]"; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ReferenceType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ReferenceType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ReferenceType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,235 @@ +package com.fasterxml.jackson.databind.type; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Specialized {@link SimpleType} for types that are referential types, + * that is, values that can be dereferenced to another value (or null), + * of different type. + * Referenced type is accessible using {@link #getContentType()}. + * + * @since 2.6 + */ +public class ReferenceType extends SimpleType +{ + private static final long serialVersionUID = 1L; + + protected final JavaType _referencedType; + + protected ReferenceType(Class cls, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType refType, + Object valueHandler, Object typeHandler, boolean asStatic) + { + super(cls, bindings, superClass, superInts, refType.hashCode(), + valueHandler, typeHandler, asStatic); + _referencedType = refType; + } + + /** + * @since 2.7 + */ + protected ReferenceType(TypeBase base, JavaType refType) + { + super(base); + _referencedType = refType; + } + + /** + * Factory method that can be used to "upgrade" a basic type into collection-like + * one; usually done via {@link TypeModifier} + * + * @since 2.7 + */ + public static ReferenceType upgradeFrom(JavaType baseType, JavaType refType) { + if (refType == null) { + throw new IllegalArgumentException("Missing referencedType"); + } + // 19-Oct-2015, tatu: Not sure if and how other types could be used as base; + // will cross that bridge if and when need be + if (baseType instanceof TypeBase) { + return new ReferenceType((TypeBase) baseType, refType); + } + throw new IllegalArgumentException("Can not upgrade from an instance of "+baseType.getClass()); + } + + /** + * @since 2.7 + */ + public static ReferenceType construct(Class cls, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, JavaType refType) + { + return new ReferenceType(cls, bindings, superClass, superInts, + refType, null, null, false); + } + + @Deprecated // since 2.7 + public static ReferenceType construct(Class cls, JavaType refType) { + return new ReferenceType(cls, TypeBindings.emptyBindings(), + // !!! TODO: missing supertypes + null, null, refType, null, null, false); + } + + @Override + public JavaType withContentType(JavaType contentType) { + if (_referencedType == contentType) { + return this; + } + return new ReferenceType(_class, _bindings, _superClass, _superInterfaces, + contentType, _valueHandler, _typeHandler, _asStatic); + } + + @Override + public ReferenceType withTypeHandler(Object h) + { + if (h == _typeHandler) { + return this; + } + return new ReferenceType(_class, _bindings, + _superClass, _superInterfaces, _referencedType, _valueHandler, h, _asStatic); + } + + @Override + public ReferenceType withContentTypeHandler(Object h) + { + if (h == _referencedType.getTypeHandler()) { + return this; + } + return new ReferenceType(_class, _bindings, + _superClass, _superInterfaces, _referencedType.withTypeHandler(h), + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public ReferenceType withValueHandler(Object h) { + if (h == _valueHandler) { + return this; + } + return new ReferenceType(_class, _bindings, + _superClass, _superInterfaces, _referencedType, h, _typeHandler,_asStatic); + } + + @Override + public ReferenceType withContentValueHandler(Object h) { + if (h == _referencedType.getValueHandler()) { + return this; + } + JavaType refdType = _referencedType.withValueHandler(h); + return new ReferenceType(_class, _bindings, + _superClass, _superInterfaces, refdType, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + public ReferenceType withStaticTyping() { + if (_asStatic) { + return this; + } + return new ReferenceType(_class, _bindings, + _superClass, _superInterfaces, _referencedType.withStaticTyping(), + _valueHandler, _typeHandler, true); + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return new ReferenceType(rawType, _bindings, + superClass, superInterfaces, _referencedType, + _valueHandler, _typeHandler, _asStatic); + } + + @Override + protected String buildCanonicalName() + { + StringBuilder sb = new StringBuilder(); + sb.append(_class.getName()); + sb.append('<'); + sb.append(_referencedType.toCanonical()); + return sb.toString(); + } + + /* + /********************************************************** + /* Narrow/widen + /********************************************************** + */ + + @Override + @Deprecated // since 2.7 + protected JavaType _narrow(Class subclass) + { + // Should we check that there is a sub-class relationship? + return new ReferenceType(subclass, _bindings, + _superClass, _superInterfaces, _referencedType, + _valueHandler, _typeHandler, _asStatic); + } + + /* + /********************************************************** + /* Public API overrides + /********************************************************** + */ + + @Override + public JavaType getContentType() { + return _referencedType; + } + + @Override + public JavaType getReferencedType() { + return _referencedType; + } + + @Override + public boolean isReferenceType() { + return true; + } + + @Override + public StringBuilder getErasedSignature(StringBuilder sb) { + return _classSignature(_class, sb, true); + } + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) + { + _classSignature(_class, sb, false); + sb.append('<'); + sb = _referencedType.getGenericSignature(sb); + sb.append(">;"); + return sb; + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public String toString() + { + return new StringBuilder(40) + .append("[reference type, class ") + .append(buildCanonicalName()) + .append('<') + .append(_referencedType) + .append('>') + .append(']') + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + + ReferenceType other = (ReferenceType) o; + + if (other._class != _class) return false; + + // Otherwise actually mostly worry about referenced type + return _referencedType.equals(other._referencedType); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/ResolvedRecursiveType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,105 @@ +package com.fasterxml.jackson.databind.type; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Internal placeholder type used for self-references. + * + * @since 2.7 + */ +public class ResolvedRecursiveType extends TypeBase +{ + private static final long serialVersionUID = 1L; + + protected JavaType _referencedType; + + public ResolvedRecursiveType(Class erasedType, TypeBindings bindings) { + super(erasedType, bindings, null, null, 0, null, null, false); + } + + public void setReference(JavaType ref) + { + // sanity check; should not be called multiple times + if (_referencedType != null) { + throw new IllegalStateException("Trying to re-set self reference; old value = "+_referencedType+", new = "+ref); + } + _referencedType = ref; + } + + public JavaType getSelfReferencedType() { return _referencedType; } + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) { + return _referencedType.getGenericSignature(sb); + } + + @Override + public StringBuilder getErasedSignature(StringBuilder sb) { + return _referencedType.getErasedSignature(sb); + } + + @Override + public JavaType withContentType(JavaType contentType) { + return this; + } + + @Override + public JavaType withTypeHandler(Object h) { + return this; + } + + @Override + public JavaType withContentTypeHandler(Object h) { + return this; + } + + @Override + public JavaType withValueHandler(Object h) { + return this; + } + + @Override + public JavaType withContentValueHandler(Object h) { + return this; + } + + @Override + public JavaType withStaticTyping() { + return this; + } + + @Deprecated // since 2.7 + @Override + protected JavaType _narrow(Class subclass) { + return this; + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + return null; + } + + @Override + public boolean isContainerType() { + return false; + } + + @Override + public String toString() { + return new StringBuilder(40) + .append("[resolved recursive type -> ") + .append(_referencedType) + .append(']') + .toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + + return ((ResolvedRecursiveType) o).getSelfReferencedType().equals(getSelfReferencedType()); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/SimpleType.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/SimpleType.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/SimpleType.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,323 @@ +package com.fasterxml.jackson.databind.type; + +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Simple types are defined as anything other than one of recognized + * container types (arrays, Collections, Maps). For our needs we + * need not know anything further, since we have no way of dealing + * with generic types other than Collections and Maps. + */ +public class SimpleType // note: until 2.6 was final + extends TypeBase +{ + private static final long serialVersionUID = 1L; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * Constructor only used by core Jackson databind functionality; + * should never be called by application code. + *

+ * As with other direct construction that by-passes {@link TypeFactory}, + * no introspection occurs with respect to super-types; caller must be + * aware of consequences if using this method. + */ + protected SimpleType(Class cls) { + this(cls, TypeBindings.emptyBindings(), null, null); + } + + protected SimpleType(Class cls, TypeBindings bindings, + JavaType superClass, JavaType[] superInts) { + this(cls, bindings, superClass, superInts, null, null, false); + } + + /** + * Simple copy-constructor, usually used when upgrading/refining a simple type + * into more specialized type. + * + * @since 2.7 + */ + protected SimpleType(TypeBase base) { + super(base); + } + + protected SimpleType(Class cls, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, + Object valueHandler, Object typeHandler, boolean asStatic) + { + super(cls, bindings, superClass, superInts, + 0, valueHandler, typeHandler, asStatic); + } + + /** + * Pass-through constructor used by {@link ReferenceType}. + * + * @since 2.6 + */ + protected SimpleType(Class cls, TypeBindings bindings, + JavaType superClass, JavaType[] superInts, int extraHash, + Object valueHandler, Object typeHandler, boolean asStatic) + { + super(cls, bindings, superClass, superInts, + extraHash, valueHandler, typeHandler, asStatic); + } + + /** + * Method used by core Jackson classes: NOT to be used by application code: + * it does NOT properly handle inspection of super-types, so neither parent + * Classes nor implemented Interfaces are accessible with resulting type + * instance. + *

+ * NOTE: public only because it is called by ObjectMapper which is + * not in same package + */ + public static SimpleType constructUnsafe(Class raw) { + return new SimpleType(raw, null, + // 18-Oct-2015, tatu: Should be ok to omit possible super-types, right? + null, null, null, null, false); + } + + /** + * Method that should NOT to be used by application code: + * it does NOT properly handle inspection of super-types, so neither parent + * Classes nor implemented Interfaces are accessible with resulting type + * instance. Instead, please use {@link TypeFactory}'s constructType + * methods which handle introspection appropriately. + *

+ * Note that prior to 2.7, method usage was not limited and would typically + * have worked acceptably: the problem comes from inability to resolve super-type + * information, for which {@link TypeFactory} is needed. + * + * @deprecated Since 2.7 + */ + @Deprecated + public static SimpleType construct(Class cls) + { + /* Let's add sanity checks, just to ensure no + * Map/Collection entries are constructed + */ + if (Map.class.isAssignableFrom(cls)) { + throw new IllegalArgumentException("Can not construct SimpleType for a Map (class: "+cls.getName()+")"); + } + if (Collection.class.isAssignableFrom(cls)) { + throw new IllegalArgumentException("Can not construct SimpleType for a Collection (class: "+cls.getName()+")"); + } + // ... and while we are at it, not array types either + if (cls.isArray()) { + throw new IllegalArgumentException("Can not construct SimpleType for an array (class: "+cls.getName()+")"); + } + TypeBindings b = TypeBindings.emptyBindings(); + return new SimpleType(cls, b, + _buildSuperClass(cls.getSuperclass(), b), null, null, null, false); + } + + @Override + @Deprecated + protected JavaType _narrow(Class subclass) + { + if (_class == subclass) { + return this; + } + // Should we check that there is a sub-class relationship? + // 15-Jan-2016, tatu: Almost yes, but there are some complications with + // placeholder values (`Void`, `NoClass`), so can not quite do yet. + // TODO: fix in 2.8 + if (!_class.isAssignableFrom(subclass)) { + /* + throw new IllegalArgumentException("Class "+subclass.getName()+" not sub-type of " + +_class.getName()); + */ + return new SimpleType(subclass, _bindings, this, _superInterfaces, + _valueHandler, _typeHandler, _asStatic); + } + // Otherwise, stitch together the hierarchy. First, super-class + Class next = subclass.getSuperclass(); + if (next == _class) { // straight up parent class? Great. + return new SimpleType(subclass, _bindings, this, + _superInterfaces, _valueHandler, _typeHandler, _asStatic); + } + if ((next != null) && _class.isAssignableFrom(next)) { + JavaType superb = _narrow(next); + return new SimpleType(subclass, _bindings, superb, + null, _valueHandler, _typeHandler, _asStatic); + } + // if not found, try a super-interface + Class[] nextI = subclass.getInterfaces(); + for (Class iface : nextI) { + if (iface == _class) { // directly implemented + return new SimpleType(subclass, _bindings, null, + new JavaType[] { this }, _valueHandler, _typeHandler, _asStatic); + } + if (_class.isAssignableFrom(iface)) { // indirect, so recurse + JavaType superb = _narrow(iface); + return new SimpleType(subclass, _bindings, null, + new JavaType[] { superb }, _valueHandler, _typeHandler, _asStatic); + } + } + // should not get here but... + throw new IllegalArgumentException("Internal error: Can not resolve sub-type for Class "+subclass.getName()+" to " + +_class.getName()); + } + + @Override + public JavaType withContentType(JavaType contentType) { + throw new IllegalArgumentException("Simple types have no content types; can not call withContentType()"); + } + + @Override + public SimpleType withTypeHandler(Object h) { + if (_typeHandler == h) { + return this; + } + return new SimpleType(_class, _bindings, _superClass, _superInterfaces, _valueHandler, h, _asStatic); + } + + @Override + public JavaType withContentTypeHandler(Object h) { + // no content type, so: + throw new IllegalArgumentException("Simple types have no content types; can not call withContenTypeHandler()"); + } + + @Override + public SimpleType withValueHandler(Object h) { + if (h == _valueHandler) { + return this; + } + return new SimpleType(_class, _bindings, _superClass, _superInterfaces, h, _typeHandler, _asStatic); + } + + @Override + public SimpleType withContentValueHandler(Object h) { + // no content type, so: + throw new IllegalArgumentException("Simple types have no content types; can not call withContenValueHandler()"); + } + + @Override + public SimpleType withStaticTyping() { + return _asStatic ? this : new SimpleType(_class, _bindings, + _superClass, _superInterfaces, _valueHandler, _typeHandler, true); + } + + @Override + public JavaType refine(Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) { + // SimpleType means something not-specialized, so: + return null; + } + + @Override + protected String buildCanonicalName() + { + StringBuilder sb = new StringBuilder(); + sb.append(_class.getName()); + + final int count = _bindings.size(); + if (count > 0) { + sb.append('<'); + for (int i = 0; i < count; ++i) { + JavaType t = containedType(i); + if (i > 0) { + sb.append(','); + } + sb.append(t.toCanonical()); + } + sb.append('>'); + } + return sb.toString(); + } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + @Override + public boolean isContainerType() { return false; } + + @Override + public StringBuilder getErasedSignature(StringBuilder sb) { + return _classSignature(_class, sb, true); + } + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) + { + _classSignature(_class, sb, false); + + final int count = _bindings.size(); + if (count > 0) { + sb.append('<'); + for (int i = 0; i < count; ++i) { + sb = containedType(i).getGenericSignature(sb); + } + sb.append('>'); + } + sb.append(';'); + return sb; + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + /** + * Helper method we need to recursively build skeletal representations + * of superclasses. + * + * @since 2.7 -- remove when not needed (2.8?) + */ + private static JavaType _buildSuperClass(Class superClass, TypeBindings b) + { + if (superClass == null) { + return null; + } + if (superClass == Object.class) { + return TypeFactory.unknownType(); + } + JavaType superSuper = _buildSuperClass(superClass.getSuperclass(), b); + return new SimpleType(superClass, b, + superSuper, null, null, null, false); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(40); + sb.append("[simple type, class ").append(buildCanonicalName()).append(']'); + return sb.toString(); + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o.getClass() != getClass()) return false; + + SimpleType other = (SimpleType) o; + + // Classes must be identical... + if (other._class != this._class) return false; + + // And finally, generic bindings, if any + TypeBindings b1 = _bindings; + TypeBindings b2 = other._bindings; + return b1.equals(b2); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeBase.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeBase.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeBase.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,262 @@ +package com.fasterxml.jackson.databind.type; + +import java.io.IOException; +import java.util.*; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +public abstract class TypeBase + extends JavaType + implements JsonSerializable +{ + private static final long serialVersionUID = 1; + + private final static TypeBindings NO_BINDINGS = TypeBindings.emptyBindings(); + private final static JavaType[] NO_TYPES = new JavaType[0]; + + protected final JavaType _superClass; + + protected final JavaType[] _superInterfaces; + + /** + * Bindings in effect for this type instance; possibly empty. + * Needed when resolving types declared in members of this type + * (if any). + * + * @since 2.7 + */ + protected final TypeBindings _bindings; + + /** + * Lazily initialized external representation of the type + */ + volatile transient String _canonicalName; + + /** + * Main constructor to use by extending classes. + */ + protected TypeBase(Class raw, TypeBindings bindings, JavaType superClass, JavaType[] superInts, + int hash, + Object valueHandler, Object typeHandler, boolean asStatic) + { + super(raw, hash, valueHandler, typeHandler, asStatic); + _bindings = (bindings == null) ? NO_BINDINGS : bindings; + _superClass = superClass; + _superInterfaces = superInts; + } + + /** + * Copy-constructor used when refining/upgrading type instances. + * + * @since 2.7 + */ + protected TypeBase(TypeBase base) { + super(base); + _superClass = base._superClass; + _superInterfaces = base._superInterfaces; + _bindings = base._bindings; + } + + @Override + public String toCanonical() + { + String str = _canonicalName; + if (str == null) { + str = buildCanonicalName(); + } + return str; + } + + protected String buildCanonicalName() { + return _class.getName(); + } + + @Override + public abstract StringBuilder getGenericSignature(StringBuilder sb); + + @Override + public abstract StringBuilder getErasedSignature(StringBuilder sb); + + @Override + @SuppressWarnings("unchecked") + public T getValueHandler() { return (T) _valueHandler; } + + @Override + @SuppressWarnings("unchecked") + public T getTypeHandler() { return (T) _typeHandler; } + + @Override + public TypeBindings getBindings() { + return _bindings; + } + + @Override + public int containedTypeCount() { + return _bindings.size(); + } + + @Override + public JavaType containedType(int index) { + return _bindings.getBoundType(index); + } + + @Override + @Deprecated + public String containedTypeName(int index) { + return _bindings.getBoundName(index); + } + + @Override + public JavaType getSuperClass() { + return _superClass; + } + + @Override + public List getInterfaces() { + if (_superInterfaces == null) { + return Collections.emptyList(); + } + switch (_superInterfaces.length) { + case 0: + return Collections.emptyList(); + case 1: + return Collections.singletonList(_superInterfaces[0]); + } + return Arrays.asList(_superInterfaces); + } + + @Override + public final JavaType findSuperType(Class rawTarget) + { + if (rawTarget == _class) { + return this; + } + // Check super interfaces first: + if (rawTarget.isInterface() && (_superInterfaces != null)) { + for (int i = 0, count = _superInterfaces.length; i < count; ++i) { + JavaType type = _superInterfaces[i].findSuperType(rawTarget); + if (type != null) { + return type; + } + } + } + // and if not found, super class and its supertypes + if (_superClass != null) { + JavaType type = _superClass.findSuperType(rawTarget); + if (type != null) { + return type; + } + } + return null; + } + + @Override + public JavaType[] findTypeParameters(Class expType) + { + JavaType match = findSuperType(expType); + if (match == null) { + return NO_TYPES; + } + return match.getBindings().typeParameterArray(); + } + + /* + /********************************************************** + /* JsonSerializable base implementation + /********************************************************** + */ + + @Override + public void serializeWithType(JsonGenerator gen, SerializerProvider provider, + TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + typeSer.writeTypePrefixForScalar(this, gen); + this.serialize(gen, provider); + typeSer.writeTypeSuffixForScalar(this, gen); + } + + @Override + public void serialize(JsonGenerator gen, SerializerProvider provider) + throws IOException, JsonProcessingException + { + gen.writeString(toCanonical()); + } + + /* + /********************************************************** + /* Methods for sub-classes to use + /********************************************************** + */ + + /** + * @param trailingSemicolon Whether to add trailing semicolon for non-primitive + * (reference) types or not + */ + protected static StringBuilder _classSignature(Class cls, StringBuilder sb, + boolean trailingSemicolon) + { + if (cls.isPrimitive()) { + if (cls == Boolean.TYPE) { + sb.append('Z'); + } else if (cls == Byte.TYPE) { + sb.append('B'); + } + else if (cls == Short.TYPE) { + sb.append('S'); + } + else if (cls == Character.TYPE) { + sb.append('C'); + } + else if (cls == Integer.TYPE) { + sb.append('I'); + } + else if (cls == Long.TYPE) { + sb.append('J'); + } + else if (cls == Float.TYPE) { + sb.append('F'); + } + else if (cls == Double.TYPE) { + sb.append('D'); + } + else if (cls == Void.TYPE) { + sb.append('V'); + } else { + throw new IllegalStateException("Unrecognized primitive type: "+cls.getName()); + } + } else { + sb.append('L'); + String name = cls.getName(); + for (int i = 0, len = name.length(); i < len; ++i) { + char c = name.charAt(i); + if (c == '.') c = '/'; + sb.append(c); + } + if (trailingSemicolon) { + sb.append(';'); + } + } + return sb; + } + + /** + * Internal helper method used to figure out nominal super-class for + * deprecated factory methods / constructors, where we are not given + * properly resolved supertype hierarchy. + * Will basically give `JavaType` for `java.lang.Object` for classes + * other than `java.lafgn.Object`; null for others. + * + * @since 2.7 + */ + protected static JavaType _bogusSuperClass(Class cls) { + Class parent = cls.getSuperclass(); + if (parent == null) { + return null; + } + return TypeFactory.unknownType(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeBindings.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeBindings.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeBindings.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,413 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.*; +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Helper class used for resolving type parameters for given class + */ +public class TypeBindings + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + private final static String[] NO_STRINGS = new String[0]; + + private final static JavaType[] NO_TYPES = new JavaType[0]; + + private final static TypeBindings EMPTY = new TypeBindings(NO_STRINGS, NO_TYPES, null); + + // // // Pre-resolved instances for minor optimizations + + // // // Actual member information + + /** + * Array of type (type variable) names. + */ + private final String[] _names; + + /** + * Types matching names + */ + private final JavaType[] _types; + + /** + * Names of potentially unresolved type variables. + * + * @since 2.3 + */ + private final String[] _unboundVariables; + + private final int _hashCode; + + /* + /********************************************************************** + /* Construction + /********************************************************************** + */ + + private TypeBindings(String[] names, JavaType[] types, String[] uvars) + { + _names = (names == null) ? NO_STRINGS : names; + _types = (types == null) ? NO_TYPES : types; + if (_names.length != _types.length) { + throw new IllegalArgumentException("Mismatching names ("+_names.length+"), types ("+_types.length+")"); + } + int h = 1; + for (int i = 0, len = _types.length; i < len; ++i) { + h += _types[i].hashCode(); + } + _unboundVariables = uvars; + _hashCode = h; + } + + public static TypeBindings emptyBindings() { + return EMPTY; + } + + // Let's just canonicalize serialized EMPTY back to static instance, if need be + protected Object readResolve() { + if ((_names == null) || (_names.length == 0)) { + return EMPTY; + } + return this; + } + + /** + * Factory method for constructing bindings for given class using specified type + * parameters. + */ + public static TypeBindings create(Class erasedType, List typeList) + { + JavaType[] types = (typeList == null || typeList.isEmpty()) ? + NO_TYPES : typeList.toArray(new JavaType[typeList.size()]); + return create(erasedType, types); + } + + public static TypeBindings create(Class erasedType, JavaType[] types) + { + if (types == null) { + types = NO_TYPES; + } else switch (types.length) { + case 1: + return create(erasedType, types[0]); + case 2: + return create(erasedType, types[0], types[1]); + } + TypeVariable[] vars = erasedType.getTypeParameters(); + String[] names; + if (vars == null || vars.length == 0) { + names = NO_STRINGS; + } else { + int len = vars.length; + names = new String[len]; + for (int i = 0; i < len; ++i) { + names[i] = vars[i].getName(); + } + } + // Check here to give better error message + if (names.length != types.length) { + throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName() + +" with "+types.length+" type parameter" + +((types.length == 1) ? "" : "s")+": class expects "+names.length); + } + return new TypeBindings(names, types, null); + } + + public static TypeBindings create(Class erasedType, JavaType typeArg1) + { + // 30-Oct-2015, tatu: Minor optimization for relatively common cases + TypeVariable[] vars = TypeParamStash.paramsFor1(erasedType); + int varLen = (vars == null) ? 0 : vars.length; + if (varLen != 1) { + throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName() + +" with 1 type parameter: class expects "+varLen); + } + return new TypeBindings(new String[] { vars[0].getName() }, + new JavaType[] { typeArg1 }, null); + } + + public static TypeBindings create(Class erasedType, JavaType typeArg1, JavaType typeArg2) + { + // 30-Oct-2015, tatu: Minor optimization for relatively common cases + TypeVariable[] vars = TypeParamStash.paramsFor2(erasedType); + int varLen = (vars == null) ? 0 : vars.length; + if (varLen != 2) { + throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName() + +" with 2 type parameters: class expects "+varLen); + } + return new TypeBindings(new String[] { vars[0].getName(), vars[1].getName() }, + new JavaType[] { typeArg1, typeArg2 }, null); + } + + /** + * Alternate factory method that may be called if it is possible that type + * does or does not require type parameters; this is mostly useful for + * collection- and map-like types. + */ + public static TypeBindings createIfNeeded(Class erasedType, JavaType typeArg1) + { + TypeVariable[] vars = erasedType.getTypeParameters(); + int varLen = (vars == null) ? 0 : vars.length; + if (varLen == 0) { + return EMPTY; + } + if (varLen != 1) { + throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName() + +" with 1 type parameter: class expects "+varLen); + } + return new TypeBindings(new String[] { vars[0].getName() }, + new JavaType[] { typeArg1 }, null); + } + + /** + * Alternate factory method that may be called if it is possible that type + * does or does not require type parameters; this is mostly useful for + * collection- and map-like types. + */ + public static TypeBindings createIfNeeded(Class erasedType, JavaType[] types) + { + TypeVariable[] vars = erasedType.getTypeParameters(); + if (vars == null || vars.length == 0) { + return EMPTY; + } + if (types == null) { + types = NO_TYPES; + } + int len = vars.length; + String[] names = new String[len]; + for (int i = 0; i < len; ++i) { + names[i] = vars[i].getName(); + } + // Check here to give better error message + if (names.length != types.length) { + throw new IllegalArgumentException("Can not create TypeBindings for class "+erasedType.getName() + +" with "+types.length+" type parameter" + +((types.length == 1) ? "" : "s")+": class expects "+names.length); + } + return new TypeBindings(names, types, null); + } + + /** + * Method for creating an instance that has same bindings as this object, + * plus an indicator for additional type variable that may be unbound within + * this context; this is needed to resolve recursive self-references. + */ + public TypeBindings withUnboundVariable(String name) + { + int len = (_unboundVariables == null) ? 0 : _unboundVariables.length; + String[] names = (len == 0) + ? new String[1] : Arrays.copyOf(_unboundVariables, len+1); + names[len] = name; + return new TypeBindings(_names, _types, names); + } + + /* + /********************************************************************** + /* Accessors + /********************************************************************** + */ + + /** + * Find type bound to specified name, if there is one; returns bound type if so, null if not. + */ + public JavaType findBoundType(String name) + { + for (int i = 0, len = _names.length; i < len; ++i) { + if (name.equals(_names[i])) { + JavaType t = _types[i]; + if (t instanceof ResolvedRecursiveType) { + ResolvedRecursiveType rrt = (ResolvedRecursiveType) t; + JavaType t2 = rrt.getSelfReferencedType(); + if (t2 != null) { + t = t2; + } else { + /* 25-Feb-2016, tatu: Looks like a potential problem, but alas + * we have a test where this should NOT fail and things... seem + * to work. So be it. + */ +/* + throw new IllegalStateException(String.format +("Unresolved ResolvedRecursiveType for parameter '%s' (index #%d; erased type %s)", +name, i, t.getRawClass())); +*/ + } + } + return t; + } + } + return null; + } + + public boolean isEmpty() { + return (_types.length == 0); + } + + /** + * Returns number of bindings contained + */ + public int size() { + return _types.length; + } + + public String getBoundName(int index) + { + if (index < 0 || index >= _names.length) { + return null; + } + return _names[index]; + } + + public JavaType getBoundType(int index) + { + if (index < 0 || index >= _types.length) { + return null; + } + return _types[index]; + } + + /** + * Accessor for getting bound types in declaration order + */ + public List getTypeParameters() + { + if (_types.length == 0) { + return Collections.emptyList(); + } + return Arrays.asList(_types); + } + + /** + * @since 2.3 + */ + public boolean hasUnbound(String name) { + if (_unboundVariables != null) { + for (int i = _unboundVariables.length; --i >= 0; ) { + if (name.equals(_unboundVariables[i])) { + return true; + } + } + } + return false; + } + + /* + /********************************************************************** + /* Standard methods + /********************************************************************** + */ + + @Override public String toString() + { + if (_types.length == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append('<'); + for (int i = 0, len = _types.length; i < len; ++i) { + if (i > 0) { + sb.append(','); + } +// sb = _types[i].appendBriefDescription(sb); + String sig = _types[i].getGenericSignature(); + sb.append(sig); + } + sb.append('>'); + return sb.toString(); + } + + @Override public int hashCode() { return _hashCode; } + + @Override public boolean equals(Object o) + { + if (o == this) return true; + if (o == null || o.getClass() != getClass()) return false; + TypeBindings other = (TypeBindings) o; + int len = _types.length; + if (len != other.size()) { + return false; + } + JavaType[] otherTypes = other._types; + for (int i = 0; i < len; ++i) { + if (!otherTypes[i].equals(_types[i])) { + return false; + } + } + return true; + } + + /* + /********************************************************************** + /* Package accessible methods + /********************************************************************** + */ + + protected JavaType[] typeParameterArray() { + return _types; + } + + /* + /********************************************************************** + /* Helper classes + /********************************************************************** + */ + + // 30-Oct-2015, tatu: Surprising, but looks like type parameters access can be bit of + // a hot spot. So avoid for a small number of common generic types. Note that we do + // need both common abstract types and concrete ones; latter for specialization + + /** + * Helper class that contains simple logic for avoiding repeated lookups via + * {@link Class#getTypeParameters()} as that can be a performance issue for + * some use cases (wasteful, usually one-off or not reusing mapper). + * Partly isolated to avoid initialization for cases where no generic types are + * used. + */ + static class TypeParamStash { + private final static TypeVariable[] VARS_ABSTRACT_LIST = AbstractList.class.getTypeParameters(); + private final static TypeVariable[] VARS_COLLECTION = Collection.class.getTypeParameters(); + private final static TypeVariable[] VARS_ITERABLE = Iterable.class.getTypeParameters(); + private final static TypeVariable[] VARS_LIST = List.class.getTypeParameters(); + private final static TypeVariable[] VARS_ARRAY_LIST = ArrayList.class.getTypeParameters(); + + private final static TypeVariable[] VARS_MAP = Map.class.getTypeParameters(); + private final static TypeVariable[] VARS_HASH_MAP = HashMap.class.getTypeParameters(); + private final static TypeVariable[] VARS_LINKED_HASH_MAP = LinkedHashMap.class.getTypeParameters(); + + public static TypeVariable[] paramsFor1(Class erasedType) + { + if (erasedType == Collection.class) { + return VARS_COLLECTION; + } + if (erasedType == List.class) { + return VARS_LIST; + } + if (erasedType == ArrayList.class) { + return VARS_ARRAY_LIST; + } + if (erasedType == AbstractList.class) { + return VARS_ABSTRACT_LIST; + } + if (erasedType == Iterable.class) { + return VARS_ITERABLE; + } + return erasedType.getTypeParameters(); + } + + public static TypeVariable[] paramsFor2(Class erasedType) + { + if (erasedType == Map.class) { + return VARS_MAP; + } + if (erasedType == HashMap.class) { + return VARS_HASH_MAP; + } + if (erasedType == LinkedHashMap.class) { + return VARS_LINKED_HASH_MAP; + } + return erasedType.getTypeParameters(); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeFactory.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeFactory.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeFactory.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1377 @@ +package com.fasterxml.jackson.databind.type; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.lang.reflect.*; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.util.ArrayBuilders; +import com.fasterxml.jackson.databind.util.ClassUtil; +import com.fasterxml.jackson.databind.util.LRUMap; + +/** + * Class used for creating concrete {@link JavaType} instances, + * given various inputs. + *

+ * Instances of this class are accessible using {@link com.fasterxml.jackson.databind.ObjectMapper} + * as well as many objects it constructs (like +* {@link com.fasterxml.jackson.databind.DeserializationConfig} and + * {@link com.fasterxml.jackson.databind.SerializationConfig})), + * but usually those objects also + * expose convenience methods (constructType). + * So, you can do for example: + *

+ *   JavaType stringType = mapper.constructType(String.class);
+ *
+ * However, more advanced methods are only exposed by factory so that you + * may need to use: + *
+ *   JavaType stringCollection = mapper.getTypeFactory().constructCollectionType(List.class, String.class);
+ *
+ */ +@SuppressWarnings({"rawtypes" }) +public final class TypeFactory + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + private final static JavaType[] NO_TYPES = new JavaType[0]; + + /** + * Globally shared singleton. Not accessed directly; non-core + * code should use per-ObjectMapper instance (via configuration objects). + * Core Jackson code uses {@link #defaultInstance} for accessing it. + */ + protected final static TypeFactory instance = new TypeFactory(); + + protected final static TypeBindings EMPTY_BINDINGS = TypeBindings.emptyBindings(); + + /* + /********************************************************** + /* Constants for "well-known" classes + /********************************************************** + */ + + // // // Let's assume that a small set of core primitive/basic types + // // // will not be modified, and can be freely shared to streamline + // // // parts of processing + + private final static Class CLS_STRING = String.class; + private final static Class CLS_OBJECT = Object.class; + + private final static Class CLS_COMPARABLE = Comparable.class; + private final static Class CLS_CLASS = Class.class; + private final static Class CLS_ENUM = Enum.class; + + private final static Class CLS_BOOL = Boolean.TYPE; + private final static Class CLS_INT = Integer.TYPE; + private final static Class CLS_LONG = Long.TYPE; + + /* + /********************************************************** + /* Cached pre-constructed JavaType instances + /********************************************************** + */ + + // note: these are primitive, hence no super types + protected final static SimpleType CORE_TYPE_BOOL = new SimpleType(CLS_BOOL); + protected final static SimpleType CORE_TYPE_INT = new SimpleType(CLS_INT); + protected final static SimpleType CORE_TYPE_LONG = new SimpleType(CLS_LONG); + + // and as to String... well, for now, ignore its super types + protected final static SimpleType CORE_TYPE_STRING = new SimpleType(CLS_STRING); + + // @since 2.7 + protected final static SimpleType CORE_TYPE_OBJECT = new SimpleType(CLS_OBJECT); + + /** + * Cache {@link Comparable} because it is both parameteric (relatively costly to + * resolve) and mostly useless (no special handling), better handle directly + * + * @since 2.7 + */ + protected final static SimpleType CORE_TYPE_COMPARABLE = new SimpleType(CLS_COMPARABLE); + + /** + * Cache {@link Enum} because it is parametric AND self-referential (costly to + * resolve) and useless in itself (no special handling). + * + * @since 2.7 + */ + protected final static SimpleType CORE_TYPE_ENUM = new SimpleType(CLS_ENUM); + + /** + * Cache {@link Class} because it is nominally parametric, but has no really + * useful information. + * + * @since 2.7 + */ + protected final static SimpleType CORE_TYPE_CLASS = new SimpleType(CLS_CLASS); + + /** + * Since type resolution can be expensive (specifically when resolving + * actual generic types), we will use small cache to avoid repetitive + * resolution of core types + */ + protected final LRUMap, JavaType> _typeCache = new LRUMap, JavaType>(16, 100); + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Registered {@link TypeModifier}s: objects that can change details + * of {@link JavaType} instances factory constructs. + */ + protected final TypeModifier[] _modifiers; + + protected final TypeParser _parser; + + /** + * ClassLoader used by this factory [databind#624]. + */ + protected final ClassLoader _classLoader; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + private TypeFactory() { + _parser = new TypeParser(this); + _modifiers = null; + _classLoader = null; + } + + protected TypeFactory(TypeParser p, TypeModifier[] mods) { + this(p, mods, null); + } + + protected TypeFactory(TypeParser p, TypeModifier[] mods, ClassLoader classLoader) { + // As per [databind#894] must ensure we have back-linkage from TypeFactory: + _parser = p.withFactory(this); + _modifiers = mods; + _classLoader = classLoader; + } + + public TypeFactory withModifier(TypeModifier mod) + { + if (mod == null) { // mostly for unit tests + return new TypeFactory(_parser, _modifiers, _classLoader); + } + if (_modifiers == null) { + return new TypeFactory(_parser, new TypeModifier[] { mod }, _classLoader); + } + return new TypeFactory(_parser, ArrayBuilders.insertInListNoDup(_modifiers, mod), _classLoader); + } + + public TypeFactory withClassLoader(ClassLoader classLoader) { + return new TypeFactory(_parser, _modifiers, classLoader); + } + + /** + * Method used to access the globally shared instance, which has + * no custom configuration. Used by ObjectMapper to + * get the default factory when constructed. + */ + public static TypeFactory defaultInstance() { return instance; } + + /** + * Method that will clear up any cached type definitions that may + * be cached by this {@link TypeFactory} instance. + * This method should not be commonly used, that is, only use it + * if you know there is a problem with retention of type definitions; + * the most likely (and currently only known) problem is retention + * of {@link Class} instances via {@link JavaType} reference. + * + * @since 2.4.1 + */ + public void clearCache() { + _typeCache.clear(); + } + + public ClassLoader getClassLoader() { + return _classLoader; + } + + /* + /********************************************************** + /* Static methods for non-instance-specific functionality + /********************************************************** + */ + + /** + * Method for constructing a marker type that indicates missing generic + * type information, which is handled same as simple type for + * java.lang.Object. + */ + public static JavaType unknownType() { + return defaultInstance()._unknownType(); + } + + /** + * Static helper method that can be called to figure out type-erased + * call for given JDK type. It can be called statically since type resolution + * process can never change actual type-erased class; thereby static + * default instance is used for determination. + */ + public static Class rawClass(Type t) { + if (t instanceof Class) { + return (Class) t; + } + // Should be able to optimize bit more in future... + return defaultInstance().constructType(t).getRawClass(); + } + + /* + /********************************************************** + /* Low-level helper methods + /********************************************************** + */ + + /** + * Low-level lookup method moved from {@link com.fasterxml.jackson.databind.util.ClassUtil}, + * to allow for overriding of lookup functionality in environments like OSGi. + * + * @since 2.6 + */ + public Class findClass(String className) throws ClassNotFoundException + { + if (className.indexOf('.') < 0) { + Class prim = _findPrimitive(className); + if (prim != null) { + return prim; + } + } + // Two-phase lookup: first using context ClassLoader; then default + Throwable prob = null; + ClassLoader loader = this.getClassLoader(); + if (loader == null) { + loader = Thread.currentThread().getContextClassLoader(); + } + if (loader != null) { + try { + return classForName(className, true, loader); + } catch (Exception e) { + prob = ClassUtil.getRootCause(e); + } + } + try { + return classForName(className); + } catch (Exception e) { + if (prob == null) { + prob = ClassUtil.getRootCause(e); + } + } + if (prob instanceof RuntimeException) { + throw (RuntimeException) prob; + } + throw new ClassNotFoundException(prob.getMessage(), prob); + } + + protected Class classForName(String name, boolean initialize, + ClassLoader loader) throws ClassNotFoundException { + return Class.forName(name, true, loader); + } + + protected Class classForName(String name) throws ClassNotFoundException { + return Class.forName(name); + } + + protected Class _findPrimitive(String className) + { + if ("int".equals(className)) return Integer.TYPE; + if ("long".equals(className)) return Long.TYPE; + if ("float".equals(className)) return Float.TYPE; + if ("double".equals(className)) return Double.TYPE; + if ("boolean".equals(className)) return Boolean.TYPE; + if ("byte".equals(className)) return Byte.TYPE; + if ("char".equals(className)) return Character.TYPE; + if ("short".equals(className)) return Short.TYPE; + if ("void".equals(className)) return Void.TYPE; + return null; + } + + /* + /********************************************************** + /* Type conversion, parameterization resolution methods + /********************************************************** + */ + + /** + * Factory method for creating a subtype of given base type, as defined + * by specified subclass; but retaining generic type information if any. + * Can be used, for example, to get equivalent of "HashMap<String,Integer>" + * from "Map<String,Integer>" by giving HashMap.class + * as subclass. + */ + public JavaType constructSpecializedType(JavaType baseType, Class subclass) + { + // simple optimization to avoid costly introspection if type-erased type does NOT differ + final Class rawBase = baseType.getRawClass(); + if (rawBase == subclass) { + return baseType; + } + + JavaType newType; + + // also: if we start from untyped, not much to save + do { // bogus loop to be able to break + if (rawBase == Object.class) { + newType = _fromClass(null, subclass, TypeBindings.emptyBindings()); + break; + } + if (!rawBase.isAssignableFrom(subclass)) { + throw new IllegalArgumentException(String.format( + "Class %s not subtype of %s", subclass.getName(), baseType)); + } + // A few special cases where we can simplify handling: + + // (1) Original target type has no generics -- just resolve subtype + if (baseType.getBindings().isEmpty()) { + newType = _fromClass(null, subclass, TypeBindings.emptyBindings()); + break; + } + // (2) A small set of "well-known" List/Map subtypes where can take a short-cut + if (baseType.isContainerType()) { + if (baseType.isMapLikeType()) { + if ((subclass == HashMap.class) + || (subclass == LinkedHashMap.class) + || (subclass == EnumMap.class) + || (subclass == TreeMap.class)) { + newType = _fromClass(null, subclass, + TypeBindings.create(subclass, baseType.getKeyType(), baseType.getContentType())); + break; + } + } else if (baseType.isCollectionLikeType()) { + if ((subclass == ArrayList.class) + || (subclass == LinkedList.class) + || (subclass == HashSet.class) + || (subclass == TreeSet.class)) { + newType = _fromClass(null, subclass, + TypeBindings.create(subclass, baseType.getContentType())); + break; + } + // 29-Oct-2015, tatu: One further shortcut: there are variants of `EnumSet`, + // but they are impl details and we basically do not care... + if (rawBase == EnumSet.class) { + return baseType; + } + } + } + // (3) Sub-class does not take type parameters -- just resolve subtype + int typeParamCount = subclass.getTypeParameters().length; + if (typeParamCount == 0) { + newType = _fromClass(null, subclass, TypeBindings.emptyBindings()); + break; + } + + // If not, we'll need to do more thorough forward+backwards resolution. Sigh. + // !!! TODO (as of 28-Jan-2016, at least) + + // 20-Oct-2015, tatu: Container, Map-types somewhat special. There is + // a way to fully resolve and merge hierarchies; but that gets expensive + // so let's, for now, try to create close-enough approximation that + // is not 100% same, structurally, but has equivalent information for + // our specific neeeds. + // 29-Mar-2016, tatu: See [databind#1173] (and test `TypeResolverTest`) + // for a case where this code does get invoked: not ideal + if (baseType.isInterface()) { + newType = baseType.refine(subclass, TypeBindings.emptyBindings(), null, + new JavaType[] { baseType }); + } else { + newType = baseType.refine(subclass, TypeBindings.emptyBindings(), baseType, + NO_TYPES); + } + // Only SimpleType returns null, but if so just resolve regularly + if (newType == null) { + // But otherwise gets bit tricky, as we need to partially resolve the type hierarchy + // (hopefully passing null Class for root is ok) + TypeBindings tb = null; + + // 14-Apr-2016, tatu: One possible short-cut; if type parameter counts + // match, chances are they ought to match. Let's take our chances... + if (baseType.containedTypeCount() == typeParamCount) { + if (typeParamCount == 1) { + tb = TypeBindings.create(subclass, baseType.containedType(0)); + } else if (typeParamCount == 2) { + tb = TypeBindings.create(subclass, baseType.containedType(0), + baseType.containedType(1)); + } + } + newType = _fromClass(null, subclass, + (tb == null) ? TypeBindings.emptyBindings() : tb); + } + } while (false); + + // except possibly handlers +// newType = newType.withHandlersFrom(baseType); + return newType; + + // 20-Oct-2015, tatu: Old simplistic approach + + /* + // Currently mostly SimpleType instances can become something else + if (baseType instanceof SimpleType) { + // and only if subclass is an array, Collection or Map + if (subclass.isArray() + || Map.class.isAssignableFrom(subclass) + || Collection.class.isAssignableFrom(subclass)) { + // need to assert type compatibility... + if (!baseType.getRawClass().isAssignableFrom(subclass)) { + throw new IllegalArgumentException("Class "+subclass.getClass().getName()+" not subtype of "+baseType); + } + // this _should_ work, right? + JavaType subtype = _fromClass(null, subclass, TypeBindings.emptyBindings()); + // one more thing: handlers to copy? + Object h = baseType.getValueHandler(); + if (h != null) { + subtype = subtype.withValueHandler(h); + } + h = baseType.getTypeHandler(); + if (h != null) { + subtype = subtype.withTypeHandler(h); + } + return subtype; + } + } + // But there is the need for special case for arrays too, it seems + if (baseType instanceof ArrayType) { + if (subclass.isArray()) { + // actually see if it might be a no-op first: + ArrayType at = (ArrayType) baseType; + Class rawComp = subclass.getComponentType(); + if (at.getContentType().getRawClass() == rawComp) { + return baseType; + } + JavaType componentType = _fromAny(null, rawComp, null); + return ((ArrayType) baseType).withComponentType(componentType); + } + } + + // otherwise regular narrowing should work just fine + return baseType.narrowBy(subclass); + */ + } + + /** + * Method similar to {@link #constructSpecializedType}, but that creates a + * less-specific type of given type. Usually this is as simple as simply + * finding super-type with type erasure of superClass, but + * there may be need for some additional work-arounds. + * + * @param superClass + * + * @since 2.7 + */ + public JavaType constructGeneralizedType(JavaType baseType, Class superClass) + { + // simple optimization to avoid costly introspection if type-erased type does NOT differ + final Class rawBase = baseType.getRawClass(); + if (rawBase == superClass) { + return baseType; + } + JavaType superType = baseType.findSuperType(superClass); + if (superType == null) { + // Most likely, caller did not verify sub/super-type relationship + if (!superClass.isAssignableFrom(rawBase)) { + throw new IllegalArgumentException(String.format( + "Class %s not a super-type of %s", superClass.getName(), baseType)); + } + // 01-Nov-2015, tatu: Should never happen, but ch + throw new IllegalArgumentException(String.format( + "Internal error: class %s not included as super-type for %s", + superClass.getName(), baseType)); + } + return superType; + } + + /** + * Factory method for constructing a {@link JavaType} out of its canonical + * representation (see {@link JavaType#toCanonical()}). + * + * @param canonical Canonical string representation of a type + * + * @throws IllegalArgumentException If canonical representation is malformed, + * or class that type represents (including its generic parameters) is + * not found + */ + public JavaType constructFromCanonical(String canonical) throws IllegalArgumentException + { + return _parser.parse(canonical); + } + + /** + * Method that is to figure out actual type parameters that given + * class binds to generic types defined by given (generic) + * interface or class. + * This could mean, for example, trying to figure out + * key and value types for Map implementations. + * + * @param type Sub-type (leaf type) that implements expType + */ + public JavaType[] findTypeParameters(JavaType type, Class expType) + { + JavaType match = type.findSuperType(expType); + if (match == null) { + return NO_TYPES; + } + return match.getBindings().typeParameterArray(); + } + + /** + * @deprecated Since 2.7 resolve raw type first, then find type parameters + */ + @Deprecated // since 2.7 + public JavaType[] findTypeParameters(Class clz, Class expType, TypeBindings bindings) { + return findTypeParameters(constructType(clz, bindings), expType); + } + + /** + * @deprecated Since 2.7 resolve raw type first, then find type parameters + */ + @Deprecated // since 2.7 + public JavaType[] findTypeParameters(Class clz, Class expType) { + return findTypeParameters(constructType(clz), expType); + } + + /** + * Method that can be called to figure out more specific of two + * types (if they are related; that is, one implements or extends the + * other); or if not related, return the primary type. + * + * @param type1 Primary type to consider + * @param type2 Secondary type to consider + * + * @since 2.2 + */ + public JavaType moreSpecificType(JavaType type1, JavaType type2) + { + if (type1 == null) { + return type2; + } + if (type2 == null) { + return type1; + } + Class raw1 = type1.getRawClass(); + Class raw2 = type2.getRawClass(); + if (raw1 == raw2) { + return type1; + } + // TODO: maybe try sub-classing, to retain generic types? + if (raw1.isAssignableFrom(raw2)) { + return type2; + } + return type1; + } + + /* + /********************************************************** + /* Public factory methods + /********************************************************** + */ + + public JavaType constructType(Type type) { + return _fromAny(null, type, EMPTY_BINDINGS); + } + + public JavaType constructType(Type type, TypeBindings bindings) { + return _fromAny(null, type, bindings); + } + + public JavaType constructType(TypeReference typeRef) + { + // 19-Oct-2015, tatu: Simpler variant like so should work + return _fromAny(null, typeRef.getType(), EMPTY_BINDINGS); + + // but if not, due to funky sub-classing, type variables, what follows + // is a more complete processing a la Java ClassMate. + + /* + final Class refdRawType = typeRef.getClass(); + JavaType type = _fromClass(null, refdRawType, EMPTY_BINDINGS); + JavaType genType = type.findSuperType(TypeReference.class); + if (genType == null) { // sanity check; shouldn't occur + throw new IllegalArgumentException("Unparameterized GenericType instance ("+refdRawType.getName()+")"); + } + TypeBindings b = genType.getBindings(); + JavaType[] params = b.typeParameterArray(); + if (params.length == 0) { + throw new IllegalArgumentException("Unparameterized GenericType instance ("+refdRawType.getName()+")"); + } + return params[0]; + */ + } + + /** + * @deprecated Since 2.7 (accidentally removed in 2.7.0; added back in 2.7.1) + */ + @Deprecated + public JavaType constructType(Type type, Class contextClass) { + TypeBindings bindings = (contextClass == null) + ? TypeBindings.emptyBindings() : constructType(contextClass).getBindings(); + return _fromAny(null, type, bindings); + } + + /** + * @deprecated Since 2.7 (accidentally removed in 2.7.0; added back in 2.7.1) + */ + @Deprecated + public JavaType constructType(Type type, JavaType contextType) { + TypeBindings bindings = (contextType == null) + ? TypeBindings.emptyBindings() : contextType.getBindings(); + return _fromAny(null, type, bindings); + } + + /* + /********************************************************** + /* Direct factory methods + /********************************************************** + */ + + /** + * Method for constructing an {@link ArrayType}. + *

+ * NOTE: type modifiers are NOT called on array type itself; but are called + * for element type (and other contained types) + */ + public ArrayType constructArrayType(Class elementType) { + return ArrayType.construct(_fromAny(null, elementType, null), null); + } + + /** + * Method for constructing an {@link ArrayType}. + *

+ * NOTE: type modifiers are NOT called on array type itself; but are called + * for contained types. + */ + public ArrayType constructArrayType(JavaType elementType) { + return ArrayType.construct(elementType, null); + } + + /** + * Method for constructing a {@link CollectionType}. + *

+ * NOTE: type modifiers are NOT called on Collection type itself; but are called + * for contained types. + */ + public CollectionType constructCollectionType(Class collectionClass, Class elementClass) { + return constructCollectionType(collectionClass, + _fromClass(null, elementClass, EMPTY_BINDINGS)); + } + + /** + * Method for constructing a {@link CollectionType}. + *

+ * NOTE: type modifiers are NOT called on Collection type itself; but are called + * for contained types. + */ + public CollectionType constructCollectionType(Class collectionClass, JavaType elementType) { + // 19-Oct-2015, tatu: Allow case of no-type-variables, since it seems likely to be + // a valid use case here + return (CollectionType) _fromClass(null, collectionClass, + TypeBindings.create(collectionClass, elementType)); + } + + /** + * Method for constructing a {@link CollectionLikeType}. + *

+ * NOTE: type modifiers are NOT called on constructed type itself; but are called + * for contained types. + */ + public CollectionLikeType constructCollectionLikeType(Class collectionClass, Class elementClass) { + return constructCollectionLikeType(collectionClass, + _fromClass(null, elementClass, EMPTY_BINDINGS)); + } + + /** + * Method for constructing a {@link CollectionLikeType}. + *

+ * NOTE: type modifiers are NOT called on constructed type itself; but are called + * for contained types. + */ + public CollectionLikeType constructCollectionLikeType(Class collectionClass, JavaType elementType) { + JavaType type = _fromClass(null, collectionClass, + TypeBindings.createIfNeeded(collectionClass, elementType)); + if (type instanceof CollectionLikeType) { + return (CollectionLikeType) type; + } + return CollectionLikeType.upgradeFrom(type, elementType); + } + + /** + * Method for constructing a {@link MapType} instance + *

+ * NOTE: type modifiers are NOT called on constructed type itself; but are called + * for contained types. + */ + public MapType constructMapType(Class mapClass, Class keyClass, Class valueClass) { + JavaType kt, vt; + if (mapClass == Properties.class) { + kt = vt = CORE_TYPE_STRING; + } else { + kt = _fromClass(null, keyClass, EMPTY_BINDINGS); + vt = _fromClass(null, valueClass, EMPTY_BINDINGS); + } + return constructMapType(mapClass, kt, vt); + } + + /** + * Method for constructing a {@link MapType} instance + *

+ * NOTE: type modifiers are NOT called on constructed type itself; but are called + * for contained types. + */ + public MapType constructMapType(Class mapClass, JavaType keyType, JavaType valueType) { + return (MapType) _fromClass(null, mapClass, + TypeBindings.create(mapClass, new JavaType[] { + keyType, valueType + })); + } + + /** + * Method for constructing a {@link MapLikeType} instance + *

+ * NOTE: type modifiers are NOT called on constructed type itself; but are called + * for contained types. + */ + public MapLikeType constructMapLikeType(Class mapClass, Class keyClass, Class valueClass) { + return constructMapLikeType(mapClass, + _fromClass(null, keyClass, EMPTY_BINDINGS), + _fromClass(null, valueClass, EMPTY_BINDINGS)); + } + + /** + * Method for constructing a {@link MapLikeType} instance + *

+ * NOTE: type modifiers are NOT called on constructed type itself; but are called + * for contained types. + */ + public MapLikeType constructMapLikeType(Class mapClass, JavaType keyType, JavaType valueType) { + // 19-Oct-2015, tatu: Allow case of no-type-variables, since it seems likely to be + // a valid use case here + JavaType type = _fromClass(null, mapClass, + TypeBindings.createIfNeeded(mapClass, new JavaType[] { keyType, valueType })); + if (type instanceof MapLikeType) { + return (MapLikeType) type; + } + return MapLikeType.upgradeFrom(type, keyType, valueType); + } + + /** + * Method for constructing a type instance with specified parameterization. + *

+ * NOTE: was briefly deprecated for 2.6. + */ + public JavaType constructSimpleType(Class rawType, JavaType[] parameterTypes) { + return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes)); + } + + /** + * Method for constructing a type instance with specified parameterization. + * + * @since 2.6 + * + * @deprecated Since 2.7 + */ + @Deprecated + public JavaType constructSimpleType(Class rawType, Class parameterTarget, + JavaType[] parameterTypes) + { + return constructSimpleType(rawType, parameterTypes); + } + + /** + * @since 2.6 + */ + public JavaType constructReferenceType(Class rawType, JavaType referredType) + { + return ReferenceType.construct(rawType, null, // no bindings + null, null, // or super-class, interfaces? + referredType); + } + + /** + * Method that will force construction of a simple type, without trying to + * check for more specialized types. + *

+ * NOTE: no type modifiers are called on type either, so calling this method + * should only be used if caller really knows what it's doing... + */ + public JavaType uncheckedSimpleType(Class cls) { + // 18-Oct-2015, tatu: Not sure how much problem missing super-type info is here + return _constructSimple(cls, EMPTY_BINDINGS, null, null); + } + + /** + * Factory method for constructing {@link JavaType} that + * represents a parameterized type. For example, to represent + * type List<Set<Integer>>, you could + * call + *

+     *  JavaType inner = TypeFactory.constructParametrizedType(Set.class, Set.class, Integer.class);
+     *  return TypeFactory.constructParametrizedType(ArrayList.class, List.class, inner);
+     *
+ *

+ * The reason for first two arguments to be separate is that parameterization may + * apply to a super-type. For example, if generic type was instead to be + * constructed for ArrayList<Integer>, the usual call would be: + *

+     *  TypeFactory.constructParametrizedType(ArrayList.class, List.class, Integer.class);
+     *
+ * since parameterization is applied to {@link java.util.List}. + * In most cases distinction does not matter, but there are types where it does; + * one such example is parameterization of types that implement {@link java.util.Iterator}. + *

+ * NOTE: type modifiers are NOT called on constructed type. + * + * @param parametrized Actual full type + * @param parameterClasses Type parameters to apply + * + * @since 2.5 NOTE: was briefly deprecated for 2.6 + */ + public JavaType constructParametricType(Class parametrized, Class... parameterClasses) { + int len = parameterClasses.length; + JavaType[] pt = new JavaType[len]; + for (int i = 0; i < len; ++i) { + pt[i] = _fromClass(null, parameterClasses[i], null); + } + return constructParametricType(parametrized, pt); + } + + /** + * Factory method for constructing {@link JavaType} that + * represents a parameterized type. For example, to represent + * type List<Set<Integer>>, you could + * call + *

+     *  JavaType inner = TypeFactory.constructParametrizedType(Set.class, Set.class, Integer.class);
+     *  return TypeFactory.constructParametrizedType(ArrayList.class, List.class, inner);
+     *
+ *

+ * The reason for first two arguments to be separate is that parameterization may + * apply to a super-type. For example, if generic type was instead to be + * constructed for ArrayList<Integer>, the usual call would be: + *

+     *  TypeFactory.constructParametrizedType(ArrayList.class, List.class, Integer.class);
+     *
+ * since parameterization is applied to {@link java.util.List}. + * In most cases distinction does not matter, but there are types where it does; + * one such example is parameterization of types that implement {@link java.util.Iterator}. + *

+ * NOTE: type modifiers are NOT called on constructed type. + * + * @param rawType Actual type-erased type + * @param parameterTypes Type parameters to apply + * + * @since 2.5 NOTE: was briefly deprecated for 2.6 + */ + public JavaType constructParametricType(Class rawType, JavaType... parameterTypes) + { + return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes)); + } + + /** + * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7) + */ + public JavaType constructParametrizedType(Class parametrized, Class parametersFor, + JavaType... parameterTypes) + { + return constructParametricType(parametrized, parameterTypes); + } + + /** + * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7) + */ + public JavaType constructParametrizedType(Class parametrized, Class parametersFor, + Class... parameterClasses) + { + return constructParametricType(parametrized, parameterClasses); + } + + /* + /********************************************************** + /* Direct factory methods for "raw" variants, used when + /* parameterization is unknown + /********************************************************** + */ + + /** + * Method that can be used to construct "raw" Collection type; meaning that its + * parameterization is unknown. + * This is similar to using Object.class parameterization, + * and is equivalent to calling: + *

+     *  typeFactory.constructCollectionType(collectionClass, typeFactory.unknownType());
+     *
+ *

+ * This method should only be used if parameterization is completely unavailable. + */ + public CollectionType constructRawCollectionType(Class collectionClass) { + return constructCollectionType(collectionClass, unknownType()); + } + + /** + * Method that can be used to construct "raw" Collection-like type; meaning that its + * parameterization is unknown. + * This is similar to using Object.class parameterization, + * and is equivalent to calling: + *

+     *  typeFactory.constructCollectionLikeType(collectionClass, typeFactory.unknownType());
+     *
+ *

+ * This method should only be used if parameterization is completely unavailable. + */ + public CollectionLikeType constructRawCollectionLikeType(Class collectionClass) { + return constructCollectionLikeType(collectionClass, unknownType()); + } + + /** + * Method that can be used to construct "raw" Map type; meaning that its + * parameterization is unknown. + * This is similar to using Object.class parameterization, + * and is equivalent to calling: + *

+     *  typeFactory.constructMapType(collectionClass, typeFactory.unknownType(), typeFactory.unknownType());
+     *
+ *

+ * This method should only be used if parameterization is completely unavailable. + */ + public MapType constructRawMapType(Class mapClass) { + return constructMapType(mapClass, unknownType(), unknownType()); + } + + /** + * Method that can be used to construct "raw" Map-like type; meaning that its + * parameterization is unknown. + * This is similar to using Object.class parameterization, + * and is equivalent to calling: + *

+     *  typeFactory.constructMapLikeType(collectionClass, typeFactory.unknownType(), typeFactory.unknownType());
+     *
+ *

+ * This method should only be used if parameterization is completely unavailable. + */ + public MapLikeType constructRawMapLikeType(Class mapClass) { + return constructMapLikeType(mapClass, unknownType(), unknownType()); + } + + /* + /********************************************************** + /* Low-level factory methods + /********************************************************** + */ + + private JavaType _mapType(Class rawClass, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + JavaType kt, vt; + + // 28-May-2015, tatu: Properties are special, as per [databind#810]; fake "correct" parameter sig + if (rawClass == Properties.class) { + kt = vt = CORE_TYPE_STRING; + } else { + List typeParams = bindings.getTypeParameters(); + // ok to have no types ("raw") + switch (typeParams.size()) { + case 0: // acceptable? + kt = vt = _unknownType(); + break; + case 2: + kt = typeParams.get(0); + vt = typeParams.get(1); + break; + default: + throw new IllegalArgumentException("Strange Map type "+rawClass.getName()+": can not determine type parameters"); + } + } + return MapType.construct(rawClass, bindings, superClass, superInterfaces, kt, vt); + } + + private JavaType _collectionType(Class rawClass, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + List typeParams = bindings.getTypeParameters(); + // ok to have no types ("raw") + JavaType ct; + if (typeParams.isEmpty()) { + ct = _unknownType(); + } else if (typeParams.size() == 1) { + ct = typeParams.get(0); + } else { + throw new IllegalArgumentException("Strange Collection type "+rawClass.getName()+": can not determine type parameters"); + } + return CollectionType.construct(rawClass, bindings, superClass, superInterfaces, ct); + } + + private JavaType _referenceType(Class rawClass, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + List typeParams = bindings.getTypeParameters(); + // ok to have no types ("raw") + JavaType ct; + if (typeParams.isEmpty()) { + ct = _unknownType(); + } else if (typeParams.size() == 1) { + ct = typeParams.get(0); + } else { + throw new IllegalArgumentException("Strange Reference type "+rawClass.getName()+": can not determine type parameters"); + } + return ReferenceType.construct(rawClass, bindings, superClass, superInterfaces, ct); + } + + /** + * Factory method to call when no special {@link JavaType} is needed, + * no generic parameters are passed. Default implementation may check + * pre-constructed values for "well-known" types, but if none found + * will simply call {@link #_newSimpleType} + * + * @since 2.7 + */ + protected JavaType _constructSimple(Class raw, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + if (bindings.isEmpty()) { + JavaType result = _findWellKnownSimple(raw); + if (result != null) { + return result; + } + } + return _newSimpleType(raw, bindings, superClass, superInterfaces); + } + + /** + * Factory method that is to create a new {@link SimpleType} with no + * checks whatsoever. Default implementation calls the single argument + * constructor of {@link SimpleType}. + * + * @since 2.7 + */ + protected JavaType _newSimpleType(Class raw, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + return new SimpleType(raw, bindings, superClass, superInterfaces); + } + + protected JavaType _unknownType() { + /* 15-Sep-2015, tatu: Prior to 2.7, we constructed new instance for each call. + * This may have been due to potential mutability of the instance; but that + * should not be issue any more, and creation is somewhat wasteful. So let's + * try reusing singleton/flyweight instance. + */ + return CORE_TYPE_OBJECT; + } + + /** + * Helper method called to see if requested, non-generic-parameterized + * type is one of common, "well-known" types, instances of which are + * pre-constructed and do not need dynamic caching. + * + * @since 2.7 + */ + protected JavaType _findWellKnownSimple(Class clz) { + if (clz.isPrimitive()) { + if (clz == CLS_BOOL) return CORE_TYPE_BOOL; + if (clz == CLS_INT) return CORE_TYPE_INT; + if (clz == CLS_LONG) return CORE_TYPE_LONG; + } else { + if (clz == CLS_STRING) return CORE_TYPE_STRING; + if (clz == CLS_OBJECT) return CORE_TYPE_OBJECT; // since 2.7 + } + return null; + } + + /* + /********************************************************** + /* Actual type resolution, traversal + /********************************************************** + */ + + /** + * Factory method that can be used if type information is passed + * as Java typing returned from getGenericXxx methods + * (usually for a return or argument type). + */ + protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings) + { + JavaType resultType; + + // simple class? + if (type instanceof Class) { + // Important: remove possible bindings since this is type-erased thingy + resultType = _fromClass(context, (Class) type, EMPTY_BINDINGS); + } + // But if not, need to start resolving. + else if (type instanceof ParameterizedType) { + resultType = _fromParamType(context, (ParameterizedType) type, bindings); + } + else if (type instanceof JavaType) { // [databind#116] + // no need to modify further if we already had JavaType + return (JavaType) type; + } + else if (type instanceof GenericArrayType) { + resultType = _fromArrayType(context, (GenericArrayType) type, bindings); + } + else if (type instanceof TypeVariable) { + resultType = _fromVariable(context, (TypeVariable) type, bindings); + } + else if (type instanceof WildcardType) { + resultType = _fromWildcard(context, (WildcardType) type, bindings); + } else { + // sanity check + throw new IllegalArgumentException("Unrecognized Type: "+((type == null) ? "[null]" : type.toString())); + } + /* 21-Feb-2016, nateB/tatu: as per [databind#1129] (applied for 2.7.2), + * we do need to let all kinds of types to be refined, esp. for Scala module. + */ + if (_modifiers != null) { + TypeBindings b = resultType.getBindings(); + if (b == null) { + b = EMPTY_BINDINGS; + } + for (TypeModifier mod : _modifiers) { + JavaType t = mod.modifyType(resultType, type, b, this); + if (t == null) { + throw new IllegalStateException(String.format( + "TypeModifier %s (of type %s) return null for type %s", + mod, mod.getClass().getName(), resultType)); + } + resultType = t; + } + } + return resultType; + } + + /** + * @param bindings Mapping of formal parameter declarations (for generic + * types) into actual types + */ + protected JavaType _fromClass(ClassStack context, Class rawType, TypeBindings bindings) + { + // Very first thing: small set of core types we know well: + JavaType result = _findWellKnownSimple(rawType); + if (result != null) { + return result; + } + // Barring that, we may have recently constructed an instance: + // !!! TODO 16-Oct-2015, tatu: For now let's only cached non-parameterized; otherwise + // need better cache key + boolean cachable = (bindings == null) || bindings.isEmpty(); + if (cachable) { + result = _typeCache.get(rawType); // ok, cache object is synced + if (result != null) { + return result; + } + } + + // 15-Oct-2015, tatu: recursive reference? + if (context == null) { + context = new ClassStack(rawType); + } else { + ClassStack prev = context.find(rawType); + if (prev != null) { + // Self-reference: needs special handling, then... + ResolvedRecursiveType selfRef = new ResolvedRecursiveType(rawType, EMPTY_BINDINGS); + prev.addSelfReference(selfRef); + return selfRef; + } + // no, but need to update context to allow for proper cycle resolution + context = context.child(rawType); + } + + // First: do we have an array type? + if (rawType.isArray()) { + result = ArrayType.construct(_fromAny(context, rawType.getComponentType(), bindings), + bindings); + } else { + // If not, need to proceed by first resolving parent type hierarchy + + JavaType superClass; + JavaType[] superInterfaces; + + if (rawType.isInterface()) { + superClass = null; + superInterfaces = _resolveSuperInterfaces(context, rawType, bindings); + } else { + // Note: even Enums can implement interfaces, so can not drop those + superClass = _resolveSuperClass(context, rawType, bindings); + superInterfaces = _resolveSuperInterfaces(context, rawType, bindings); + } + + // 19-Oct-2015, tatu: Bit messy, but we need to 'fix' java.util.Properties here... + if (rawType == Properties.class) { + result = MapType.construct(rawType, bindings, superClass, superInterfaces, + CORE_TYPE_STRING, CORE_TYPE_STRING); + } + // And then check what flavor of type we got. Start by asking resolved + // super-type if refinement is all that is needed? + else if (superClass != null) { + result = superClass.refine(rawType, bindings, superClass, superInterfaces); + } + // if not, perhaps we are now resolving a well-known class or interface? + if (result == null) { + result = _fromWellKnownClass(context, rawType, bindings, superClass, superInterfaces); + if (result == null) { + result = _fromWellKnownInterface(context, rawType, bindings, superClass, superInterfaces); + if (result == null) { + // but if nothing else, "simple" class for now: + result = _newSimpleType(rawType, bindings, superClass, superInterfaces); + } + } + } + } + context.resolveSelfReferences(result); + + if (cachable) { + _typeCache.putIfAbsent(rawType, result); // cache object syncs + } + return result; + } + + protected JavaType _resolveSuperClass(ClassStack context, Class rawType, TypeBindings parentBindings) + { + Type parent = ClassUtil.getGenericSuperclass(rawType); + if (parent == null) { + return null; + } + return _fromAny(context, parent, parentBindings); + } + + protected JavaType[] _resolveSuperInterfaces(ClassStack context, Class rawType, TypeBindings parentBindings) + { + Type[] types = ClassUtil.getGenericInterfaces(rawType); + if (types == null || types.length == 0) { + return NO_TYPES; + } + int len = types.length; + JavaType[] resolved = new JavaType[len]; + for (int i = 0; i < len; ++i) { + Type type = types[i]; + resolved[i] = _fromAny(context, type, parentBindings); + } + return resolved; + } + + /** + * Helper class used to check whether exact class for which type is being constructed + * is one of well-known base interfaces or classes that indicates alternate + * {@link JavaType} implementation. + */ + protected JavaType _fromWellKnownClass(ClassStack context, Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + // Quite simple when we resolving exact class/interface; start with that + if (rawType == Map.class) { + return _mapType(rawType, bindings, superClass, superInterfaces); + } + if (rawType == Collection.class) { + return _collectionType(rawType, bindings, superClass, superInterfaces); + } + // and since 2.6 one referential type + if (rawType == AtomicReference.class) { + return _referenceType(rawType, bindings, superClass, superInterfaces); + } + // 01-Nov-2015, tatu: As of 2.7, couple of potential `CollectionLikeType`s (like + // `Iterable`, `Iterator`), and `MapLikeType`s (`Map.Entry`) are not automatically + // detected, related to difficulties in propagating type upwards (Iterable, for + // example, is a weak, tag-on type). They may be detectable in future. + return null; + } + + protected JavaType _fromWellKnownInterface(ClassStack context, Class rawType, TypeBindings bindings, + JavaType superClass, JavaType[] superInterfaces) + { + // But that's not all: may be possible current type actually implements an + // interface type. So... + final int intCount = superInterfaces.length; + + for (int i = 0; i < intCount; ++i) { + JavaType result = superInterfaces[i].refine(rawType, bindings, superClass, superInterfaces); + if (result != null) { + return result; + } + } + return null; + } + + /** + * This method deals with parameterized types, that is, + * first class generic classes. + */ + protected JavaType _fromParamType(ClassStack context, ParameterizedType ptype, + TypeBindings parentBindings) + { + // Assumption here is we'll always get Class, not one of other Types + Class rawType = (Class) ptype.getRawType(); + + // 29-Oct-2015, tatu: For performance reasons, let's streamline handling of + // couple of not-so-useful parametric types + if (rawType == CLS_ENUM) { + return CORE_TYPE_ENUM; + } + if (rawType == CLS_COMPARABLE) { + return CORE_TYPE_COMPARABLE; + } + if (rawType == CLS_CLASS) { + return CORE_TYPE_CLASS; + } + + // First: what is the actual base type? One odd thing is that 'getRawType' + // returns Type, not Class as one might expect. But let's assume it is + // always of type Class: if not, need to add more code to resolve it to Class. + Type[] args = ptype.getActualTypeArguments(); + int paramCount = (args == null) ? 0 : args.length; + JavaType[] pt; + TypeBindings newBindings; + + if (paramCount == 0) { + newBindings = EMPTY_BINDINGS; + } else { + pt = new JavaType[paramCount]; + for (int i = 0; i < paramCount; ++i) { + pt[i] = _fromAny(context, args[i], parentBindings); + } + newBindings = TypeBindings.create(rawType, pt); + } + return _fromClass(context, rawType, newBindings); + } + + protected JavaType _fromArrayType(ClassStack context, GenericArrayType type, TypeBindings bindings) + { + JavaType elementType = _fromAny(context, type.getGenericComponentType(), bindings); + return ArrayType.construct(elementType, bindings); + } + + protected JavaType _fromVariable(ClassStack context, TypeVariable var, TypeBindings bindings) + { + // ideally should find it via bindings: + final String name = var.getName(); + JavaType type = bindings.findBoundType(name); + if (type != null) { + return type; + } + // but if not, use bounds... note that approach here is simplistic; not taking + // into account possible multiple bounds, nor consider upper bounds. + if (bindings.hasUnbound(name)) { + return CORE_TYPE_OBJECT; + } + bindings = bindings.withUnboundVariable(name); + + Type[] bounds = var.getBounds(); + return _fromAny(context, bounds[0], bindings); + } + + protected JavaType _fromWildcard(ClassStack context, WildcardType type, TypeBindings bindings) + { + /* Similar to challenges with TypeVariable, we may have multiple upper bounds. + * But it is also possible that if upper bound defaults to Object, we might + * want to consider lower bounds instead. + * For now, we won't try anything more advanced; above is just for future reference. + */ + return _fromAny(context, type.getUpperBounds()[0], bindings); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeModifier.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeModifier.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeModifier.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,36 @@ +package com.fasterxml.jackson.databind.type; + +import java.lang.reflect.Type; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Class that defines API that can be used to modify details of + * {@link JavaType} instances constructed using {@link TypeFactory}. + * Registered modifiers are called in order, to let them modify (or + * replace) basic type instance factory constructs. + * This is typically needed to support creation of + * {@link MapLikeType} and {@link CollectionLikeType} instances, + * as those can not be constructed in generic fashion. + */ +public abstract class TypeModifier +{ + /** + * Method called to let modifier change constructed type definition. + * Note that this is only guaranteed to be called for + * non-container types ("simple" types not recognized as arrays, + * java.util.Collection or java.util.Map). + * + * @param type Instance to modify + * @param jdkType JDK type that was used to construct instance to modify + * @param context Type resolution context used for the type + * @param typeFactory Type factory that can be used to construct parameter type; note, + * however, that care must be taken to avoid infinite loops -- specifically, do not + * construct instance of primary type itself + * + * @return Actual type instance to use; usually either type (as is or with + * modifications), or a newly constructed type instance based on it. Can not be null. + */ + public abstract JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, + TypeFactory typeFactory); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeParser.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeParser.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/TypeParser.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,138 @@ +package com.fasterxml.jackson.databind.type; + +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Simple recursive-descent parser for parsing canonical {@link JavaType} + * representations and constructing type instances. + */ +public class TypeParser + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final TypeFactory _factory; + + public TypeParser(TypeFactory f) { + _factory = f; + } + + /** + * @since 2.6.2 + */ + public TypeParser withFactory(TypeFactory f) { + return (f == _factory) ? this : new TypeParser(f); + } + + public JavaType parse(String canonical) throws IllegalArgumentException + { + canonical = canonical.trim(); + MyTokenizer tokens = new MyTokenizer(canonical); + JavaType type = parseType(tokens); + // must be end, now + if (tokens.hasMoreTokens()) { + throw _problem(tokens, "Unexpected tokens after complete type"); + } + return type; + } + + protected JavaType parseType(MyTokenizer tokens) + throws IllegalArgumentException + { + if (!tokens.hasMoreTokens()) { + throw _problem(tokens, "Unexpected end-of-string"); + } + Class base = findClass(tokens.nextToken(), tokens); + + // either end (ok, non generic type), or generics + if (tokens.hasMoreTokens()) { + String token = tokens.nextToken(); + if ("<".equals(token)) { + List parameterTypes = parseTypes(tokens); + TypeBindings b = TypeBindings.create(base, parameterTypes); + return _factory._fromClass(null, base, b); + } + // can be comma that separates types, or closing '>' + tokens.pushBack(token); + } + return _factory._fromClass(null, base, null); + } + + protected List parseTypes(MyTokenizer tokens) + throws IllegalArgumentException + { + ArrayList types = new ArrayList(); + while (tokens.hasMoreTokens()) { + types.add(parseType(tokens)); + if (!tokens.hasMoreTokens()) break; + String token = tokens.nextToken(); + if (">".equals(token)) return types; + if (!",".equals(token)) { + throw _problem(tokens, "Unexpected token '"+token+"', expected ',' or '>')"); + } + } + throw _problem(tokens, "Unexpected end-of-string"); + } + + protected Class findClass(String className, MyTokenizer tokens) + { + try { + return _factory.findClass(className); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw _problem(tokens, "Can not locate class '"+className+"', problem: "+e.getMessage()); + } + } + + protected IllegalArgumentException _problem(MyTokenizer tokens, String msg) + { + return new IllegalArgumentException("Failed to parse type '"+tokens.getAllInput() + +"' (remaining: '"+tokens.getRemainingInput()+"'): "+msg); + } + + final static class MyTokenizer + extends StringTokenizer + { + protected final String _input; + + protected int _index; + + protected String _pushbackToken; + + public MyTokenizer(String str) { + super(str, "<,>", true); + _input = str; + } + + @Override + public boolean hasMoreTokens() { + return (_pushbackToken != null) || super.hasMoreTokens(); + } + + @Override + public String nextToken() { + String token; + if (_pushbackToken != null) { + token = _pushbackToken; + _pushbackToken = null; + } else { + token = super.nextToken(); + } + _index += token.length(); + return token; + } + + public void pushBack(String token) { + _pushbackToken = token; + _index -= token.length(); + } + + public String getAllInput() { return _input; } + public String getUsedInput() { return _input.substring(0, _index); } + public String getRemainingInput() { return _input.substring(_index); } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/type/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,10 @@ +/** + * Package that contains concrete implementations of + * {@link com.fasterxml.jackson.databind.JavaType}, as + * well as the factory ({@link com.fasterxml.jackson.databind.type.TypeFactory}) for + * constructing instances from various input data types + * (like {@link java.lang.Class}, {@link java.lang.reflect.Type}) + * and programmatically (for structured types, arrays, + * {@link java.util.List}s and {@link java.util.Map}s). + */ +package com.fasterxml.jackson.databind.type; Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Annotations.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Annotations.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Annotations.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,24 @@ +package com.fasterxml.jackson.databind.util; + +import java.lang.annotation.Annotation; + +/** + * Interface that defines interface for accessing contents of a + * collection of annotations. This is needed when introspecting + * annotation-based features from different kinds of things, not + * just objects that Java Reflection interface exposes. + *

+ * Standard mutable implementation is {@link com.fasterxml.jackson.databind.introspect.AnnotationMap} + */ +public interface Annotations +{ + /** + * Main access method used to find value for given annotation. + */ + public A get(Class cls); + + /** + * Returns number of annotation entries in this collection. + */ + public int size(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ArrayBuilders.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ArrayBuilders.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ArrayBuilders.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,300 @@ +package com.fasterxml.jackson.databind.util; + +import java.lang.reflect.Array; +import java.util.*; + +/** + * Helper class that contains set of distinct builders for different + * arrays of primitive values. It also provides trivially simple + * reuse scheme, which assumes that caller knows not to use instances + * concurrently (which works ok with primitive arrays since they can + * not contain other non-primitive types). + * Also note that instances are not thread safe; the intent is that + * a builder is constructed on per-call (deserialization) basis. + */ +public final class ArrayBuilders +{ + private BooleanBuilder _booleanBuilder = null; + + // note: no need for char[] builder, assume they are Strings + + private ByteBuilder _byteBuilder = null; + private ShortBuilder _shortBuilder = null; + private IntBuilder _intBuilder = null; + private LongBuilder _longBuilder = null; + + private FloatBuilder _floatBuilder = null; + private DoubleBuilder _doubleBuilder = null; + + public ArrayBuilders() { } + + public BooleanBuilder getBooleanBuilder() + { + if (_booleanBuilder == null) { + _booleanBuilder = new BooleanBuilder(); + } + return _booleanBuilder; + } + + public ByteBuilder getByteBuilder() + { + if (_byteBuilder == null) { + _byteBuilder = new ByteBuilder(); + } + return _byteBuilder; + } + public ShortBuilder getShortBuilder() + { + if (_shortBuilder == null) { + _shortBuilder = new ShortBuilder(); + } + return _shortBuilder; + } + public IntBuilder getIntBuilder() + { + if (_intBuilder == null) { + _intBuilder = new IntBuilder(); + } + return _intBuilder; + } + public LongBuilder getLongBuilder() + { + if (_longBuilder == null) { + _longBuilder = new LongBuilder(); + } + return _longBuilder; + } + + public FloatBuilder getFloatBuilder() + { + if (_floatBuilder == null) { + _floatBuilder = new FloatBuilder(); + } + return _floatBuilder; + } + public DoubleBuilder getDoubleBuilder() + { + if (_doubleBuilder == null) { + _doubleBuilder = new DoubleBuilder(); + } + return _doubleBuilder; + } + + /* + /********************************************************** + /* Impl classes + /********************************************************** + */ + + public final static class BooleanBuilder + extends PrimitiveArrayBuilder + { + public BooleanBuilder() { } + @Override + public final boolean[] _constructArray(int len) { return new boolean[len]; } + } + + public final static class ByteBuilder + extends PrimitiveArrayBuilder + { + public ByteBuilder() { } + @Override + public final byte[] _constructArray(int len) { return new byte[len]; } + } + public final static class ShortBuilder + extends PrimitiveArrayBuilder + { + public ShortBuilder() { } + @Override + public final short[] _constructArray(int len) { return new short[len]; } + } + public final static class IntBuilder + extends PrimitiveArrayBuilder + { + public IntBuilder() { } + @Override + public final int[] _constructArray(int len) { return new int[len]; } + } + public final static class LongBuilder + extends PrimitiveArrayBuilder + { + public LongBuilder() { } + @Override + public final long[] _constructArray(int len) { return new long[len]; } + } + + public final static class FloatBuilder + extends PrimitiveArrayBuilder + { + public FloatBuilder() { } + @Override + public final float[] _constructArray(int len) { return new float[len]; } + } + public final static class DoubleBuilder + extends PrimitiveArrayBuilder + { + public DoubleBuilder() { } + @Override + public final double[] _constructArray(int len) { return new double[len]; } + } + + /* + /********************************************************** + /* Static helper methods + /********************************************************** + */ + + /** + * Helper method used for constructing simple value comparator used for + * comparing arrays for content equality. + *

+ * Note: current implementation is not optimized for speed; if performance + * ever becomes an issue, it is possible to construct much more efficient + * typed instances (one for Object[] and sub-types; one per primitive type). + * + * @since 2.2 Moved from earlier Comparators class + */ + public static Object getArrayComparator(final Object defaultValue) + { + final int length = Array.getLength(defaultValue); + final Class defaultValueType = defaultValue.getClass(); + return new Object() { + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (other == null || other.getClass() != defaultValueType) { + return false; + } + if (Array.getLength(other) != length) return false; + // so far so good: compare actual equality; but only shallow one + for (int i = 0; i < length; ++i) { + Object value1 = Array.get(defaultValue, i); + Object value2 = Array.get(other, i); + if (value1 == value2) continue; + if (value1 != null) { + if (!value1.equals(value2)) { + return false; + } + } + } + return true; + } + }; + } + + public static HashSet arrayToSet(T[] elements) + { + HashSet result = new HashSet(); + if (elements != null) { + for (T elem : elements) { + result.add(elem); + } + } + return result; + } + + public static ArrayList arrayToList(T[] elements) + { + ArrayList result = new ArrayList(); + if (elements != null) { + for (T elem : elements) { + result.add(elem); + } + } + return result; + } + + public static HashSet setAndArray(Set set, T[] elements) + { + HashSet result = new HashSet(); + if (set != null) { + result.addAll(set); + } + if (elements != null) { + for (T value : elements) { + result.add(value); + } + } + return result; + } + + /** + * Helper method for adding specified element to a List, but also + * considering case where the List may not have been yet constructed + * (that is, null is passed instead). + * + * @param list List to add to; may be null to indicate that a new + * List is to be constructed + * @param element Element to add to list + * + * @return List in which element was added; either list + * (if it was not null), or a newly constructed List. + */ + public static List addToList(List list, T element) + { + if (list == null) { + list = new ArrayList(); + } + list.add(element); + return list; + } + + /** + * Helper method for constructing a new array that contains specified + * element followed by contents of the given array. No checking is done + * to see if element being inserted is duplicate. + */ + public static T[] insertInList(T[] array, T element) + { + int len = array.length; + @SuppressWarnings("unchecked") + T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), len+1); + if (len > 0) { + System.arraycopy(array, 0, result, 1, len); + } + result[0] = element; + return result; + } + + /** + * Helper method for constructing a new array that contains specified + * element followed by contents of the given array but never contains + * duplicates. + * If element already existed, one of two things happens: if the element + * was already the first one in array, array is returned as is; but + * if not, a new copy is created in which element has moved as the head. + */ + @SuppressWarnings("unchecked") + public static T[] insertInListNoDup(T[] array, T element) + { + final int len = array.length; + + // First: see if the element already exists + for (int ix = 0; ix < len; ++ix) { + if (array[ix] == element) { + // if at head already, return as is + if (ix == 0) { + return array; + } + // otherwise move things around + T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), len); + System.arraycopy(array, 0, result, 1, ix); + result[0] = element; + ++ix; + int left = len - ix; + if (left > 0) { + System.arraycopy(array, ix, result, ix, left); + } + return result; + } + } + + // but if not, allocate new array, move + T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), len+1); + if (len > 0) { + System.arraycopy(array, 0, result, 1, len); + } + result[0] = element; + return result; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ArrayIterator.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ArrayIterator.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ArrayIterator.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,34 @@ +package com.fasterxml.jackson.databind.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Iterator implementation used to efficiently expose contents of an + * Array as read-only iterator. + */ +public class ArrayIterator implements Iterator, Iterable +{ + private final T[] _a; + + private int _index; + + public ArrayIterator(T[] a) { + _a = a; + _index = 0; + } + + @Override + public boolean hasNext() { return _index < _a.length; } + + @Override + public T next() { + if (_index >= _a.length) { + throw new NoSuchElementException(); + } + return _a[_index++]; + } + + @Override public void remove() { throw new UnsupportedOperationException(); } + @Override public Iterator iterator() { return this; } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/BeanUtil.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/BeanUtil.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/BeanUtil.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,278 @@ +package com.fasterxml.jackson.databind.util; + +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; + +/** + * Helper class that contains functionality needed by both serialization + * and deserialization side. + */ +public class BeanUtil +{ + /* + /********************************************************** + /* Handling property names + /********************************************************** + */ + + /** + * @since 2.5 + */ + public static String okNameForGetter(AnnotatedMethod am, boolean stdNaming) { + String name = am.getName(); + String str = okNameForIsGetter(am, name, stdNaming); + if (str == null) { + str = okNameForRegularGetter(am, name, stdNaming); + } + return str; + } + + /** + * @since 2.5 + */ + public static String okNameForRegularGetter(AnnotatedMethod am, String name, + boolean stdNaming) + { + if (name.startsWith("get")) { + /* 16-Feb-2009, tatu: To handle [JACKSON-53], need to block + * CGLib-provided method "getCallbacks". Not sure of exact + * safe criteria to get decent coverage without false matches; + * but for now let's assume there's no reason to use any + * such getter from CGLib. + * But let's try this approach... + */ + if ("getCallbacks".equals(name)) { + if (isCglibGetCallbacks(am)) { + return null; + } + } else if ("getMetaClass".equals(name)) { + // 30-Apr-2009, tatu: Need to suppress serialization of a cyclic reference + if (isGroovyMetaClassGetter(am)) { + return null; + } + } + return stdNaming + ? stdManglePropertyName(name, 3) + : legacyManglePropertyName(name, 3); + } + return null; + } + + /** + * @since 2.5 + */ + public static String okNameForIsGetter(AnnotatedMethod am, String name, + boolean stdNaming) + { + if (name.startsWith("is")) { // plus, must return a boolean + Class rt = am.getRawType(); + if (rt == Boolean.class || rt == Boolean.TYPE) { + return stdNaming + ? stdManglePropertyName(name, 2) + : legacyManglePropertyName(name, 2); + } + } + return null; + } + + /** + * @since 2.5 + */ + public static String okNameForSetter(AnnotatedMethod am, boolean stdNaming) { + String name = okNameForMutator(am, "set", stdNaming); + if ((name != null) + // 26-Nov-2009, tatu: need to suppress this internal groovy method + && (!"metaClass".equals(name) || !isGroovyMetaClassSetter(am))) { + return name; + } + return null; + } + + /** + * @since 2.5 + */ + public static String okNameForMutator(AnnotatedMethod am, String prefix, + boolean stdNaming) { + String name = am.getName(); + if (name.startsWith(prefix)) { + return stdNaming + ? stdManglePropertyName(name, prefix.length()) + : legacyManglePropertyName(name, prefix.length()); + } + return null; + } + + /* + /********************************************************** + /* Handling property names, deprecated methods + /********************************************************** + */ + + @Deprecated // since 2.5 + public static String okNameForGetter(AnnotatedMethod am) { + return okNameForGetter(am, false); + } + + @Deprecated // since 2.5 + public static String okNameForRegularGetter(AnnotatedMethod am, String name) { + return okNameForRegularGetter(am, name, false); + } + + @Deprecated // since 2.5 + public static String okNameForIsGetter(AnnotatedMethod am, String name) { + return okNameForIsGetter(am, name, false); + } + + @Deprecated // since 2.5 + public static String okNameForSetter(AnnotatedMethod am) { + return okNameForSetter(am, false); + } + + @Deprecated // since 2.5 + public static String okNameForMutator(AnnotatedMethod am, String prefix) { + return okNameForMutator(am, prefix, false); + } + + /* + /********************************************************** + /* Special case handling + /********************************************************** + */ + + /** + * This method was added to address [JACKSON-53]: need to weed out + * CGLib-injected "getCallbacks". + * At this point caller has detected a potential getter method + * with name "getCallbacks" and we need to determine if it is + * indeed injectect by Cglib. We do this by verifying that the + * result type is "net.sf.cglib.proxy.Callback[]" + */ + protected static boolean isCglibGetCallbacks(AnnotatedMethod am) + { + Class rt = am.getRawType(); + // Ok, first: must return an array type + if (rt == null || !rt.isArray()) { + return false; + } + /* And that type needs to be "net.sf.cglib.proxy.Callback". + * Theoretically could just be a type that implements it, but + * for now let's keep things simple, fix if need be. + */ + Class compType = rt.getComponentType(); + // Actually, let's just verify it's a "net.sf.cglib.*" class/interface + String pkgName = ClassUtil.getPackageName(compType); + if (pkgName != null) { + if (pkgName.contains(".cglib")) { + if (pkgName.startsWith("net.sf.cglib") + // also, as per [JACKSON-177] + || pkgName.startsWith("org.hibernate.repackage.cglib") + // and [core#674] + || pkgName.startsWith("org.springframework.cglib") + ) { + return true; + } + } + } + return false; + } + + /** + * Similar to {@link #isCglibGetCallbacks}, need to suppress + * a cyclic reference to resolve [JACKSON-103] + */ + protected static boolean isGroovyMetaClassSetter(AnnotatedMethod am) + { + Class argType = am.getRawParameterType(0); + String pkgName = ClassUtil.getPackageName(argType); + if (pkgName != null && pkgName.startsWith("groovy.lang")) { + return true; + } + return false; + } + + /** + * Another helper method to deal with rest of [JACKSON-103] + */ + protected static boolean isGroovyMetaClassGetter(AnnotatedMethod am) + { + Class rt = am.getRawType(); + if (rt == null || rt.isArray()) { + return false; + } + String pkgName = ClassUtil.getPackageName(rt); + if (pkgName != null && pkgName.startsWith("groovy.lang")) { + return true; + } + return false; + } + + /* + /********************************************************** + /* Actual name mangling methods + /********************************************************** + */ + + /** + * Method called to figure out name of the property, given + * corresponding suggested name based on a method or field name. + * + * @param basename Name of accessor/mutator method, not including prefix + * ("get"/"is"/"set") + */ + protected static String legacyManglePropertyName(final String basename, final int offset) + { + final int end = basename.length(); + if (end == offset) { // empty name, nope + return null; + } + // next check: is the first character upper case? If not, return as is + char c = basename.charAt(offset); + char d = Character.toLowerCase(c); + + if (c == d) { + return basename.substring(offset); + } + // otherwise, lower case initial chars. Common case first, just one char + StringBuilder sb = new StringBuilder(end - offset); + sb.append(d); + int i = offset+1; + for (; i < end; ++i) { + c = basename.charAt(i); + d = Character.toLowerCase(c); + if (c == d) { + sb.append(basename, i, end); + break; + } + sb.append(d); + } + return sb.toString(); + } + + /** + * @since 2.5 + */ + protected static String stdManglePropertyName(final String basename, final int offset) + { + final int end = basename.length(); + if (end == offset) { // empty name, nope + return null; + } + // first: if it doesn't start with capital, return as-is + char c0 = basename.charAt(offset); + char c1 = Character.toLowerCase(c0); + if (c0 == c1) { + return basename.substring(offset); + } + // 17-Dec-2014, tatu: As per [databind#653], need to follow more + // closely Java Beans spec; specifically, if two first are upper-case, + // then no lower-casing should be done. + if ((offset + 1) < end) { + if (Character.isUpperCase(basename.charAt(offset+1))) { + return basename.substring(offset); + } + } + StringBuilder sb = new StringBuilder(end - offset); + sb.append(c1); + sb.append(basename, offset+1, end); + return sb.toString(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ByteBufferBackedInputStream.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ByteBufferBackedInputStream.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ByteBufferBackedInputStream.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * Simple {@link InputStream} implementation that exposes currently + * available content of a {@link ByteBuffer}. + */ +public class ByteBufferBackedInputStream extends InputStream { + protected final ByteBuffer _b; + + public ByteBufferBackedInputStream(ByteBuffer buf) { _b = buf; } + + @Override public int available() { return _b.remaining(); } + + @Override + public int read() throws IOException { return _b.hasRemaining() ? (_b.get() & 0xFF) : -1; } + + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + if (!_b.hasRemaining()) return -1; + len = Math.min(len, _b.remaining()); + _b.get(bytes, off, len); + return len; + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ByteBufferBackedOutputStream.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ByteBufferBackedOutputStream.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ByteBufferBackedOutputStream.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,17 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.*; +import java.nio.ByteBuffer; + +/** + * Simple {@link OutputStream} implementation that appends content + * written in given {@link ByteBuffer} instance. + */ +public class ByteBufferBackedOutputStream extends OutputStream { + protected final ByteBuffer _b; + + public ByteBufferBackedOutputStream(ByteBuffer buf) { _b = buf; } + + @Override public void write(int b) throws IOException { _b.put((byte) b); } + @Override public void write(byte[] bytes, int off, int len) throws IOException { _b.put(bytes, off, len); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ClassUtil.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ClassUtil.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ClassUtil.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1186 @@ +package com.fasterxml.jackson.databind.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.*; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; + +public final class ClassUtil +{ + private final static Class CLS_OBJECT = Object.class; + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /* 21-Feb-2016, tatu: Unfortunately `Collections.emptyIterator()` only + * comes with JDK7, so we'll still have to include our bogus implementation + * for as long as we want JDK6 runtime compatibility + */ + private final static class EmptyIterator implements Iterator { + @Override public boolean hasNext() { return false; } + @Override public T next() { throw new NoSuchElementException(); } + @Override public void remove() { throw new UnsupportedOperationException(); } + } + + private final static EmptyIterator EMPTY_ITERATOR = new EmptyIterator(); + + /* + /********************************************************** + /* Simple factory methods + /********************************************************** + */ + + /** + * @since 2.7 + */ + @SuppressWarnings("unchecked") + public static Iterator emptyIterator() { +// 21-Feb-2016, tatu: As per above, use a locally defined empty iterator +// return Collections.emptyIterator(); + return (Iterator) EMPTY_ITERATOR; + } + + /* + /********************************************************** + /* Methods that deal with inheritance + /********************************************************** + */ + + /** + * Method that will find all sub-classes and implemented interfaces + * of a given class or interface. Classes are listed in order of + * precedence, starting with the immediate super-class, followed by + * interfaces class directly declares to implemented, and then recursively + * followed by parent of super-class and so forth. + * Note that Object.class is not included in the list + * regardless of whether endBefore argument is defined or not. + * + * @param endBefore Super-type to NOT include in results, if any; when + * encountered, will be ignored (and no super types are checked). + * + * @since 2.7 + */ + public static List findSuperTypes(JavaType type, Class endBefore, + boolean addClassItself) { + if ((type == null) || type.hasRawClass(endBefore) || type.hasRawClass(Object.class)) { + return Collections.emptyList(); + } + List result = new ArrayList(8); + _addSuperTypes(type, endBefore, result, addClassItself); + return result; + } + + /** + * @since 2.7 + */ + public static List> findRawSuperTypes(Class cls, Class endBefore, boolean addClassItself) { + if ((cls == null) || (cls == endBefore) || (cls == Object.class)) { + return Collections.emptyList(); + } + List> result = new ArrayList>(8); + _addRawSuperTypes(cls, endBefore, result, addClassItself); + return result; + } + + /** + * Method for finding all super classes (but not super interfaces) of given class, + * starting with the immediate super class and ending in the most distant one. + * Class itself is included if addClassItself is true. + * + * @since 2.7 + */ + public static List> findSuperClasses(Class cls, Class endBefore, + boolean addClassItself) { + List> result = new LinkedList>(); + if ((cls != null) && (cls != endBefore)) { + if (addClassItself) { + result.add(cls); + } + while ((cls = cls.getSuperclass()) != null) { + if (cls == endBefore) { + break; + } + result.add(cls); + } + } + return result; + } + + @Deprecated // since 2.7 + public static List> findSuperTypes(Class cls, Class endBefore) { + return findSuperTypes(cls, endBefore, new ArrayList>(8)); + } + + @Deprecated // since 2.7 + public static List> findSuperTypes(Class cls, Class endBefore, List> result) { + _addRawSuperTypes(cls, endBefore, result, false); + return result; + } + + private static void _addSuperTypes(JavaType type, Class endBefore, Collection result, + boolean addClassItself) + { + if (type == null) { + return; + } + final Class cls = type.getRawClass(); + if (cls == endBefore || cls == Object.class) { return; } + if (addClassItself) { + if (result.contains(type)) { // already added, no need to check supers + return; + } + result.add(type); + } + for (JavaType intCls : type.getInterfaces()) { + _addSuperTypes(intCls, endBefore, result, true); + } + _addSuperTypes(type.getSuperClass(), endBefore, result, true); + } + + private static void _addRawSuperTypes(Class cls, Class endBefore, Collection> result, boolean addClassItself) { + if (cls == endBefore || cls == null || cls == Object.class) { return; } + if (addClassItself) { + if (result.contains(cls)) { // already added, no need to check supers + return; + } + result.add(cls); + } + for (Class intCls : _interfaces(cls)) { + _addRawSuperTypes(intCls, endBefore, result, true); + } + _addRawSuperTypes(cls.getSuperclass(), endBefore, result, true); + } + + /* + /********************************************************** + /* Class type detection methods + /********************************************************** + */ + + /** + * @return Null if class might be a bean; type String (that identifies + * why it's not a bean) if not + */ + public static String canBeABeanType(Class type) + { + // First: language constructs that ain't beans: + if (type.isAnnotation()) { + return "annotation"; + } + if (type.isArray()) { + return "array"; + } + if (type.isEnum()) { + return "enum"; + } + if (type.isPrimitive()) { + return "primitive"; + } + + // Anything else? Seems valid, then + return null; + } + + public static String isLocalType(Class type, boolean allowNonStatic) + { + /* As per [JACKSON-187], GAE seems to throw SecurityExceptions + * here and there... and GAE itself has a bug, too + * (see []). Bah. So we need to catch some wayward exceptions on GAE + */ + try { + // one more: method locals, anonymous, are not good: + if (hasEnclosingMethod(type)) { + return "local/anonymous"; + } + + /* But how about non-static inner classes? Can't construct + * easily (theoretically, we could try to check if parent + * happens to be enclosing... but that gets convoluted) + */ + if (!allowNonStatic) { + if (!Modifier.isStatic(type.getModifiers())) { + if (getEnclosingClass(type) != null) { + return "non-static member class"; + } + } + } + } + catch (SecurityException e) { } + catch (NullPointerException e) { } + return null; + } + + /** + * Method for finding enclosing class for non-static inner classes + */ + public static Class getOuterClass(Class type) + { + // as above, GAE has some issues... + try { + // one more: method locals, anonymous, are not good: + if (hasEnclosingMethod(type)) { + return null; + } + if (!Modifier.isStatic(type.getModifiers())) { + return getEnclosingClass(type); + } + } catch (SecurityException e) { } + return null; + } + + + /** + * Helper method used to weed out dynamic Proxy types; types that do + * not expose concrete method API that we could use to figure out + * automatic Bean (property) based serialization. + */ + public static boolean isProxyType(Class type) + { + // As per [databind#57], should NOT disqualify JDK proxy: + /* + // Then: well-known proxy (etc) classes + if (Proxy.isProxyClass(type)) { + return true; + } + */ + String name = type.getName(); + // Hibernate uses proxies heavily as well: + if (name.startsWith("net.sf.cglib.proxy.") + || name.startsWith("org.hibernate.proxy.")) { + return true; + } + // Not one of known proxies, nope: + return false; + } + + /** + * Helper method that checks if given class is a concrete one; + * that is, not an interface or abstract class. + */ + public static boolean isConcrete(Class type) + { + int mod = type.getModifiers(); + return (mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0; + } + + public static boolean isConcrete(Member member) + { + int mod = member.getModifiers(); + return (mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0; + } + + public static boolean isCollectionMapOrArray(Class type) + { + if (type.isArray()) return true; + if (Collection.class.isAssignableFrom(type)) return true; + if (Map.class.isAssignableFrom(type)) return true; + return false; + } + + /* + /********************************************************** + /* Type name handling methods + /********************************************************** + */ + + /** + * Helper method used to construct appropriate description + * when passed either type (Class) or an instance; in latter + * case, class of instance is to be used. + */ + public static String getClassDescription(Object classOrInstance) + { + if (classOrInstance == null) { + return "unknown"; + } + Class cls = (classOrInstance instanceof Class) ? + (Class) classOrInstance : classOrInstance.getClass(); + return cls.getName(); + } + + /* + /********************************************************** + /* Class loading + /********************************************************** + */ + + /** + * @deprecated Since 2.6, use method in {@link com.fasterxml.jackson.databind.type.TypeFactory}. + */ + @Deprecated + public static Class findClass(String className) throws ClassNotFoundException + { + // [JACKSON-597]: support primitive types (and void) + if (className.indexOf('.') < 0) { + if ("int".equals(className)) return Integer.TYPE; + if ("long".equals(className)) return Long.TYPE; + if ("float".equals(className)) return Float.TYPE; + if ("double".equals(className)) return Double.TYPE; + if ("boolean".equals(className)) return Boolean.TYPE; + if ("byte".equals(className)) return Byte.TYPE; + if ("char".equals(className)) return Character.TYPE; + if ("short".equals(className)) return Short.TYPE; + if ("void".equals(className)) return Void.TYPE; + } + // Two-phase lookup: first using context ClassLoader; then default + Throwable prob = null; + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + if (loader != null) { + try { + return Class.forName(className, true, loader); + } catch (Exception e) { + prob = getRootCause(e); + } + } + try { + return Class.forName(className); + } catch (Exception e) { + if (prob == null) { + prob = getRootCause(e); + } + } + if (prob instanceof RuntimeException) { + throw (RuntimeException) prob; + } + throw new ClassNotFoundException(prob.getMessage(), prob); + } + + /* + /********************************************************** + /* Caching access to class metadata, added in 2.7 + /********************************************************** + */ + + /* 17-Sep-2015, tatu: Although access methods should not be significant + * problems for most proper usage, they may become problematic if + * ObjectMapper has to be re-created; and especially so on Android. + * So let's do somewhat aggressive caching. + */ + + private final static LRUMap,ClassMetadata> sCached = new LRUMap,ClassMetadata>(48, 48); + + /** + * @since 2.7 + */ + public static String getPackageName(Class cls) { + return _getMetadata(cls).getPackageName(); + } + + /** + * @since 2.7 + */ + public static boolean hasEnclosingMethod(Class cls) { + return _getMetadata(cls).hasEnclosingMethod(); + } + + /** + * @since 2.7 + */ + public static Field[] getDeclaredFields(Class cls) { + return _getMetadata(cls).getDeclaredFields(); + } + + /** + * @since 2.7 + */ + public static Method[] getDeclaredMethods(Class cls) { + return _getMetadata(cls).getDeclaredMethods(); + } + + /** + * @since 2.7 + */ + public static Annotation[] findClassAnnotations(Class cls) { + return _getMetadata(cls).getDeclaredAnnotations(); + } + + /** + * @since 2.7 + */ + public static Ctor[] getConstructors(Class cls) { + return _getMetadata(cls).getConstructors(); + } + + // // // Then methods that do NOT cache access but were considered + // // // (and could be added to do caching if it was proven effective) + + /** + * @since 2.7 + */ + public static Class getDeclaringClass(Class cls) { + // Caching does not seem worthwhile, as per profiling + return isObjectOrPrimitive(cls) ? null : cls.getDeclaringClass(); + } + + /** + * @since 2.7 + */ + public static Type getGenericSuperclass(Class cls) { + return cls.getGenericSuperclass(); + } + + /** + * @since 2.7 + */ + public static Type[] getGenericInterfaces(Class cls) { + return _getMetadata(cls).getGenericInterfaces(); + } + + /** + * @since 2.7 + */ + public static Class getEnclosingClass(Class cls) { + // Caching does not seem worthwhile, as per profiling + return isObjectOrPrimitive(cls) ? null : cls.getEnclosingClass(); + } + + + private static Class[] _interfaces(Class cls) { + return _getMetadata(cls).getInterfaces(); + } + + private static ClassMetadata _getMetadata(Class cls) + { + ClassMetadata md = sCached.get(cls); + if (md == null) { + md = new ClassMetadata(cls); + // tiny optimization, but in case someone concurrently constructed it, + // let's use that instance, to reduce extra concurrent work. + ClassMetadata old = sCached.putIfAbsent(cls, md); + if (old != null) { + md = old; + } + } + return md; + } + + /* + /********************************************************** + /* Method type detection methods + /********************************************************** + */ + + /** + * @deprecated Since 2.6 not used; may be removed before 3.x + */ + @Deprecated // since 2.6 + public static boolean hasGetterSignature(Method m) + { + // First: static methods can't be getters + if (Modifier.isStatic(m.getModifiers())) { + return false; + } + // Must take no args + Class[] pts = m.getParameterTypes(); + if (pts != null && pts.length != 0) { + return false; + } + // Can't be a void method + if (Void.TYPE == m.getReturnType()) { + return false; + } + // Otherwise looks ok: + return true; + } + + /* + /********************************************************** + /* Exception handling + /********************************************************** + */ + + /** + * Method that can be used to find the "root cause", innermost + * of chained (wrapped) exceptions. + */ + public static Throwable getRootCause(Throwable t) + { + while (t.getCause() != null) { + t = t.getCause(); + } + return t; + } + + /** + * Method that will unwrap root causes of given Throwable, and throw + * the innermost {@link Exception} or {@link Error} as is. + * This is useful in cases where mandatory wrapping is added, which + * is often done by Reflection API. + */ + public static void throwRootCause(Throwable t) throws Exception + { + t = getRootCause(t); + if (t instanceof Exception) { + throw (Exception) t; + } + throw (Error) t; + } + + /** + * Method that will wrap 't' as an {@link IllegalArgumentException} if it + * is a checked exception; otherwise (runtime exception or error) throw as is + */ + public static void throwAsIAE(Throwable t) + { + throwAsIAE(t, t.getMessage()); + } + + /** + * Method that will wrap 't' as an {@link IllegalArgumentException} (and with + * specified message) if it + * is a checked exception; otherwise (runtime exception or error) throw as is + */ + public static void throwAsIAE(Throwable t, String msg) + { + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + if (t instanceof Error) { + throw (Error) t; + } + throw new IllegalArgumentException(msg, t); + } + + /** + * Method that will locate the innermost exception for given Throwable; + * and then wrap it as an {@link IllegalArgumentException} if it + * is a checked exception; otherwise (runtime exception or error) throw as is + */ + public static void unwrapAndThrowAsIAE(Throwable t) + { + throwAsIAE(getRootCause(t)); + } + + /** + * Method that will locate the innermost exception for given Throwable; + * and then wrap it as an {@link IllegalArgumentException} if it + * is a checked exception; otherwise (runtime exception or error) throw as is + */ + public static void unwrapAndThrowAsIAE(Throwable t, String msg) + { + throwAsIAE(getRootCause(t), msg); + } + + /* + /********************************************************** + /* Instantiation + /********************************************************** + */ + + /** + * Method that can be called to try to create an instantiate of + * specified type. Instantiation is done using default no-argument + * constructor. + * + * @param canFixAccess Whether it is possible to try to change access + * rights of the default constructor (in case it is not publicly + * accessible) or not. + * + * @throws IllegalArgumentException If instantiation fails for any reason; + * except for cases where constructor throws an unchecked exception + * (which will be passed as is) + */ + public static T createInstance(Class cls, boolean canFixAccess) + throws IllegalArgumentException + { + Constructor ctor = findConstructor(cls, canFixAccess); + if (ctor == null) { + throw new IllegalArgumentException("Class "+cls.getName()+" has no default (no arg) constructor"); + } + try { + return ctor.newInstance(); + } catch (Exception e) { + ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+cls.getName()+", problem: "+e.getMessage()); + return null; + } + } + + public static Constructor findConstructor(Class cls, boolean canFixAccess) + throws IllegalArgumentException + { + try { + Constructor ctor = cls.getDeclaredConstructor(); + if (canFixAccess) { + checkAndFixAccess(ctor); + } else { + // Has to be public... + if (!Modifier.isPublic(ctor.getModifiers())) { + throw new IllegalArgumentException("Default constructor for "+cls.getName()+" is not accessible (non-public?): not allowed to try modify access via Reflection: can not instantiate type"); + } + } + return ctor; + } catch (NoSuchMethodException e) { + ; + } catch (Exception e) { + ClassUtil.unwrapAndThrowAsIAE(e, "Failed to find default constructor of class "+cls.getName()+", problem: "+e.getMessage()); + } + return null; + } + + /* + /********************************************************** + /* Primitive type support + /********************************************************** + */ + + /** + * Helper method used to get default value for wrappers used for primitive types + * (0 for Integer etc) + */ + public static Object defaultValue(Class cls) + { + if (cls == Integer.TYPE) { + return Integer.valueOf(0); + } + if (cls == Long.TYPE) { + return Long.valueOf(0L); + } + if (cls == Boolean.TYPE) { + return Boolean.FALSE; + } + if (cls == Double.TYPE) { + return Double.valueOf(0.0); + } + if (cls == Float.TYPE) { + return Float.valueOf(0.0f); + } + if (cls == Byte.TYPE) { + return Byte.valueOf((byte) 0); + } + if (cls == Short.TYPE) { + return Short.valueOf((short) 0); + } + if (cls == Character.TYPE) { + return '\0'; + } + throw new IllegalArgumentException("Class "+cls.getName()+" is not a primitive type"); + } + + /** + * Helper method for finding wrapper type for given primitive type (why isn't + * there one in JDK?) + */ + public static Class wrapperType(Class primitiveType) + { + if (primitiveType == Integer.TYPE) { + return Integer.class; + } + if (primitiveType == Long.TYPE) { + return Long.class; + } + if (primitiveType == Boolean.TYPE) { + return Boolean.class; + } + if (primitiveType == Double.TYPE) { + return Double.class; + } + if (primitiveType == Float.TYPE) { + return Float.class; + } + if (primitiveType == Byte.TYPE) { + return Byte.class; + } + if (primitiveType == Short.TYPE) { + return Short.class; + } + if (primitiveType == Character.TYPE) { + return Character.class; + } + throw new IllegalArgumentException("Class "+primitiveType.getName()+" is not a primitive type"); + } + + /** + * Method that can be used to find primitive type for given class if (but only if) + * it is either wrapper type or primitive type; returns `null` if type is neither. + * + * @since 2.7 + */ + public static Class primitiveType(Class type) + { + if (type.isPrimitive()) { + return type; + } + + if (type == Integer.class) { + return Integer.TYPE; + } + if (type == Long.class) { + return Long.TYPE; + } + if (type == Boolean.class) { + return Boolean.TYPE; + } + if (type == Double.class) { + return Double.TYPE; + } + if (type == Float.class) { + return Float.TYPE; + } + if (type == Byte.class) { + return Byte.TYPE; + } + if (type == Short.class) { + return Short.TYPE; + } + if (type == Character.class) { + return Character.TYPE; + } + return null; + } + + /* + /********************************************************** + /* Access checking/handling methods + /********************************************************** + */ + + /** + * Equivalent to call: + *
+     *   checkAndFixAccess(member, false);
+     *
+ * + * @deprecated Since 2.7 call variant that takes boolean flag. + */ + @Deprecated + public static void checkAndFixAccess(Member member) { + checkAndFixAccess(member, false); + } + + /** + * Method that is called if a {@link Member} may need forced access, + * to force a field, method or constructor to be accessible: this + * is done by calling {@link AccessibleObject#setAccessible(boolean)}. + * + * @param member Accessor to call setAccessible() on. + * @param force Whether to always try to make accessor accessible (true), + * or only if needed as per access rights (false) + * + * @since 2.7 + */ + public static void checkAndFixAccess(Member member, boolean force) + { + // We know all members are also accessible objects... + AccessibleObject ao = (AccessibleObject) member; + + /* 14-Jan-2009, tatu: It seems safe and potentially beneficial to + * always to make it accessible (latter because it will force + * skipping checks we have no use for...), so let's always call it. + */ + //if (!ao.isAccessible()) { + try { + if (force || + (!Modifier.isPublic(member.getModifiers()) + || !Modifier.isPublic(member.getDeclaringClass().getModifiers()))) { + ao.setAccessible(true); + } + } catch (SecurityException se) { + /* 17-Apr-2009, tatu: Related to [JACKSON-101]: this can fail on + * platforms like EJB and Google App Engine); so let's + * only fail if we really needed it... + */ + if (!ao.isAccessible()) { + Class declClass = member.getDeclaringClass(); + throw new IllegalArgumentException("Can not access "+member+" (from class "+declClass.getName()+"; failed to set access: "+se.getMessage()); + } + } + //} + } + + /* + /********************************************************** + /* Enum type detection + /********************************************************** + */ + + /** + * Helper method that can be used to dynamically figure out + * enumeration type of given {@link EnumSet}, without having + * access to its declaration. + * Code is needed to work around design flaw in JDK. + */ + public static Class> findEnumType(EnumSet s) + { + // First things first: if not empty, easy to determine + if (!s.isEmpty()) { + return findEnumType(s.iterator().next()); + } + // Otherwise need to locate using an internal field + return EnumTypeLocator.instance.enumTypeFor(s); + } + + /** + * Helper method that can be used to dynamically figure out + * enumeration type of given {@link EnumSet}, without having + * access to its declaration. + * Code is needed to work around design flaw in JDK. + */ + public static Class> findEnumType(EnumMap m) + { + if (!m.isEmpty()) { + return findEnumType(m.keySet().iterator().next()); + } + // Otherwise need to locate using an internal field + return EnumTypeLocator.instance.enumTypeFor(m); + } + + /** + * Helper method that can be used to dynamically figure out formal + * enumeration type (class) for given enumeration. This is either + * class of enum instance (for "simple" enumerations), or its + * superclass (for enums with instance fields or methods) + */ + @SuppressWarnings("unchecked") + public static Class> findEnumType(Enum en) + { + // enums with "body" are sub-classes of the formal type + Class ec = en.getClass(); + if (ec.getSuperclass() != Enum.class) { + ec = ec.getSuperclass(); + } + return (Class>) ec; + } + + /** + * Helper method that can be used to dynamically figure out formal + * enumeration type (class) for given class of an enumeration value. + * This is either class of enum instance (for "simple" enumerations), + * or its superclass (for enums with instance fields or methods) + */ + @SuppressWarnings("unchecked") + public static Class> findEnumType(Class cls) + { + // enums with "body" are sub-classes of the formal type + if (cls.getSuperclass() != Enum.class) { + cls = cls.getSuperclass(); + } + return (Class>) cls; + } + + /* + /********************************************************** + /* Jackson-specific stuff + /********************************************************** + */ + + /** + * Method that can be called to determine if given Object is the default + * implementation Jackson uses; as opposed to a custom serializer installed by + * a module or calling application. Determination is done using + * {@link JacksonStdImpl} annotation on handler (serializer, deserializer etc) + * class. + */ + public static boolean isJacksonStdImpl(Object impl) { + return (impl != null) && isJacksonStdImpl(impl.getClass()); + } + + public static boolean isJacksonStdImpl(Class implClass) { + return (implClass.getAnnotation(JacksonStdImpl.class) != null); + } + + public static boolean isBogusClass(Class cls) { + return (cls == Void.class || cls == Void.TYPE + || cls == com.fasterxml.jackson.databind.annotation.NoClass.class); + } + + public static boolean isNonStaticInnerClass(Class cls) { + return !Modifier.isStatic(cls.getModifiers()) + && (getEnclosingClass(cls) != null); + } + + /** + * @since 2.7 + */ + public static boolean isObjectOrPrimitive(Class cls) { + return (cls == CLS_OBJECT) || cls.isPrimitive(); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Inner class used to contain gory details of how we can determine + * details of instances of common JDK types like {@link EnumMap}s. + */ + private static class EnumTypeLocator + { + final static EnumTypeLocator instance = new EnumTypeLocator(); + + private final Field enumSetTypeField; + private final Field enumMapTypeField; + + private EnumTypeLocator() { + //JDK uses following fields to store information about actual Enumeration + // type for EnumSets, EnumMaps... + enumSetTypeField = locateField(EnumSet.class, "elementType", Class.class); + enumMapTypeField = locateField(EnumMap.class, "elementType", Class.class); + } + + @SuppressWarnings("unchecked") + public Class> enumTypeFor(EnumSet set) + { + if (enumSetTypeField != null) { + return (Class>) get(set, enumSetTypeField); + } + throw new IllegalStateException("Can not figure out type for EnumSet (odd JDK platform?)"); + } + + @SuppressWarnings("unchecked") + public Class> enumTypeFor(EnumMap set) + { + if (enumMapTypeField != null) { + return (Class>) get(set, enumMapTypeField); + } + throw new IllegalStateException("Can not figure out type for EnumMap (odd JDK platform?)"); + } + + private Object get(Object bean, Field field) + { + try { + return field.get(bean); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + private static Field locateField(Class fromClass, String expectedName, Class type) + { + Field found = null; + // First: let's see if we can find exact match: + Field[] fields = getDeclaredFields(fromClass); + for (Field f : fields) { + if (expectedName.equals(f.getName()) && f.getType() == type) { + found = f; + break; + } + } + // And if not, if there is just one field with the type, that field + if (found == null) { + for (Field f : fields) { + if (f.getType() == type) { + // If more than one, can't choose + if (found != null) return null; + found = f; + } + } + } + if (found != null) { // it's non-public, need to force accessible + try { + found.setAccessible(true); + } catch (Throwable t) { } + } + return found; + } + } + + /* + /********************************************************** + /* Helper classed used for caching + /********************************************************** + */ + + /** + * @since 2.7 + */ + private final static class ClassMetadata + { + private final static Annotation[] NO_ANNOTATIONS = new Annotation[0]; + private final static Ctor[] NO_CTORS = new Ctor[0]; + + private final Class _forClass; + + private String _packageName; + private Boolean _hasEnclosingMethod; + + private Class[] _interfaces; + private Type[] _genericInterfaces; + + private Annotation[] _annotations; + private Ctor[] _constructors; + private Field[] _fields; + private Method[] _methods; + + public ClassMetadata(Class forClass) { + _forClass = forClass; + } + + public String getPackageName() { + String name = _packageName; + if (name == null) { + Package pkg = _forClass.getPackage(); + name = (pkg == null) ? null : pkg.getName(); + if (name == null) { + name = ""; + } + _packageName = name; + } + return (name == "") ? null : name; + } + + // 19-Sep-2015, tatu: Bit of performance improvement, after finding this + // in profile; maybe 5% in "wasteful" deserialization case + public Class[] getInterfaces() { + Class[] result = _interfaces; + if (result == null) { + result = _forClass.getInterfaces(); + _interfaces = result; + } + return result; + } + + // 30-Oct-2015, tatu: Minor performance boost too (5% or less) + public Type[] getGenericInterfaces() { + Type[] result = _genericInterfaces; + if (result == null) { + result = _forClass.getGenericInterfaces(); + _genericInterfaces = result; + } + return result; + } + + // 19-Sep-2015, tatu: Modest performance improvement, after finding this + // in profile; maybe 2-3% in "wasteful" deserialization case + public Annotation[] getDeclaredAnnotations() { + Annotation[] result = _annotations; + if (result == null) { + result = isObjectOrPrimitive() ? NO_ANNOTATIONS : _forClass.getDeclaredAnnotations(); + _annotations = result; + } + return result; + } + + // 19-Sep-2015, tatu: Some performance improvement, after finding this + // in profile; maybe 8-10% in "wasteful" deserialization case + public Ctor[] getConstructors() { + Ctor[] result = _constructors; + if (result == null) { + // Note: can NOT skip abstract classes as they may be used with mix-ins + // and for regular use shouldn't really matter. + if (_forClass.isInterface() || isObjectOrPrimitive()) { + result = NO_CTORS; + } else { + Constructor[] rawCtors = _forClass.getDeclaredConstructors(); + final int len = rawCtors.length; + result = new Ctor[len]; + for (int i = 0; i < len; ++i) { + result[i] = new Ctor(rawCtors[i]); + } + } + _constructors = result; + } + return result; + } + + // 21-Spe-2015, tatu: Surprisingly significant improvement (+10%)... + public Field[] getDeclaredFields() { + Field[] fields = _fields; + if (fields == null) { + fields = _forClass.getDeclaredFields(); + _fields = fields; + } + return fields; + } + + // 21-Spe-2015, tatu: Surprisingly significant improvement (+30%)... + public Method[] getDeclaredMethods() { + Method[] methods = _methods; + if (methods == null) { + methods = _forClass.getDeclaredMethods(); + _methods = methods; + } + return methods; + } + + // Prominently listed on profiling when not cached, improvement + // modest, 1-2% range; but at least is measurable so keep it + public boolean hasEnclosingMethod() { + Boolean b = _hasEnclosingMethod; + if (b == null) { + b = isObjectOrPrimitive() ? Boolean.FALSE : Boolean.valueOf(_forClass.getEnclosingMethod() != null); + _hasEnclosingMethod = b; + } + return b.booleanValue(); + } + + private boolean isObjectOrPrimitive() { + return (_forClass == CLS_OBJECT) || _forClass.isPrimitive(); + } + + /* And then we have a bunch of accessors that did show up in profiling + * of "wasteful" cases, but for which caching did not yield non-trivial + * improvements (for tests less than 1% improvement) + */ + + // Caching does not seem worthwhile, as per profiling +// public Type getGenericSuperclass(); +// public Class getDeclaringClass(); +// public Class getEnclosingClass(); + } + + /** + * Value class used for caching Constructor declarations; used because + * caching done by JDK appears to be somewhat inefficient for some use cases. + * + * @since 2.7 + */ + public final static class Ctor + { + public final Constructor _ctor; + + private Annotation[] _annotations; + + private Annotation[][] _paramAnnotations; + + private int _paramCount = -1; + + public Ctor(Constructor ctor) { + _ctor = ctor; + } + + public Constructor getConstructor() { + return _ctor; + } + + public int getParamCount() { + int c = _paramCount; + if (c < 0) { + c = _ctor.getParameterTypes().length; + _paramCount = c; + } + return c; + } + + public Class getDeclaringClass() { + return _ctor.getDeclaringClass(); + } + + // Modest boost: maybe 1%? + public Annotation[] getDeclaredAnnotations() { + Annotation[] result = _annotations; + if (result == null) { + result = _ctor.getDeclaredAnnotations(); + _annotations = result; + } + return result; + } + + // Modest boost: maybe 1%? + public Annotation[][] getParameterAnnotations() { + Annotation[][] result = _paramAnnotations; + if (result == null) { + result = _ctor.getParameterAnnotations(); + _paramAnnotations = result; + } + return result; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/CompactStringObjectMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,138 @@ +package com.fasterxml.jackson.databind.util; + +import java.util.*; + +/** + * Specialized lookup class that implements functionality similar to + * {@link java.util.Map}, but for special case of key always being + * {@link java.lang.String} and using more compact (and memory-access + * friendly) hashing scheme. Assumption is also that keys are typically + * intern()ed. + *

+ * Generics are not used to avoid bridge methods and since these maps + * are not exposed as part of external API. + * + * @since 2.6 + */ +public final class CompactStringObjectMap + implements java.io.Serializable // since 2.6.2 +{ + private static final long serialVersionUID = 1L; + + /** + * Shared instance that can be used when there are no contents to Map. + */ + private final static CompactStringObjectMap EMPTY = new CompactStringObjectMap(1, 0, + new Object[4]); + + private final int _hashMask, _spillCount; + + private final Object[] _hashArea; + + private CompactStringObjectMap(int hashMask, int spillCount, Object[] hashArea) + { + _hashMask = hashMask; + _spillCount = spillCount; + _hashArea = hashArea; + } + + public static CompactStringObjectMap construct(Map all) + { + if (all.isEmpty()) { // can this happen? + return EMPTY; + } + + // First: calculate size of primary hash area + final int size = findSize(all.size()); + final int mask = size-1; + // and allocate enough to contain primary/secondary, expand for spillovers as need be + int alloc = (size + (size>>1)) * 2; + Object[] hashArea = new Object[alloc]; + int spillCount = 0; + + for (Map.Entry entry : all.entrySet()) { + String key = entry.getKey(); + + int slot = key.hashCode() & mask; + int ix = slot+slot; + + // primary slot not free? + if (hashArea[ix] != null) { + // secondary? + ix = (size + (slot >> 1)) << 1; + if (hashArea[ix] != null) { + // ok, spill over. + ix = ((size + (size >> 1) ) << 1) + spillCount; + spillCount += 2; + if (ix >= hashArea.length) { + hashArea = Arrays.copyOf(hashArea, hashArea.length + 4); + } + } + } + hashArea[ix] = key; + hashArea[ix+1] = entry.getValue(); + } + return new CompactStringObjectMap(mask, spillCount, hashArea); + } + + private final static int findSize(int size) + { + if (size <= 5) { + return 8; + } + if (size <= 12) { + return 16; + } + int needed = size + (size >> 2); // at most 80% full + int result = 32; + while (result < needed) { + result += result; + } + return result; + } + + public Object find(String key) { + int slot = key.hashCode() & _hashMask; + int ix = (slot<<1); + Object match = _hashArea[ix]; + if ((match == key) || key.equals(match)) { + return _hashArea[ix+1]; + } + return _find2(key, slot, match); + } + + private final Object _find2(String key, int slot, Object match) + { + if (match == null) { + return null; + } + int hashSize = _hashMask+1; + int ix = hashSize + (slot>>1) << 1; + match = _hashArea[ix]; + if (key.equals(match)) { + return _hashArea[ix+1]; + } + if (match != null) { // _findFromSpill(...) + int i = (hashSize + (hashSize>>1)) << 1; + for (int end = i + _spillCount; i < end; i += 2) { + match = _hashArea[i]; + if ((match == key) || key.equals(match)) { + return _hashArea[i+1]; + } + } + } + return null; + } + + public List keys() { + final int end = _hashArea.length; + List keys = new ArrayList(end >> 2); + for (int i = 0; i < end; i += 2) { + Object key = _hashArea[i]; + if (key != null) { + keys.add((String) key); + } + } + return keys; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Converter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Converter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Converter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,67 @@ +package com.fasterxml.jackson.databind.util; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Helper interface for things that convert Objects of + * one type to another. + *

+ * NOTE: implementors are strongly encouraged to extend {@link StdConverter} + * instead of directly implementing {@link Converter}, since that can + * help with default implementation of typically boiler-plate code. + * + * @param Type of values converter takes + * @param Result type from conversion + * + * @see com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer + * @see com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer + * + * @since 2.1 + */ +public interface Converter +{ + /** + * Main conversion method. + */ + public OUT convert(IN value); + + /** + * Method that can be used to find out actual input (source) type; this + * usually can be determined from type parameters, but may need + * to be implemented differently from programmatically defined + * converters (which can not change static type parameter bindings). + * + * @since 2.2 + */ + public JavaType getInputType(TypeFactory typeFactory); + + /** + * Method that can be used to find out actual output (target) type; this + * usually can be determined from type parameters, but may need + * to be implemented differently from programmatically defined + * converters (which can not change static type parameter bindings). + * + * @since 2.2 + */ + public JavaType getOutputType(TypeFactory typeFactory); + + /* + /********************************************************** + /* Helper class(es) + /********************************************************** + */ + + /** + * This marker class is only to be used with annotations, to + * indicate that no converter is to be used. + *

+ * Specifically, this class is to be used as the marker for + * annotation {@link com.fasterxml.jackson.databind.annotation.JsonSerialize}, + * property converter (and related) + * + * @since 2.2 + */ + public abstract static class None + implements Converter { } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/EnumResolver.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/EnumResolver.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/EnumResolver.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,161 @@ +package com.fasterxml.jackson.databind.util; + +import java.lang.reflect.Method; +import java.util.*; + +import com.fasterxml.jackson.databind.AnnotationIntrospector; + + /** + * Helper class used to resolve String values (either JSON Object field + * names or regular String values) into Java Enum instances. + */ +public class EnumResolver implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final Class> _enumClass; + + protected final Enum[] _enums; + + protected final HashMap> _enumsById; + + protected EnumResolver(Class> enumClass, Enum[] enums, HashMap> map) + { + _enumClass = enumClass; + _enums = enums; + _enumsById = map; + } + + /** + * Factory method for constructing resolver that maps from Enum.name() into + * Enum value + */ + public static EnumResolver constructFor(Class> enumCls, AnnotationIntrospector ai) + { + Enum[] enumValues = enumCls.getEnumConstants(); + if (enumValues == null) { + throw new IllegalArgumentException("No enum constants for class "+enumCls.getName()); + } + String[] names = ai.findEnumValues(enumCls, enumValues, new String[enumValues.length]); + HashMap> map = new HashMap>(); + for (int i = 0, len = enumValues.length; i < len; ++i) { + String name = names[i]; + if (name == null) { + name = enumValues[i].name(); + } + map.put(name, enumValues[i]); + } + return new EnumResolver(enumCls, enumValues, map); + } + + /** + * Factory method for constructing resolver that maps from Enum.toString() into + * Enum value + */ + public static EnumResolver constructUsingToString(Class> enumCls) + { + Enum[] enumValues = enumCls.getEnumConstants(); + HashMap> map = new HashMap>(); + // from last to first, so that in case of duplicate values, first wins + for (int i = enumValues.length; --i >= 0; ) { + Enum e = enumValues[i]; + map.put(e.toString(), e); + } + return new EnumResolver(enumCls, enumValues, map); + } + + public static EnumResolver constructUsingMethod(Class> enumCls, + Method accessor) + { + Enum[] enumValues = enumCls.getEnumConstants(); + HashMap> map = new HashMap>(); + // from last to first, so that in case of duplicate values, first wins + for (int i = enumValues.length; --i >= 0; ) { + Enum en = enumValues[i]; + try { + Object o = accessor.invoke(en); + if (o != null) { + map.put(o.toString(), en); + } + } catch (Exception e) { + throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage()); + } + } + return new EnumResolver(enumCls, enumValues, map); + } + + /** + * This method is needed because of the dynamic nature of constructing Enum + * resolvers. + */ + @SuppressWarnings({ "unchecked" }) + public static EnumResolver constructUnsafe(Class rawEnumCls, AnnotationIntrospector ai) + { + /* This is oh so wrong... but at least ugliness is mostly hidden in just + * this one place. + */ + Class> enumCls = (Class>) rawEnumCls; + return constructFor(enumCls, ai); + } + + /** + * Method that needs to be used instead of {@link #constructUsingToString} + * if static type of enum is not known. + */ + @SuppressWarnings({ "unchecked" }) + public static EnumResolver constructUnsafeUsingToString(Class rawEnumCls) + { + // oh so wrong... not much that can be done tho + Class> enumCls = (Class>) rawEnumCls; + return constructUsingToString(enumCls); + } + + /** + * Method used when actual String serialization is indicated using @JsonValue + * on a method. + */ + @SuppressWarnings({ "unchecked" }) + public static EnumResolver constructUnsafeUsingMethod(Class rawEnumCls, Method accessor) + { + // wrong as ever but: + Class> enumCls = (Class>) rawEnumCls; + return constructUsingMethod(enumCls, accessor); + } + + public CompactStringObjectMap constructLookup() { + return CompactStringObjectMap.construct(_enumsById); + } + + public Enum findEnum(String key) { return _enumsById.get(key); } + + public Enum getEnum(int index) { + if (index < 0 || index >= _enums.length) { + return null; + } + return _enums[index]; + } + + public Enum[] getRawEnums() { + return _enums; + } + + public List> getEnums() { + ArrayList> enums = new ArrayList>(_enums.length); + for (Enum e : _enums) { + enums.add(e); + } + return enums; + } + + /** + * @since 2.7.3 + */ + public Collection getEnumIds() { + return _enumsById.keySet(); + } + + public Class> getEnumClass() { return _enumClass; } + + public int lastValidIndex() { return _enums.length-1; } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/EnumValues.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/EnumValues.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/EnumValues.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,116 @@ +package com.fasterxml.jackson.databind.util; + +import java.util.*; + +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; + +/** + * Helper class used for storing String serializations of + * enumerations. + */ +public final class EnumValues + implements java.io.Serializable +{ + private static final long serialVersionUID = 1; + + private final Class> _enumClass; + + private final Enum[] _values; + private final SerializableString[] _textual; + + private transient EnumMap _asMap; + + private EnumValues(Class> enumClass, SerializableString[] textual) + { + _enumClass = enumClass; + _values = enumClass.getEnumConstants(); + _textual = textual; + } + + /** + * NOTE: do NOT call this if configuration may change, and choice between toString() + * and name() might change dynamically. + */ + public static EnumValues construct(SerializationConfig config, Class> enumClass) { + if (config.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { + return constructFromToString(config, enumClass); + } + return constructFromName(config, enumClass); + } + + public static EnumValues constructFromName(MapperConfig config, Class> enumClass) + { + // Enum types with per-instance sub-classes need special handling + Class> enumCls = ClassUtil.findEnumType(enumClass); + Enum[] enumValues = enumCls.getEnumConstants(); + if (enumValues == null) { + throw new IllegalArgumentException("Can not determine enum constants for Class "+enumClass.getName()); + } + String[] names = config.getAnnotationIntrospector().findEnumValues(enumCls, enumValues, new String[enumValues.length]); + SerializableString[] textual = new SerializableString[enumValues.length]; + for (int i = 0, len = enumValues.length; i < len; ++i) { + Enum en = enumValues[i]; + String name = names[i]; + if (name == null) { + name = en.name(); + } + textual[en.ordinal()] = config.compileString(name); + } + return new EnumValues(enumClass, textual); + } + + public static EnumValues constructFromToString(MapperConfig config, Class> enumClass) + { + Class> cls = ClassUtil.findEnumType(enumClass); + Enum[] values = cls.getEnumConstants(); + if (values != null) { + SerializableString[] textual = new SerializableString[values.length]; + for (Enum en : values) { + textual[en.ordinal()] = config.compileString(en.toString()); + } + return new EnumValues(enumClass, textual); + } + throw new IllegalArgumentException("Can not determine enum constants for Class "+enumClass.getName()); + } + + public SerializableString serializedValueFor(Enum key) { + return _textual[key.ordinal()]; + } + + public Collection values() { + return Arrays.asList(_textual); + } + + /** + * Convenience accessor for getting raw Enum instances. + * + * @since 2.6 + */ + public List> enums() { + return Arrays.asList(_values); + } + + /** + * Method used for serialization and introspection by core Jackson code. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public EnumMap internalMap() { + EnumMap result = _asMap; + if (result == null) { + // Alas, need to create it in a round-about way, due to typing constraints... + Map,SerializableString> map = new LinkedHashMap,SerializableString>(); + for (Enum en : _values) { + map.put(en, _textual[en.ordinal()]); + } + result = new EnumMap(map); + } + return result; + } + + /** + * @since 2.2 + */ + public Class> getEnumClass() { return _enumClass; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.util; + +import java.text.*; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +/** + * Provide a fast thread-safe formatter/parser DateFormat for ISO8601 dates ONLY. + * It was mainly done to be used with Jackson JSON Processor. + *

+ * Watch out for clone implementation that returns itself. + *

+ * All other methods but parse and format and clone are undefined behavior. + * + * @see ISO8601Utils + */ +public class ISO8601DateFormat extends DateFormat +{ + private static final long serialVersionUID = 1L; + + // those classes are to try to allow a consistent behavior for hascode/equals and other methods + private static Calendar CALENDAR = new GregorianCalendar(); + private static NumberFormat NUMBER_FORMAT = new DecimalFormat(); + + public ISO8601DateFormat() { + this.numberFormat = NUMBER_FORMAT; + this.calendar = CALENDAR; + } + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { + String value = ISO8601Utils.format(date); + toAppendTo.append(value); + return toAppendTo; + } + + @Override + public Date parse(String source, ParsePosition pos) { + try { + return ISO8601Utils.parse(source, pos); + } + catch (ParseException e) { + return null; + } + } + + //supply our own parse(String) since pos isn't updated during parsing, + //but the exception should have the right error offset. + @Override + public Date parse(String source) throws ParseException { + return ISO8601Utils.parse(source, new ParsePosition(0)); + } + + @Override + public Object clone() { + /* Jackson calls clone for every call. Since this instance is + * immutable (and hence thread-safe) + * we can just return this instance + */ + return this; + } + + @Override + public String toString() { return getClass().getName(); } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ISO8601Utils.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ISO8601Utils.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ISO8601Utils.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,419 @@ +package com.fasterxml.jackson.databind.util; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.*; + +/** + * Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than using SimpleDateFormat so + * highly suitable if you (un)serialize lots of date objects. + * + * Supported parse format: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]] + * + * @see this specification + */ +public class ISO8601Utils +{ + @Deprecated // since 2.7 + private static final String GMT_ID = "GMT"; + + /** + * ID to represent the 'UTC' string, default timezone since Jackson 2.7 + * + * @since 2.7 + */ + private static final String UTC_ID = "UTC"; + + /** + * The GMT timezone, prefetched to avoid more lookups. + * + * @deprecated Since 2.7 use {@link #TIMEZONE_UTC} instead + */ + @Deprecated + private static final TimeZone TIMEZONE_GMT = TimeZone.getTimeZone(GMT_ID); + + /** + * The UTC timezone, prefetched to avoid more lookups. + * + * @since 2.7 + */ + private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID); + + /** + * Timezone we use for 'Z' in ISO-8601 date/time values: since 2.7 + * {@link #TIMEZONE_UTC}; with earlier versions up to 2.7 was {@link #TIMEZONE_GMT}. + */ + private static final TimeZone TIMEZONE_Z = TIMEZONE_UTC; + + /* + /********************************************************** + /* Static factories + /********************************************************** + */ + + /** + * Accessor for static GMT timezone instance. + * + * @deprecated since 2.6 + */ + @Deprecated // since 2.6 + public static TimeZone timeZoneGMT() { + return TIMEZONE_GMT; + } + + /* + /********************************************************** + /* Formatting + /********************************************************** + */ + + /** + * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision) + * + * @param date the date to format + * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ' + */ + public static String format(Date date) { + return format(date, false, TIMEZONE_UTC); + } + + /** + * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone) + * + * @param date the date to format + * @param millis true to include millis precision otherwise false + * @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z' + */ + public static String format(Date date, boolean millis) { + return format(date, millis, TIMEZONE_UTC); + } + + /** + * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] + * + * @param date the date to format + * @param millis true to include millis precision otherwise false + * @param tz timezone to use for the formatting (UTC will produce 'Z') + * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] + */ + public static String format(Date date, boolean millis, TimeZone tz) { + Calendar calendar = new GregorianCalendar(tz, Locale.US); + calendar.setTime(date); + + // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) + int capacity = "yyyy-MM-ddThh:mm:ss".length(); + capacity += millis ? ".sss".length() : 0; + capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length(); + StringBuilder formatted = new StringBuilder(capacity); + + padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length()); + formatted.append('-'); + padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length()); + formatted.append('-'); + padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length()); + formatted.append('T'); + padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length()); + formatted.append(':'); + padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length()); + formatted.append(':'); + padInt(formatted, calendar.get(Calendar.SECOND), "ss".length()); + if (millis) { + formatted.append('.'); + padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length()); + } + + int offset = tz.getOffset(calendar.getTimeInMillis()); + if (offset != 0) { + int hours = Math.abs((offset / (60 * 1000)) / 60); + int minutes = Math.abs((offset / (60 * 1000)) % 60); + formatted.append(offset < 0 ? '-' : '+'); + padInt(formatted, hours, "hh".length()); + formatted.append(':'); + padInt(formatted, minutes, "mm".length()); + } else { + formatted.append('Z'); + } + + return formatted.toString(); + } + + /* + /********************************************************** + /* Parsing + /********************************************************** + */ + + /** + * Parse a date from ISO-8601 formatted string. It expects a format + * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]] + * + * @param date ISO string to parse in the appropriate format. + * @param pos The position to start parsing from, updated to where parsing stopped. + * @return the parsed date + * @throws ParseException if the date is not in the appropriate format + */ + public static Date parse(String date, ParsePosition pos) throws ParseException { + Exception fail = null; + try { + int offset = pos.getIndex(); + + // extract year + int year = parseInt(date, offset, offset += 4); + if (checkOffset(date, offset, '-')) { + offset += 1; + } + + // extract month + int month = parseInt(date, offset, offset += 2); + if (checkOffset(date, offset, '-')) { + offset += 1; + } + + // extract day + int day = parseInt(date, offset, offset += 2); + // default time value + int hour = 0; + int minutes = 0; + int seconds = 0; + int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time + + // if the value has no time component (and no time zone), we are done + boolean hasT = checkOffset(date, offset, 'T'); + + if (!hasT && (date.length() <= offset)) { + Calendar calendar = new GregorianCalendar(year, month - 1, day); + + pos.setIndex(offset); + return calendar.getTime(); + } + + if (hasT) { + + // extract hours, minutes, seconds and milliseconds + hour = parseInt(date, offset += 1, offset += 2); + if (checkOffset(date, offset, ':')) { + offset += 1; + } + + minutes = parseInt(date, offset, offset += 2); + if (checkOffset(date, offset, ':')) { + offset += 1; + } + // second and milliseconds can be optional + if (date.length() > offset) { + char c = date.charAt(offset); + if (c != 'Z' && c != '+' && c != '-') { + seconds = parseInt(date, offset, offset += 2); + if (seconds > 59 && seconds < 63) seconds = 59; // truncate up to 3 leap seconds + // milliseconds can be optional in the format + if (checkOffset(date, offset, '.')) { + offset += 1; + int endOffset = indexOfNonDigit(date, offset + 1); // assume at least one digit + int parseEndOffset = Math.min(endOffset, offset + 3); // parse up to 3 digits + int fraction = parseInt(date, offset, parseEndOffset); + // compensate for "missing" digits + switch (parseEndOffset - offset) { // number of digits parsed + case 2: + milliseconds = fraction * 10; + break; + case 1: + milliseconds = fraction * 100; + break; + default: + milliseconds = fraction; + } + offset = endOffset; + } + } + } + } + + // extract timezone + if (date.length() <= offset) { + throw new IllegalArgumentException("No time zone indicator"); + } + + TimeZone timezone = null; + char timezoneIndicator = date.charAt(offset); + + if (timezoneIndicator == 'Z') { + timezone = TIMEZONE_Z; + offset += 1; + } else if (timezoneIndicator == '+' || timezoneIndicator == '-') { + String timezoneOffset = date.substring(offset); + offset += timezoneOffset.length(); + // 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00" + if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) { + timezone = TIMEZONE_Z; + } else { + // 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC... + // not sure why, but that's the way it looks. Further, Javadocs for + // `java.util.TimeZone` specifically instruct use of GMT as base for + // custom timezones... odd. + String timezoneId = "GMT" + timezoneOffset; +// String timezoneId = "UTC" + timezoneOffset; + + timezone = TimeZone.getTimeZone(timezoneId); + + String act = timezone.getID(); + if (!act.equals(timezoneId)) { + /* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given + * one without. If so, don't sweat. + * Yes, very inefficient. Hopefully not hit often. + * If it becomes a perf problem, add 'loose' comparison instead. + */ + String cleaned = act.replace(":", ""); + if (!cleaned.equals(timezoneId)) { + throw new IndexOutOfBoundsException("Mismatching time zone indicator: "+timezoneId+" given, resolves to " + +timezone.getID()); + } + } + } + } else { + throw new IndexOutOfBoundsException("Invalid time zone indicator '" + timezoneIndicator+"'"); + } + + Calendar calendar = new GregorianCalendar(timezone); + calendar.setLenient(false); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minutes); + calendar.set(Calendar.SECOND, seconds); + calendar.set(Calendar.MILLISECOND, milliseconds); + + pos.setIndex(offset); + return calendar.getTime(); + // If we get a ParseException it'll already have the right message/offset. + // Other exception types can convert here. + } catch (IndexOutOfBoundsException e) { + fail = e; + } catch (NumberFormatException e) { + fail = e; + } catch (IllegalArgumentException e) { + fail = e; + } + String input = (date == null) ? null : ('"' + date + '"'); + String msg = fail.getMessage(); + if (msg == null || msg.isEmpty()) { + msg = "("+fail.getClass().getName()+")"; + } + ParseException ex = new ParseException("Failed to parse date " + input + ": " + msg, pos.getIndex()); + ex.initCause(fail); + throw ex; + } + + /** + * Check if the expected character exist at the given offset in the value. + * + * @param value the string to check at the specified offset + * @param offset the offset to look for the expected character + * @param expected the expected character + * @return true if the expected character exist at the given offset + */ + private static boolean checkOffset(String value, int offset, char expected) { + return (offset < value.length()) && (value.charAt(offset) == expected); + } + + /** + * Parse an integer located between 2 given offsets in a string + * + * @param value the string to parse + * @param beginIndex the start index for the integer in the string + * @param endIndex the end index for the integer in the string + * @return the int + * @throws NumberFormatException if the value is not a number + */ + private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException { + if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) { + throw new NumberFormatException(value); + } + // use same logic as in Integer.parseInt() but less generic we're not supporting negative values + int i = beginIndex; + int result = 0; + int digit; + if (i < endIndex) { + digit = Character.digit(value.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); + } + result = -digit; + } + while (i < endIndex) { + digit = Character.digit(value.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); + } + result *= 10; + result -= digit; + } + return -result; + } + + /** + * Zero pad a number to a specified length + * + * @param buffer buffer to use for padding + * @param value the integer value to pad if necessary. + * @param length the length of the string we should zero pad + */ + private static void padInt(StringBuilder buffer, int value, int length) { + String strValue = Integer.toString(value); + for (int i = length - strValue.length(); i > 0; i--) { + buffer.append('0'); + } + buffer.append(strValue); + } + + /** + * Returns the index of the first character in the string that is not a digit, starting at offset. + */ + private static int indexOfNonDigit(String string, int offset) { + for (int i = offset; i < string.length(); i++) { + char c = string.charAt(i); + if (c < '0' || c > '9') return i; + } + return string.length(); + } + + public static void main(String[] args) + { + final int REPS = 250000; + while (true) { + long start = System.currentTimeMillis(); + int resp = test1(REPS, 3); + long msecs = System.currentTimeMillis() - start; + System.out.println("Pow ("+resp+") -> "+msecs+" ms"); + + start = System.currentTimeMillis(); + resp = test2(REPS, 3); + msecs = System.currentTimeMillis() - start; + System.out.println("Iter ("+resp+") -> "+msecs+" ms"); + } + } + + static int test1(int reps, int pow) + { + int resp = 3; + while (--reps >= 0) { + resp = (int) Math.pow(10, pow); + } + return resp; + } + + static int test2(int reps, int pow) + { + int resp = 3; + while (--reps >= 0) { + resp = 10; + int p = pow; + + while (--p > 0) { + resp *= 10; + } + } + return resp; + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/JSONPObject.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/JSONPObject.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/JSONPObject.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,92 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Container class that can be used to wrap any Object instances (including + * nulls), and will serialize embedded in + * JSONP wrapping. + * + * @see com.fasterxml.jackson.databind.util.JSONWrappedObject + * + * @author tatu + */ +public class JSONPObject + implements JsonSerializable +{ + /** + * JSONP function name to use for serialization + */ + protected final String _function; + + /** + * Value to be serialized as JSONP padded; can be null. + */ + protected final Object _value; + + /** + * Optional static type to use for serialization; if null, runtime + * type is used. Can be used to specify declared type which defines + * serializer to use, as well as aspects of extra type information + * to include (if any). + */ + protected final JavaType _serializationType; + + public JSONPObject(String function, Object value) { + this(function, value, (JavaType) null); + } + + public JSONPObject(String function, Object value, JavaType asType) + { + _function = function; + _value = value; + _serializationType = asType; + } + + /* + /********************************************************** + /* JsonSerializable(WithType) implementation + /********************************************************** + */ + + @Override + public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + // No type for JSONP wrapping: value serializer will handle typing for value: + serialize(jgen, provider); + } + + @Override + public void serialize(JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException + { + // First, wrapping: + jgen.writeRaw(_function); + jgen.writeRaw('('); + if (_value == null) { + provider.defaultSerializeNull(jgen); + } else if (_serializationType != null) { + provider.findTypedValueSerializer(_serializationType, true, null).serialize(_value, jgen, provider); + } else { + Class cls = _value.getClass(); + provider.findTypedValueSerializer(cls, true, null).serialize(_value, jgen, provider); + } + jgen.writeRaw(')'); + } + + /* + /************************************************************** + /* Accessors + /************************************************************** + */ + + public String getFunction() { return _function; } + public Object getValue() { return _value; } + public JavaType getSerializationType() { return _serializationType; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/JSONWrappedObject.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/JSONWrappedObject.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/JSONWrappedObject.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,104 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.IOException; + +import com.fasterxml.jackson.core.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * General-purpose wrapper class that can be used to decorate serialized + * value with arbitrary literal prefix and suffix. This can be used for + * example to construct arbitrary Javascript values (similar to how basic + * function name and parenthesis are used with JSONP). + * + * @see com.fasterxml.jackson.databind.util.JSONPObject + */ +public class JSONWrappedObject implements JsonSerializable +{ + /** + * Literal String to output before serialized value. + * Will not be quoted when serializing value. + */ + protected final String _prefix; + + /** + * Literal String to output after serialized value. + * Will not be quoted when serializing value. + */ + protected final String _suffix; + + /** + * Value to be serialized as JSONP padded; can be null. + */ + protected final Object _value; + + /** + * Optional static type to use for serialization; if null, runtime + * type is used. Can be used to specify declared type which defines + * serializer to use, as well as aspects of extra type information + * to include (if any). + */ + protected final JavaType _serializationType; + + public JSONWrappedObject(String prefix, String suffix, Object value) { + this(prefix, suffix, value, (JavaType) null); + } + + /** + * Constructor that should be used when specific serialization type to use + * is important, and needs to be passed instead of just using runtime + * (type-erased) type of the value. + */ + public JSONWrappedObject(String prefix, String suffix, Object value, JavaType asType) + { + _prefix = prefix; + _suffix = suffix; + _value = value; + _serializationType = asType; + } + + /* + /************************************************************** + /* JsonSerializable(WithType) implementation + /************************************************************** + */ + + @Override + public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + // No type for JSONP wrapping: value serializer will handle typing for value: + serialize(jgen, provider); + } + + @Override + public void serialize(JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException + { + // First, wrapping: + if (_prefix != null) jgen.writeRaw(_prefix); + if (_value == null) { + provider.defaultSerializeNull(jgen); + } else if (_serializationType != null) { + provider.findTypedValueSerializer(_serializationType, true, null).serialize(_value, jgen, provider); + } else { + Class cls = _value.getClass(); + provider.findTypedValueSerializer(cls, true, null).serialize(_value, jgen, provider); + } + if (_suffix != null) jgen.writeRaw(_suffix); + } + + /* + /************************************************************** + /* Accessors + /************************************************************** + */ + + public String getPrefix() { return _prefix; } + public String getSuffix() { return _suffix; } + public Object getValue() { return _value; } + public JavaType getSerializationType() { return _serializationType; } + +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/LRUMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/LRUMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/LRUMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,98 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Helper for simple bounded maps used for reusing lookup values. + *

+ * Note that serialization behavior is such that contents are NOT serialized, + * on assumption that all use cases are for caching where persistence + * does not make sense. The only thing serialized is the cache size of Map. + *

+ * NOTE: since version 2.4.2, this is NOT an LRU-based at all; reason + * being that it is not possible to use JDK components that do LRU _AND_ perform + * well wrt synchronization on multi-core systems. So we choose efficient synchronization + * over potentially more efficient handling of entries. + *

+ * And yes, there are efficient LRU implementations such as + * concurrentlinkedhashmap; + * but at this point we really try to keep external deps to minimum. But perhaps + * a shaded variant may be used one day. + */ +public class LRUMap + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final transient int _maxEntries; + + protected final transient ConcurrentHashMap _map; + + public LRUMap(int initialEntries, int maxEntries) + { + // We'll use concurrency level of 4, seems reasonable + _map = new ConcurrentHashMap(initialEntries, 0.8f, 4); + _maxEntries = maxEntries; + } + + public V put(K key, V value) { + if (_map.size() >= _maxEntries) { + // double-locking, yes, but safe here; trying to avoid "clear storms" + synchronized (this) { + if (_map.size() >= _maxEntries) { + clear(); + } + } + } + return _map.put(key, value); + } + + /** + * @since 2.5 + */ + public V putIfAbsent(K key, V value) { + // not 100% optimal semantically, but better from correctness (never exceeds + // defined maximum) and close enough all in all: + if (_map.size() >= _maxEntries) { + synchronized (this) { + if (_map.size() >= _maxEntries) { + clear(); + } + } + } + return _map.putIfAbsent(key, value); + } + + // NOTE: key is of type Object only to retain binary backwards-compatibility + public V get(Object key) { return _map.get(key); } + + public void clear() { _map.clear(); } + public int size() { return _map.size(); } + + /* + /********************************************************** + /* Serializable overrides + /********************************************************** + */ + + /** + * Ugly hack, to work through the requirement that _value is indeed final, + * and that JDK serialization won't call ctor(s) if Serializable is implemented. + * + * @since 2.1 + */ + protected transient int _jdkSerializeMaxEntries; + + private void readObject(ObjectInputStream in) throws IOException { + _jdkSerializeMaxEntries = in.readInt(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeInt(_jdkSerializeMaxEntries); + } + + protected Object readResolve() { + return new LRUMap(_jdkSerializeMaxEntries, _jdkSerializeMaxEntries); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/LinkedNode.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/LinkedNode.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/LinkedNode.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,54 @@ +package com.fasterxml.jackson.databind.util; + +/** + * Node of a forward-only linked list. + * + * @author tatu + * + * @param Type of contained object + */ +public final class LinkedNode +{ + private final T value; + + private LinkedNode next; + + public LinkedNode(T value, LinkedNode next) + { + this.value = value; + this.next = next; + } + + public void linkNext(LinkedNode n) + { + if (next != null) { // sanity check + throw new IllegalStateException(); + } + next = n; + } + + public LinkedNode next() { return next; } + + public T value() { return value; } + + /** + * Convenience method that can be used to check if a linked list + * with given head node (which may be null to indicate empty list) + * contains given value + * + * @param Type argument that defines contents of the linked list parameter + * @param node Head node of the linked list + * @param value Value to look for + * @return True if linked list contains the value, false otherwise + */ + public static boolean contains(LinkedNode node, ST value) + { + while (node != null) { + if (node.value() == value) { + return true; + } + node = node.next(); + } + return false; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/NameTransformer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/NameTransformer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/NameTransformer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,145 @@ +package com.fasterxml.jackson.databind.util; + +/** + * Helper class used to encapsulate details of name mangling, transforming + * of names using different strategies (prefixes, suffixes). + * Default implementation is "no-operation" (aka identity transformation). + */ +public abstract class NameTransformer +{ + /** + * Singleton "no-operation" transformer which simply returns given + * name as is. Used commonly as placeholder or marker. + */ + public final static NameTransformer NOP = new NopTransformer(); + + protected final static class NopTransformer + extends NameTransformer + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + @Override + public String transform(String name) { + return name; + } + @Override + public String reverse(String transformed) { + // identity transformation is always reversible: + return transformed; + } + } + + protected NameTransformer() { } + + /** + * Factory method for constructing a simple transformer based on + * prefix and/or suffix. + */ + public static NameTransformer simpleTransformer(final String prefix, final String suffix) + { + boolean hasPrefix = (prefix != null) && (prefix.length() > 0); + boolean hasSuffix = (suffix != null) && (suffix.length() > 0); + + if (hasPrefix) { + if (hasSuffix) { + return new NameTransformer() { + @Override + public String transform(String name) { return prefix + name + suffix; } + @Override + public String reverse(String transformed) { + if (transformed.startsWith(prefix)) { + String str = transformed.substring(prefix.length()); + if (str.endsWith(suffix)) { + return str.substring(0, str.length() - suffix.length()); + } + } + return null; + } + @Override + public String toString() { return "[PreAndSuffixTransformer('"+prefix+"','"+suffix+"')]"; } + }; + } + return new NameTransformer() { + @Override + public String transform(String name) { return prefix + name; } + @Override + public String reverse(String transformed) { + if (transformed.startsWith(prefix)) { + return transformed.substring(prefix.length()); + } + return null; + } + @Override + public String toString() { return "[PrefixTransformer('"+prefix+"')]"; } + }; + } + if (hasSuffix) { + return new NameTransformer() { + @Override + public String transform(String name) { return name + suffix; } + @Override + public String reverse(String transformed) { + if (transformed.endsWith(suffix)) { + return transformed.substring(0, transformed.length() - suffix.length()); + } + return null; + } + @Override + public String toString() { return "[SuffixTransformer('"+suffix+"')]"; } + }; + } + return NOP; + } + + /** + * Method that constructs transformer that applies given transformers + * as a sequence; essentially combines separate transform operations + * into one logical transformation. + */ + public static NameTransformer chainedTransformer(NameTransformer t1, NameTransformer t2) { + return new Chained(t1, t2); + } + + /** + * Method called when (forward) transformation is needed. + */ + public abstract String transform(String name); + + /** + * Method called when reversal of transformation is needed; should return + * null if this is not possible, that is, given name can not have been + * result of calling {@link #transform} of this object. + */ + public abstract String reverse(String transformed); + + public static class Chained extends NameTransformer + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + protected final NameTransformer _t1, _t2; + + public Chained(NameTransformer t1, NameTransformer t2) { + _t1 = t1; + _t2 = t2; + } + + @Override + public String transform(String name) { + return _t1.transform(_t2.transform(name)); + } + + @Override + public String reverse(String transformed) { + transformed = _t1.reverse(transformed); + if (transformed != null) { + transformed = _t2.reverse(transformed); + } + return transformed; + } + + @Override + public String toString() { return "[ChainedTransformer("+_t1+", "+_t2+")]"; } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Named.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Named.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/Named.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,8 @@ +package com.fasterxml.jackson.databind.util; + +/** + * Simple tag interface mostly to allow sorting by name. + */ +public interface Named { + public String getName(); +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ObjectBuffer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ObjectBuffer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ObjectBuffer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,211 @@ +package com.fasterxml.jackson.databind.util; + +import java.lang.reflect.Array; +import java.util.List; + +/** + * Helper class to use for constructing Object arrays by appending entries + * to create arrays of various lengths (length that is not known a priori). + */ +public final class ObjectBuffer +{ + // // // Config constants + + /** + * Also: let's expand by doubling up until 64k chunks (which is 16k entries for + * 32-bit machines) + */ + private final static int SMALL_CHUNK = (1 << 14); + + /** + * Let's limit maximum size of chunks we use; helps avoid excessive allocation + * overhead for huge data sets. + * For now, let's limit to quarter million entries, 1 meg chunks for 32-bit + * machines. + */ + private final static int MAX_CHUNK = (1 << 18); + + // // // Data storage + + private LinkedNode _head; + + private LinkedNode _tail; + + /** + * Number of total buffered entries in this buffer, counting all instances + * within linked list formed by following {@link #_head}. + */ + private int _size; + + // // // Simple reuse + + /** + * Reusable Object array, stored here after buffer has been released having + * been used previously. + */ + private Object[] _freeBuffer; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + public ObjectBuffer() { } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + /** + * Method called to start buffering process. Will ensure that the buffer + * is empty, and then return an object array to start chunking content on + */ + public Object[] resetAndStart() + { + _reset(); + if (_freeBuffer == null) { + return new Object[12]; + } + return _freeBuffer; + } + + /** + * Method called to add a full Object array as a chunk buffered within + * this buffer, and to obtain a new array to fill. Caller is not to use + * the array it gives; but to use the returned array for continued + * buffering. + * + * @param fullChunk Completed chunk that the caller is requesting + * to append to this buffer. It is generally chunk that was + * returned by an earlier call to {@link #resetAndStart} or + * {@link #appendCompletedChunk} (although this is not required or + * enforced) + * + * @return New chunk buffer for caller to fill + */ + public Object[] appendCompletedChunk(Object[] fullChunk) + { + LinkedNode next = new LinkedNode(fullChunk, null); + if (_head == null) { // first chunk + _head = _tail = next; + } else { // have something already + _tail.linkNext(next); + _tail = next; + } + int len = fullChunk.length; + _size += len; + // double the size for small chunks + if (len < SMALL_CHUNK) { + len += len; + } else if (len < MAX_CHUNK) { // but by +25% for larger (to limit overhead) + len += (len >> 2); + } + return new Object[len]; + } + + /** + * Method called to indicate that the buffering process is now + * complete; and to construct a combined exactly-sized result + * array. Additionally the buffer itself will be reset to + * reduce memory retention. + *

+ * Resulting array will be of generic Object[] type: + * if a typed array is needed, use the method with additional + * type argument. + */ + public Object[] completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries) + { + int totalSize = lastChunkEntries + _size; + Object[] result = new Object[totalSize]; + _copyTo(result, totalSize, lastChunk, lastChunkEntries); + return result; + } + + /** + * Type-safe alternative to + * {@link #completeAndClearBuffer(Object[], int)}, to allow + * for constructing explicitly typed result array. + * + * @param componentType Type of elements included in the buffer. Will be + * used for constructing the result array. + */ + public T[] completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries, Class componentType) + { + int totalSize = lastChunkEntries + _size; + @SuppressWarnings("unchecked") + T[] result = (T[]) Array.newInstance(componentType, totalSize); + _copyTo(result, totalSize, lastChunk, lastChunkEntries); + _reset(); + return result; + } + + public void completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries, List resultList) + { + for (LinkedNode n = _head; n != null; n = n.next()) { + Object[] curr = n.value(); + for (int i = 0, len = curr.length; i < len; ++i) { + resultList.add(curr[i]); + } + } + // and then the last one + for (int i = 0; i < lastChunkEntries; ++i) { + resultList.add(lastChunk[i]); + } + } + + /** + * Helper method that can be used to check how much free capacity + * will this instance start with. Can be used to choose the best + * instance to reuse, based on size of reusable object chunk + * buffer holds reference to. + */ + public int initialCapacity() { + return (_freeBuffer == null) ? 0 : _freeBuffer.length; + } + + /** + * Method that can be used to check how many Objects have been buffered + * within this buffer. + */ + public int bufferedSize() { return _size; } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected void _reset() + { + // can we reuse the last (and thereby biggest) array for next time? + if (_tail != null) { + _freeBuffer = _tail.value(); + } + // either way, must discard current contents + _head = _tail = null; + _size = 0; + } + + protected final void _copyTo(Object resultArray, int totalSize, + Object[] lastChunk, int lastChunkEntries) + { + int ptr = 0; + + for (LinkedNode n = _head; n != null; n = n.next()) { + Object[] curr = n.value(); + int len = curr.length; + System.arraycopy(curr, 0, resultArray, ptr, len); + ptr += len; + } + System.arraycopy(lastChunk, 0, resultArray, ptr, lastChunkEntries); + ptr += lastChunkEntries; + + // sanity check (could have failed earlier due to out-of-bounds, too) + if (ptr != totalSize) { + throw new IllegalStateException("Should have gotten "+totalSize+" entries, got "+ptr); + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ObjectIdMap.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ObjectIdMap.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ObjectIdMap.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,38 @@ +package com.fasterxml.jackson.databind.util; + +import java.util.IdentityHashMap; + +/** + * Map used during serialization, to keep track of referable Objects + * along with lazily evaluated ids. + */ +@SuppressWarnings("serial") +public class ObjectIdMap extends IdentityHashMap +{ + public ObjectIdMap() + { + super(16); + } + + /* + /********************************************************** + /* API + /********************************************************** + */ + + /** + * Method that is called to figure out whether we have already + * seen given POJO: if yes, we will return its id (first looking + * it up as necessary); if not, we will mark down that we have + * seen it but return null. + */ + public Object findId(Object pojo) + { + return get(pojo); + } + + public void insertId(Object pojo, Object id) + { + put(pojo, id); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,182 @@ +package com.fasterxml.jackson.databind.util; + +/** + * Base class for specialized primitive array builders. + */ +public abstract class PrimitiveArrayBuilder +{ + /** + * Let's start with small chunks; typical usage is for small arrays anyway. + */ + final static int INITIAL_CHUNK_SIZE = 12; + + /** + * Also: let's expand by doubling up until 64k chunks (which is 16k entries for + * 32-bit machines) + */ + final static int SMALL_CHUNK_SIZE = (1 << 14); + + /** + * Let's limit maximum size of chunks we use; helps avoid excessive allocation + * overhead for huge data sets. + * For now, let's limit to quarter million entries, 1 meg chunks for 32-bit + * machines. + */ + final static int MAX_CHUNK_SIZE = (1 << 18); + + // // // Data storage + + protected T _freeBuffer; + + protected Node _bufferHead; + + protected Node _bufferTail; + + /** + * Number of total buffered entries in this buffer, counting all instances + * within linked list formed by following {@link #_bufferHead}. + */ + protected int _bufferedEntryCount; + + // // // Recycled instances of sub-classes + + // // // Life-cycle + + protected PrimitiveArrayBuilder() { } + + /* + /********************************************************** + /* Public API + /********************************************************** + */ + + public int bufferedSize() { return _bufferedEntryCount; } + + public T resetAndStart() + { + _reset(); + return (_freeBuffer == null) ? + _constructArray(INITIAL_CHUNK_SIZE) : _freeBuffer; + } + + /** + * @return Length of the next chunk to allocate + */ + public final T appendCompletedChunk(T fullChunk, int fullChunkLength) + { + Node next = new Node(fullChunk, fullChunkLength); + if (_bufferHead == null) { // first chunk + _bufferHead = _bufferTail = next; + } else { // have something already + _bufferTail.linkNext(next); + _bufferTail = next; + } + _bufferedEntryCount += fullChunkLength; + int nextLen = fullChunkLength; // start with last chunk size + // double the size for small chunks + if (nextLen < SMALL_CHUNK_SIZE) { + nextLen += nextLen; + } else { // but by +25% for larger (to limit overhead) + nextLen += (nextLen >> 2); + } + return _constructArray(nextLen); + } + + public T completeAndClearBuffer(T lastChunk, int lastChunkEntries) + { + int totalSize = lastChunkEntries + _bufferedEntryCount; + T resultArray = _constructArray(totalSize); + + int ptr = 0; + + for (Node n = _bufferHead; n != null; n = n.next()) { + ptr = n.copyData(resultArray, ptr); + } + System.arraycopy(lastChunk, 0, resultArray, ptr, lastChunkEntries); + ptr += lastChunkEntries; + + // sanity check (could have failed earlier due to out-of-bounds, too) + if (ptr != totalSize) { + throw new IllegalStateException("Should have gotten "+totalSize+" entries, got "+ptr); + } + return resultArray; + } + + /* + /********************************************************** + /* Abstract methods for sub-classes to implement + /********************************************************** + */ + + protected abstract T _constructArray(int len); + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected void _reset() + { + // can we reuse the last (and thereby biggest) array for next time? + if (_bufferTail != null) { + _freeBuffer = _bufferTail.getData(); + } + // either way, must discard current contents + _bufferHead = _bufferTail = null; + _bufferedEntryCount = 0; + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * For actual buffering beyond the current buffer, we can actually + * use shared class which only deals with opaque "untyped" chunks. + * This works because {@link java.lang.System#arraycopy} does not + * take type; hence we can implement some aspects of primitive data + * handling in generic fashion. + */ + final static class Node + { + /** + * Data stored in this node. + */ + final T _data; + + /** + * Number entries in the (untyped) array. Offset is assumed to be 0. + */ + final int _dataLength; + + Node _next; + + public Node(T data, int dataLen) + { + _data = data; + _dataLength = dataLen; + } + + public T getData() { return _data; } + + public int copyData(T dst, int ptr) + { + System.arraycopy(_data, 0, dst, ptr, _dataLength); + ptr += _dataLength; + return ptr; + } + + public Node next() { return _next; } + + public void linkNext(Node next) + { + if (_next != null) { // sanity check + throw new IllegalStateException(); + } + _next = next; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/RawValue.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/RawValue.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/RawValue.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,125 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +/** + * Helper class used to encapsulate "raw values", pre-encoded textual content + * that can be output as opaque value with no quoting/escaping, using + * {@link com.fasterxml.jackson.core.JsonGenerator#writeRawValue(String)}. + * It may be stored in {@link TokenBuffer}, as well as in Tree Model + * ({@link com.fasterxml.jackson.databind.JsonNode}) + * + * @since 2.6 + */ +public class RawValue + implements JsonSerializable +{ + /** + * Contents to serialize. Untyped because there are multiple types that are + * supported: {@link java.lang.String}, {@link JsonSerializable}, {@link SerializableString}. + */ + protected Object _value; + + public RawValue(String v) { + _value = v; + } + + public RawValue(SerializableString v) { + _value = v; + } + + public RawValue(JsonSerializable v) { + _value = v; + } + + /** + * Constructor that may be used by sub-classes, and allows passing value + * types other than ones for which explicit constructor exists. Caller has to + * take care that values of types not supported by base implementation are + * handled properly, usually by overriding some of existing serialization + * methods. + */ + protected RawValue(Object value, boolean bogus) { + _value = value; + } + + /** + * Accessor for returning enclosed raw value in whatever form it was created in + * (usually {@link java.lang.String}, {link SerializableString}, or any {@link JsonSerializable}). + */ + public Object rawValue() { + return _value; + } + + @Override + public void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException + { + if (_value instanceof JsonSerializable) { + ((JsonSerializable) _value).serialize(gen, serializers); + } else { + _serialize(gen); + } + } + + @Override + public void serializeWithType(JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) throws IOException + { + if (_value instanceof JsonSerializable) { + ((JsonSerializable) _value).serializeWithType(gen, serializers, typeSer); + } else if (_value instanceof SerializableString) { + /* Since these are not really to be deserialized (with or without type info), + * just re-route as regular write, which will create one... hopefully it works + */ + serialize(gen, serializers); + } + } + + public void serialize(JsonGenerator gen) throws IOException + { + if (_value instanceof JsonSerializable) { + // No SerializerProvider passed, must go via generator, callback + gen.writeObject(_value); + } else { + _serialize(gen); + } + } + + protected void _serialize(JsonGenerator gen) throws IOException + { + if (_value instanceof SerializableString) { + gen.writeRawValue((SerializableString) _value); + } else { + gen.writeRawValue(String.valueOf(_value)); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof RawValue)) return false; + RawValue other = (RawValue) o; + + if (_value == other._value) { + return true; + } + return (_value != null) && _value.equals(other._value); + } + + @Override + public int hashCode() { + return (_value == null) ? 0 : _value.hashCode(); + } + + @Override + public String toString() { + return String.format("[RawValue of type %s]", + (_value == null) ? "NULL" : _value.getClass().getName()); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/RootNameLookup.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/RootNameLookup.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/RootNameLookup.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,62 @@ +package com.fasterxml.jackson.databind.util; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * Helper class for caching resolved root names. + */ +public class RootNameLookup implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * For efficient operation, let's try to minimize number of times we + * need to introspect root element name to use. + */ + protected transient LRUMap _rootNames; + + public RootNameLookup() { + _rootNames = new LRUMap(20, 200); + } + + public PropertyName findRootName(JavaType rootType, MapperConfig config) { + return findRootName(rootType.getRawClass(), config); + } + + public PropertyName findRootName(Class rootType, MapperConfig config) + { + ClassKey key = new ClassKey(rootType); + PropertyName name = _rootNames.get(key); + if (name != null) { + return name; + } + BeanDescription beanDesc = config.introspectClassAnnotations(rootType); + AnnotationIntrospector intr = config.getAnnotationIntrospector(); + AnnotatedClass ac = beanDesc.getClassInfo(); + name = intr.findRootName(ac); + // No answer so far? Let's just default to using simple class name + if (name == null || !name.hasSimpleName()) { + // Should we strip out enclosing class tho? For now, nope: + name = PropertyName.construct(rootType.getSimpleName()); + } + _rootNames.put(key, name); + return name; + } + + /* + /********************************************************** + /* Serializable overrides + /********************************************************** + */ + + /** + * Need to override to reproduce cache object via constructor, instead + * of serialize/deserialize (since we do NOT want to retain cached data) + */ + protected Object readResolve() { + return new RootNameLookup(); + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,335 @@ +package com.fasterxml.jackson.databind.util; + +import java.util.Collections; +import java.util.Iterator; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.PropertyMetadata; +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.*; + +/** + * Simple immutable {@link BeanPropertyDefinition} implementation that can + * be wrapped around a {@link AnnotatedMember} that is a simple + * accessor (getter) or mutator (setter, constructor parameter) + * (or both, for fields). + */ +public class SimpleBeanPropertyDefinition + extends BeanPropertyDefinition +{ + protected final AnnotationIntrospector _introspector; + + /** + * Member that defines logical property. Assumption is that it + * should be a 'simple' accessor; meaning a zero-argument getter, + * single-argument setter or constructor parameter. + *

+ * NOTE: for "virtual" properties, this is null. + */ + protected final AnnotatedMember _member; + + /** + * @since 2.5 + */ + protected final PropertyMetadata _metadata; + + /** + * @since 2.5 + */ + protected final PropertyName _fullName; + + /** + * @since 2.5 + */ + protected final JsonInclude.Value _inclusion; + + /** + * @deprecated Since 2.5 use _fullName instead. + */ + @Deprecated + protected final String _name; + + /* + /********************************************************** + /* Construction + /********************************************************** + */ + + protected SimpleBeanPropertyDefinition(AnnotatedMember member, PropertyName fullName, + AnnotationIntrospector intr, PropertyMetadata metadata, + JsonInclude.Include inclusion) + { + this(member, fullName, intr, metadata, + ((inclusion == null) || (inclusion == JsonInclude.Include.USE_DEFAULTS) + ? EMPTY_INCLUDE : JsonInclude.Value.construct(inclusion, null))); + } + + protected SimpleBeanPropertyDefinition(AnnotatedMember member, PropertyName fullName, + AnnotationIntrospector intr, PropertyMetadata metadata, + JsonInclude.Value inclusion) + { + _introspector = intr; + _member = member; + _fullName = fullName; + _name = fullName.getSimpleName(); + _metadata = (metadata == null) ? PropertyMetadata.STD_OPTIONAL: metadata; + _inclusion = inclusion; + } + + /** + * @deprecated Since 2.5 Use variant that takes PropertyName + */ + @Deprecated + protected SimpleBeanPropertyDefinition(AnnotatedMember member, String name, + AnnotationIntrospector intr) { + this(member, new PropertyName(name), intr, null, EMPTY_INCLUDE); + } + + /** + * @since 2.2 + */ + public static SimpleBeanPropertyDefinition construct(MapperConfig config, + AnnotatedMember member) { + return new SimpleBeanPropertyDefinition(member, PropertyName.construct(member.getName()), + (config == null) ? null : config.getAnnotationIntrospector(), + null, EMPTY_INCLUDE); + } + + /** + * @deprecated Since 2.5 + */ + @Deprecated + public static SimpleBeanPropertyDefinition construct(MapperConfig config, + AnnotatedMember member, String name) { + return new SimpleBeanPropertyDefinition(member, PropertyName.construct(name), + (config == null) ? null : config.getAnnotationIntrospector(), + null, EMPTY_INCLUDE); + } + + /** + * @since 2.5 + */ + public static SimpleBeanPropertyDefinition construct(MapperConfig config, + AnnotatedMember member, PropertyName name) { + return construct(config, member, name, null, EMPTY_INCLUDE); + } + + /** + * @since 2.5 + */ + public static SimpleBeanPropertyDefinition construct(MapperConfig config, + AnnotatedMember member, PropertyName name, PropertyMetadata metadata, + JsonInclude.Include inclusion) { + return new SimpleBeanPropertyDefinition(member, name, + (config == null) ? null : config.getAnnotationIntrospector(), + metadata, inclusion); + } + + /** + * @since 2.7 + */ + public static SimpleBeanPropertyDefinition construct(MapperConfig config, + AnnotatedMember member, PropertyName name, PropertyMetadata metadata, + JsonInclude.Value inclusion) { + return new SimpleBeanPropertyDefinition(member, name, + (config == null) ? null : config.getAnnotationIntrospector(), + metadata, inclusion); + } + + /* + /********************************************************** + /* Fluent factories + /********************************************************** + */ + + // Note: removed from base class in 2.6; left here until 2.7 + @Deprecated // since 2.3 (remove in 2.7) + public BeanPropertyDefinition withName(String newName) { + return withSimpleName(newName); + } + + @Override + public BeanPropertyDefinition withSimpleName(String newName) { + if (_fullName.hasSimpleName(newName) && !_fullName.hasNamespace()) { + return this; + } + return new SimpleBeanPropertyDefinition(_member, new PropertyName(newName), + _introspector, _metadata, _inclusion); + } + + @Override + public BeanPropertyDefinition withName(PropertyName newName) { + if (_fullName.equals(newName)) { + return this; + } + return new SimpleBeanPropertyDefinition(_member, newName, + _introspector, _metadata, _inclusion); + } + + /** + * @since 2.5 + */ + public BeanPropertyDefinition withMetadata(PropertyMetadata metadata) { + if (metadata.equals(_metadata)) { + return this; + } + return new SimpleBeanPropertyDefinition(_member, _fullName, + _introspector, metadata, _inclusion); + } + + /** + * @since 2.5 + */ + public BeanPropertyDefinition withInclusion(JsonInclude.Value inclusion) { + if (_inclusion == inclusion) { + return this; + } + return new SimpleBeanPropertyDefinition(_member, _fullName, + _introspector, _metadata, inclusion); + } + + /* + /********************************************************** + /* Basic property information, name, type + /********************************************************** + */ + + @Override + public String getName() { return _fullName.getSimpleName(); } + + @Override + public PropertyName getFullName() { return _fullName; } + + @Override + public boolean hasName(PropertyName name) { + return _fullName.equals(name); + } + + @Override + public String getInternalName() { return getName(); } + + @Override + public PropertyName getWrapperName() { + return ((_introspector == null) && (_member != null)) + ? null : _introspector.findWrapperName(_member); + } + + // hmmh. what should we claim here? + + @Override public boolean isExplicitlyIncluded() { return false; } + @Override public boolean isExplicitlyNamed() { return false; } + + /** + * We will indicate that property is optional, since there is nothing + * to indicate whether it might be required. + */ + @Override + public PropertyMetadata getMetadata() { + return _metadata; + } + + @Override + public JsonInclude.Value findInclusion() { + return _inclusion; + } + + /* + /********************************************************** + /* Access to accessors (fields, methods etc) + /********************************************************** + */ + + @Override + public boolean hasGetter() { return (getGetter() != null); } + + @Override + public boolean hasSetter() { return (getSetter() != null); } + + @Override + public boolean hasField() { return (_member instanceof AnnotatedField); } + + @Override + public boolean hasConstructorParameter() { return (_member instanceof AnnotatedParameter); } + + @Override + public AnnotatedMethod getGetter() { + if ((_member instanceof AnnotatedMethod) + && ((AnnotatedMethod) _member).getParameterCount() == 0) { + return (AnnotatedMethod) _member; + } + return null; + } + + @Override + public AnnotatedMethod getSetter() { + if ((_member instanceof AnnotatedMethod) + && ((AnnotatedMethod) _member).getParameterCount() == 1) { + return (AnnotatedMethod) _member; + } + return null; + } + + @Override + public AnnotatedField getField() { + return (_member instanceof AnnotatedField) ? (AnnotatedField) _member : null; + } + + @Override + public AnnotatedParameter getConstructorParameter() { + return (_member instanceof AnnotatedParameter) ? (AnnotatedParameter) _member : null; + } + + @Override + public Iterator getConstructorParameters() { + AnnotatedParameter param = getConstructorParameter(); + if (param == null) { + return ClassUtil.emptyIterator(); + } + return Collections.singleton(param).iterator(); + } + + /** + * Method used to find accessor (getter, field to access) to use for accessing + * value of the property. + * Null if no such member exists. + */ + @Override + public AnnotatedMember getAccessor() { + AnnotatedMember acc = getGetter(); + if (acc == null) { + acc = getField(); + } + return acc; + } + + /** + * Method used to find mutator (constructor parameter, setter, field) to use for + * changing value of the property. + * Null if no such member exists. + */ + @Override + public AnnotatedMember getMutator() { + AnnotatedMember acc = getConstructorParameter(); + if (acc == null) { + acc = getSetter(); + if (acc == null) { + acc = getField(); + } + } + return acc; + } + + @Override + public AnnotatedMember getNonConstructorMutator() { + AnnotatedMember acc = getSetter(); + if (acc == null) { + acc = getField(); + } + return acc; + } + + @Override + public AnnotatedMember getPrimaryMember() { return _member; } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/StdConverter.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/StdConverter.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/StdConverter.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,43 @@ +package com.fasterxml.jackson.databind.util; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Standard implementation of {@link Converter} that supports explicit + * type access, instead of relying type detection of generic type + * parameters. + * + * @since 2.2 + */ +public abstract class StdConverter + implements Converter +{ + /* + /********************************************************** + /* Partial Converter API implementation + /********************************************************** + */ + + @Override + public abstract OUT convert(IN value); + + @Override + public JavaType getInputType(TypeFactory typeFactory) { + return _findConverterType(typeFactory).containedType(0); + } + + @Override + public JavaType getOutputType(TypeFactory typeFactory) { + return _findConverterType(typeFactory).containedType(1); + } + + protected JavaType _findConverterType(TypeFactory tf) { + JavaType thisType = tf.constructType(getClass()); + JavaType convType = thisType.findSuperType(Converter.class); + if (convType == null || convType.containedTypeCount() < 2) { + throw new IllegalStateException("Can not find OUT type parameter for Converter of type "+getClass().getName()); + } + return convType; + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/StdDateFormat.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/StdDateFormat.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/StdDateFormat.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,588 @@ +package com.fasterxml.jackson.databind.util; + +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.*; + +import com.fasterxml.jackson.core.io.NumberInput; + +/** + * Default {@link DateFormat} implementation used by standard Date + * serializers and deserializers. For serialization defaults to using + * an ISO-8601 compliant format (format String "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + * and for deserialization, both ISO-8601 and RFC-1123. + */ +@SuppressWarnings("serial") +public class StdDateFormat + extends DateFormat +{ + /* TODO !!! 24-Nov-2009, tatu: Should rewrite this class: + * JDK date parsing is awfully brittle, and ISO-8601 is quite + * permissive. The two don't mix, need to write a better one. + */ + // 02-Oct-2014, tatu: Alas. While spit'n'polished a few times, still + // not really robust. But still in use. + + /** + * Defines a commonly used date format that conforms + * to ISO-8601 date formatting standard, when it includes basic undecorated + * timezone definition + */ + public final static String DATE_FORMAT_STR_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + + /** + * Same as 'regular' 8601, but handles 'Z' as an alias for "+0000" + * (or "UTC") + */ + protected final static String DATE_FORMAT_STR_ISO8601_Z = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + + /** + * ISO-8601 with just the Date part, no time + */ + protected final static String DATE_FORMAT_STR_PLAIN = "yyyy-MM-dd"; + + /** + * This constant defines the date format specified by + * RFC 1123 / RFC 822. + */ + protected final static String DATE_FORMAT_STR_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; + + /** + * For error messages we'll also need a list of all formats. + */ + protected final static String[] ALL_FORMATS = new String[] { + DATE_FORMAT_STR_ISO8601, + DATE_FORMAT_STR_ISO8601_Z, + DATE_FORMAT_STR_RFC1123, + DATE_FORMAT_STR_PLAIN + }; + + /** + * By default we use UTC for everything, with Jackson 2.7 and later + * (2.6 and earlier relied on GMT) + */ + private final static TimeZone DEFAULT_TIMEZONE; + static { + DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC"); // since 2.7 + } + + private final static Locale DEFAULT_LOCALE = Locale.US; + + protected final static DateFormat DATE_FORMAT_RFC1123; + + protected final static DateFormat DATE_FORMAT_ISO8601; + protected final static DateFormat DATE_FORMAT_ISO8601_Z; + + protected final static DateFormat DATE_FORMAT_PLAIN; + + /* Let's construct "blueprint" date format instances: can not be used + * as is, due to thread-safety issues, but can be used for constructing + * actual instances more cheaply (avoids re-parsing). + */ + static { + /* Another important thing: let's force use of default timezone for + * baseline DataFormat objects + */ + + DATE_FORMAT_RFC1123 = new SimpleDateFormat(DATE_FORMAT_STR_RFC1123, DEFAULT_LOCALE); + DATE_FORMAT_RFC1123.setTimeZone(DEFAULT_TIMEZONE); + DATE_FORMAT_ISO8601 = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601, DEFAULT_LOCALE); + DATE_FORMAT_ISO8601.setTimeZone(DEFAULT_TIMEZONE); + DATE_FORMAT_ISO8601_Z = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601_Z, DEFAULT_LOCALE); + DATE_FORMAT_ISO8601_Z.setTimeZone(DEFAULT_TIMEZONE); + DATE_FORMAT_PLAIN = new SimpleDateFormat(DATE_FORMAT_STR_PLAIN, DEFAULT_LOCALE); + DATE_FORMAT_PLAIN.setTimeZone(DEFAULT_TIMEZONE); + } + + /** + * A singleton instance can be used for cloning purposes, as a blueprint of sorts. + */ + public final static StdDateFormat instance = new StdDateFormat(); + + /** + * Caller may want to explicitly override timezone to use; if so, + * we will have non-null value here. + */ + protected transient TimeZone _timezone; + + protected final Locale _locale; + + /** + * Explicit override for leniency, if specified. + *

+ * Can not be `final` because {@link #setLenient(boolean)} returns + * `void`. + * + * @since 2.7 + */ + protected Boolean _lenient; + + protected transient DateFormat _formatRFC1123; + protected transient DateFormat _formatISO8601; + protected transient DateFormat _formatISO8601_z; + protected transient DateFormat _formatPlain; + + /* + /********************************************************** + /* Life cycle, accessing singleton "standard" formats + /********************************************************** + */ + + public StdDateFormat() { + _locale = DEFAULT_LOCALE; + } + + @Deprecated // since 2.7 + public StdDateFormat(TimeZone tz, Locale loc) { + _timezone = tz; + _locale = loc; + } + + protected StdDateFormat(TimeZone tz, Locale loc, Boolean lenient) { + _timezone = tz; + _locale = loc; + _lenient = lenient; + } + + public static TimeZone getDefaultTimeZone() { + return DEFAULT_TIMEZONE; + } + + /** + * Method used for creating a new instance with specified timezone; + * if no timezone specified, defaults to the default timezone (UTC). + */ + public StdDateFormat withTimeZone(TimeZone tz) { + if (tz == null) { + tz = DEFAULT_TIMEZONE; + } + if ((tz == _timezone) || tz.equals(_timezone)) { + return this; + } + return new StdDateFormat(tz, _locale, _lenient); + } + + public StdDateFormat withLocale(Locale loc) { + if (loc.equals(_locale)) { + return this; + } + return new StdDateFormat(_timezone, loc, _lenient); + } + + @Override + public StdDateFormat clone() { + /* Although there is that much state to share, we do need to + * orchestrate a bit, mostly since timezones may be changed + */ + return new StdDateFormat(_timezone, _locale, _lenient); + } + + /** + * @deprecated Since 2.4; use variant that takes Locale + */ + @Deprecated + public static DateFormat getISO8601Format(TimeZone tz) { + return getISO8601Format(tz, DEFAULT_LOCALE); + } + + /** + * Method for getting a non-shared DateFormat instance + * that uses specified timezone and can handle simple ISO-8601 + * compliant date format. + * + * @since 2.4 + */ + public static DateFormat getISO8601Format(TimeZone tz, Locale loc) { + return _cloneFormat(DATE_FORMAT_ISO8601, DATE_FORMAT_STR_ISO8601, tz, loc, null); + } + + /** + * Method for getting a non-shared DateFormat instance + * that uses specific timezone and can handle RFC-1123 + * compliant date format. + * + * @since 2.4 + */ + public static DateFormat getRFC1123Format(TimeZone tz, Locale loc) { + return _cloneFormat(DATE_FORMAT_RFC1123, DATE_FORMAT_STR_RFC1123, + tz, loc, null); + } + + /** + * @deprecated Since 2.4; use variant that takes Locale + */ + @Deprecated + public static DateFormat getRFC1123Format(TimeZone tz) { + return getRFC1123Format(tz, DEFAULT_LOCALE); + } + + /* + /********************************************************** + /* Public API, configuration + /********************************************************** + */ + + @Override // since 2.6 + public TimeZone getTimeZone() { + return _timezone; + } + + @Override + public void setTimeZone(TimeZone tz) + { + /* DateFormats are timezone-specific (via Calendar contained), + * so need to reset instances if timezone changes: + */ + if (!tz.equals(_timezone)) { + _clearFormats(); + _timezone = tz; + } + } + + /** + * Need to override since we need to keep track of leniency locally, + * and not via underlying {@link Calendar} instance like base class + * does. + */ + @Override // since 2.7 + public void setLenient(boolean enabled) { + Boolean newValue = enabled; + if (_lenient != newValue) { + _lenient = newValue; + // and since leniency settings may have been used: + _clearFormats(); + } + } + + @Override // since 2.7 + public boolean isLenient() { + if (_lenient == null) { + // default is, I believe, true + return true; + } + return _lenient.booleanValue(); + } + + /* + /********************************************************** + /* Public API, parsing + /********************************************************** + */ + + @Override + public Date parse(String dateStr) throws ParseException + { + dateStr = dateStr.trim(); + ParsePosition pos = new ParsePosition(0); + + Date dt; + + if (looksLikeISO8601(dateStr)) { // also includes "plain" + dt = parseAsISO8601(dateStr, pos, true); + } else { + // Also consider "stringified" simple time stamp + int i = dateStr.length(); + while (--i >= 0) { + char ch = dateStr.charAt(i); + if (ch < '0' || ch > '9') { + // 07-Aug-2013, tatu: And [databind#267] points out that negative numbers should also work + if (i > 0 || ch != '-') { + break; + } + } + } + if ((i < 0) + // let's just assume negative numbers are fine (can't be RFC-1123 anyway); check length for positive + && (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false))) { + dt = new Date(Long.parseLong(dateStr)); + } else { + // Otherwise, fall back to using RFC 1123 + dt = parseAsRFC1123(dateStr, pos); + } + } + if (dt != null) { + return dt; + } + + StringBuilder sb = new StringBuilder(); + for (String f : ALL_FORMATS) { + if (sb.length() > 0) { + sb.append("\", \""); + } else { + sb.append('"'); + } + sb.append(f); + } + sb.append('"'); + throw new ParseException + (String.format("Can not parse date \"%s\": not compatible with any of standard forms (%s)", + dateStr, sb.toString()), pos.getErrorIndex()); + } + + @Override + public Date parse(String dateStr, ParsePosition pos) + { + if (looksLikeISO8601(dateStr)) { // also includes "plain" + try { + return parseAsISO8601(dateStr, pos, false); + } catch (ParseException e) { // will NOT be thrown due to false but is declared... + return null; + } + } + // Also consider "stringified" simple time stamp + int i = dateStr.length(); + while (--i >= 0) { + char ch = dateStr.charAt(i); + if (ch < '0' || ch > '9') { + // 07-Aug-2013, tatu: And [databind#267] points out that negative numbers should also work + if (i > 0 || ch != '-') { + break; + } + } + } + if (i < 0) { // all digits + // let's just assume negative numbers are fine (can't be RFC-1123 anyway); check length for positive + if (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false)) { + return new Date(Long.parseLong(dateStr)); + } + } + // Otherwise, fall back to using RFC 1123 + return parseAsRFC1123(dateStr, pos); + } + + /* + /********************************************************** + /* Public API, writing + /********************************************************** + */ + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, + FieldPosition fieldPosition) + { + if (_formatISO8601 == null) { + _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601, DATE_FORMAT_STR_ISO8601, + _timezone, _locale, _lenient); + } + return _formatISO8601.format(date, toAppendTo, fieldPosition); + } + + /* + /********************************************************** + /* Std overrides + /********************************************************** + */ + + @Override + public String toString() { + String str = "DateFormat "+getClass().getName(); + TimeZone tz = _timezone; + if (tz != null) { + str += " (timezone: "+tz+")"; + } + str += "(locale: "+_locale+")"; + return str; + } + + @Override // since 2.7[.2], as per [databind#1130] + public boolean equals(Object o) { + return (o == this); + } + + @Override // since 2.7[.2], as per [databind#1130] + public int hashCode() { + return System.identityHashCode(this); + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + /** + * Overridable helper method used to figure out which of supported + * formats is the likeliest match. + */ + protected boolean looksLikeISO8601(String dateStr) + { + if (dateStr.length() >= 5 + && Character.isDigit(dateStr.charAt(0)) + && Character.isDigit(dateStr.charAt(3)) + && dateStr.charAt(4) == '-' + ) { + return true; + } + return false; + } + + protected Date parseAsISO8601(String dateStr, ParsePosition pos, boolean throwErrors) + throws ParseException + { + /* 21-May-2009, tatu: DateFormat has very strict handling of + * timezone modifiers for ISO-8601. So we need to do some scrubbing. + */ + + /* First: do we have "zulu" format ('Z' == "UTC")? If yes, that's + * quite simple because we already set date format timezone to be + * UTC, and hence can just strip out 'Z' altogether + */ + int len = dateStr.length(); + char c = dateStr.charAt(len-1); + DateFormat df; + String formatStr; + + // Need to support "plain" date... + if (len <= 10 && Character.isDigit(c)) { + df = _formatPlain; + formatStr = DATE_FORMAT_STR_PLAIN; + if (df == null) { + df = _formatPlain = _cloneFormat(DATE_FORMAT_PLAIN, formatStr, + _timezone, _locale, _lenient); + } + } else if (c == 'Z') { + df = _formatISO8601_z; + formatStr = DATE_FORMAT_STR_ISO8601_Z; + if (df == null) { + df = _formatISO8601_z = _cloneFormat(DATE_FORMAT_ISO8601_Z, formatStr, + _timezone, _locale, _lenient); + } + // may be missing milliseconds... if so, add + if (dateStr.charAt(len-4) == ':') { + StringBuilder sb = new StringBuilder(dateStr); + sb.insert(len-1, ".000"); + dateStr = sb.toString(); + } + } else { + // Let's see if we have timezone indicator or not... + if (hasTimeZone(dateStr)) { + c = dateStr.charAt(len-3); + if (c == ':') { // remove optional colon + // remove colon + StringBuilder sb = new StringBuilder(dateStr); + sb.delete(len-3, len-2); + dateStr = sb.toString(); + } else if (c == '+' || c == '-') { // missing minutes + // let's just append '00' + dateStr += "00"; + } + // Milliseconds partial or missing; and even seconds are optional + len = dateStr.length(); + // remove 'T', '+'/'-' and 4-digit timezone-offset + int timeLen = len - dateStr.lastIndexOf('T') - 6; + if (timeLen < 12) { // 8 for hh:mm:ss, 4 for .sss + int offset = len - 5; // insertion offset, before tz-offset + StringBuilder sb = new StringBuilder(dateStr); + switch (timeLen) { + case 11: + sb.insert(offset, '0'); break; + case 10: + sb.insert(offset, "00"); break; + case 9: // is this legal? (just second fraction marker) + sb.insert(offset, "000"); break; + case 8: + sb.insert(offset, ".000"); break; + case 7: // not legal to have single-digit second + break; + case 6: // probably not legal, but let's allow + sb.insert(offset, "00.000"); + case 5: // is legal to omit seconds + sb.insert(offset, ":00.000"); + } + dateStr = sb.toString(); + } + df = _formatISO8601; + formatStr = DATE_FORMAT_STR_ISO8601; + if (_formatISO8601 == null) { + df = _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601, formatStr, + _timezone, _locale, _lenient); + } + } else { + // If not, plain date. Easiest to just patch 'Z' in the end? + StringBuilder sb = new StringBuilder(dateStr); + // And possible also millisecond part if missing + int timeLen = len - dateStr.lastIndexOf('T') - 1; + if (timeLen < 12) { // missing, or partial + switch (timeLen) { + case 11: sb.append('0'); + case 10: sb.append('0'); + case 9: sb.append('0'); + break; + default: + sb.append(".000"); + } + } + sb.append('Z'); + dateStr = sb.toString(); + df = _formatISO8601_z; + formatStr = DATE_FORMAT_STR_ISO8601_Z; + if (df == null) { + df = _formatISO8601_z = _cloneFormat(DATE_FORMAT_ISO8601_Z, formatStr, + _timezone, _locale, _lenient); + } + } + } + Date dt = df.parse(dateStr, pos); + // 22-Dec-2015, tatu: With non-lenient, may get null + if (dt == null) { + throw new ParseException + (String.format("Can not parse date \"%s\": while it seems to fit format '%s', parsing fails (leniency? %s)", + dateStr, formatStr, _lenient), + pos.getErrorIndex()); + } + return dt; + } + + protected Date parseAsRFC1123(String dateStr, ParsePosition pos) + { + if (_formatRFC1123 == null) { + _formatRFC1123 = _cloneFormat(DATE_FORMAT_RFC1123, DATE_FORMAT_STR_RFC1123, + _timezone, _locale, _lenient); + } + return _formatRFC1123.parse(dateStr, pos); + } + + private final static boolean hasTimeZone(String str) + { + // Only accept "+hh", "+hhmm" and "+hh:mm" (and with minus), so + int len = str.length(); + if (len >= 6) { + char c = str.charAt(len-6); + if (c == '+' || c == '-') return true; + c = str.charAt(len-5); + if (c == '+' || c == '-') return true; + c = str.charAt(len-3); + if (c == '+' || c == '-') return true; + } + return false; + } + + private final static DateFormat _cloneFormat(DateFormat df, String format, + TimeZone tz, Locale loc, Boolean lenient) + { + if (!loc.equals(DEFAULT_LOCALE)) { + df = new SimpleDateFormat(format, loc); + df.setTimeZone((tz == null) ? DEFAULT_TIMEZONE : tz); + } else { + df = (DateFormat) df.clone(); + if (tz != null) { + df.setTimeZone(tz); + } + } + if (lenient != null) { + df.setLenient(lenient.booleanValue()); + } + return df; + } + + protected void _clearFormats() { + _formatRFC1123 = null; + _formatISO8601 = null; + _formatISO8601_z = null; + _formatPlain = null; + } +} + Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/TokenBuffer.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/TokenBuffer.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/TokenBuffer.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,1891 @@ +package com.fasterxml.jackson.databind.util; + +import java.io.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.TreeMap; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.base.ParserMinimalBase; +import com.fasterxml.jackson.core.json.JsonReadContext; +import com.fasterxml.jackson.core.json.JsonWriteContext; +import com.fasterxml.jackson.core.util.ByteArrayBuilder; +import com.fasterxml.jackson.databind.*; + +/** + * Utility class used for efficient storage of {@link JsonToken} + * sequences, needed for temporary buffering. + * Space efficient for different sequence lengths (especially so for smaller + * ones; but not significantly less efficient for larger), highly efficient + * for linear iteration and appending. Implemented as segmented/chunked + * linked list of tokens; only modifications are via appends. + *

+ * Note that before version 2.0, this class was located in the "core" + * bundle, not data-binding; but since it was only used by data binding, + * was moved here to reduce size of core package + */ +public class TokenBuffer +/* Won't use JsonGeneratorBase, to minimize overhead for validity + * checking + */ + extends JsonGenerator +{ + protected final static int DEFAULT_GENERATOR_FEATURES = JsonGenerator.Feature.collectDefaults(); + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + /** + * Object codec to use for stream-based object + * conversion through parser/generator interfaces. If null, + * such methods can not be used. + */ + protected ObjectCodec _objectCodec; + + /** + * Bit flag composed of bits that indicate which + * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s + * are enabled. + *

+ * NOTE: most features have no effect on this class + */ + protected int _generatorFeatures; + + protected boolean _closed; + + /** + * @since 2.3 + */ + protected boolean _hasNativeTypeIds; + + /** + * @since 2.3 + */ + protected boolean _hasNativeObjectIds; + + /** + * @since 2.3 + */ + protected boolean _mayHaveNativeIds; + + /** + * Flag set during construction, if use of {@link BigDecimal} is to be forced + * on all floating-point values. + * + * @since 2.7 + */ + protected boolean _forceBigDecimal; + + /* + /********************************************************** + /* Token buffering state + /********************************************************** + */ + + /** + * First segment, for contents this buffer has + */ + protected Segment _first; + + /** + * Last segment of this buffer, one that is used + * for appending more tokens + */ + protected Segment _last; + + /** + * Offset within last segment, + */ + protected int _appendAt; + + /** + * If native type ids supported, this is the id for following + * value (or first token of one) to be written. + */ + protected Object _typeId; + + /** + * If native object ids supported, this is the id for following + * value (or first token of one) to be written. + */ + protected Object _objectId; + + /** + * Do we currently have a native type or object id buffered? + */ + protected boolean _hasNativeId = false; + + /* + /********************************************************** + /* Output state + /********************************************************** + */ + + protected JsonWriteContext _writeContext; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + /** + * @param codec Object codec to use for stream-based object + * conversion through parser/generator interfaces. If null, + * such methods can not be used. + * + * @deprecated since 2.3 preferred variant is one that takes {@link JsonParser} or additional boolean parameter. + */ + @Deprecated + public TokenBuffer(ObjectCodec codec) { + this(codec, false); + } + + /** + * @param codec Object codec to use for stream-based object + * conversion through parser/generator interfaces. If null, + * such methods can not be used. + * @param hasNativeIds Whether resulting {@link JsonParser} (if created) + * is considered to support native type and object ids + */ + public TokenBuffer(ObjectCodec codec, boolean hasNativeIds) + { + _objectCodec = codec; + _generatorFeatures = DEFAULT_GENERATOR_FEATURES; + _writeContext = JsonWriteContext.createRootContext(null); + // at first we have just one segment + _first = _last = new Segment(); + _appendAt = 0; + _hasNativeTypeIds = hasNativeIds; + _hasNativeObjectIds = hasNativeIds; + + _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; + } + + /** + * @since 2.3 + */ + public TokenBuffer(JsonParser p) { + this(p, null); + } + + /** + * @since 2.7 + */ + public TokenBuffer(JsonParser p, DeserializationContext ctxt) + { + _objectCodec = p.getCodec(); + _generatorFeatures = DEFAULT_GENERATOR_FEATURES; + _writeContext = JsonWriteContext.createRootContext(null); + // at first we have just one segment + _first = _last = new Segment(); + _appendAt = 0; + _hasNativeTypeIds = p.canReadTypeId(); + _hasNativeObjectIds = p.canReadObjectId(); + _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; + _forceBigDecimal = (ctxt == null) ? false + : ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); + } + + /** + * @since 2.7 + */ + public TokenBuffer forceUseOfBigDecimal(boolean b) { + _forceBigDecimal = b; + return this; + } + + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /** + * Method used to create a {@link JsonParser} that can read contents + * stored in this buffer. Will use default _objectCodec for + * object conversions. + *

+ * Note: instances are not synchronized, that is, they are not thread-safe + * if there are concurrent appends to the underlying buffer. + * + * @return Parser that can be used for reading contents stored in this buffer + */ + public JsonParser asParser() + { + return asParser(_objectCodec); + } + + /** + * Method used to create a {@link JsonParser} that can read contents + * stored in this buffer. + *

+ * Note: instances are not synchronized, that is, they are not thread-safe + * if there are concurrent appends to the underlying buffer. + * + * @param codec Object codec to use for stream-based object + * conversion through parser/generator interfaces. If null, + * such methods can not be used. + * + * @return Parser that can be used for reading contents stored in this buffer + */ + public JsonParser asParser(ObjectCodec codec) + { + return new Parser(_first, codec, _hasNativeTypeIds, _hasNativeObjectIds); + } + + /** + * @param src Parser to use for accessing source information + * like location, configured codec + */ + public JsonParser asParser(JsonParser src) + { + Parser p = new Parser(_first, src.getCodec(), _hasNativeTypeIds, _hasNativeObjectIds); + p.setLocation(src.getTokenLocation()); + return p; + } + + /* + /********************************************************** + /* Additional accessors + /********************************************************** + */ + + public JsonToken firstToken() { + if (_first != null) { + return _first.type(0); + } + return null; + } + + /* + /********************************************************** + /* Other custom methods not needed for implementing interfaces + /********************************************************** + */ + + /** + * Helper method that will append contents of given buffer into this + * buffer. + * Not particularly optimized; can be made faster if there is need. + * + * @return This buffer + */ + @SuppressWarnings("resource") + public TokenBuffer append(TokenBuffer other) throws IOException + { + // Important? If source has native ids, need to store + if (!_hasNativeTypeIds) { + _hasNativeTypeIds = other.canWriteTypeId(); + } + if (!_hasNativeObjectIds) { + _hasNativeObjectIds = other.canWriteObjectId(); + } + _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; + + JsonParser p = other.asParser(); + while (p.nextToken() != null) { + copyCurrentStructure(p); + } + return this; + } + + /** + * Helper method that will write all contents of this buffer + * using given {@link JsonGenerator}. + *

+ * Note: this method would be enough to implement + * JsonSerializer for TokenBuffer type; + * but we can not have upwards + * references (from core to mapper package); and as such we also + * can not take second argument. + */ + public void serialize(JsonGenerator gen) throws IOException + { + Segment segment = _first; + int ptr = -1; + + final boolean checkIds = _mayHaveNativeIds; + boolean hasIds = checkIds && (segment.hasIds()); + + while (true) { + if (++ptr >= Segment.TOKENS_PER_SEGMENT) { + ptr = 0; + segment = segment.next(); + if (segment == null) break; + hasIds = checkIds && (segment.hasIds()); + } + JsonToken t = segment.type(ptr); + if (t == null) break; + + if (hasIds) { + Object id = segment.findObjectId(ptr); + if (id != null) { + gen.writeObjectId(id); + } + id = segment.findTypeId(ptr); + if (id != null) { + gen.writeTypeId(id); + } + } + + // Note: copied from 'copyCurrentEvent'... + switch (t) { + case START_OBJECT: + gen.writeStartObject(); + break; + case END_OBJECT: + gen.writeEndObject(); + break; + case START_ARRAY: + gen.writeStartArray(); + break; + case END_ARRAY: + gen.writeEndArray(); + break; + case FIELD_NAME: + { + // 13-Dec-2010, tatu: Maybe we should start using different type tokens to reduce casting? + Object ob = segment.get(ptr); + if (ob instanceof SerializableString) { + gen.writeFieldName((SerializableString) ob); + } else { + gen.writeFieldName((String) ob); + } + } + break; + case VALUE_STRING: + { + Object ob = segment.get(ptr); + if (ob instanceof SerializableString) { + gen.writeString((SerializableString) ob); + } else { + gen.writeString((String) ob); + } + } + break; + case VALUE_NUMBER_INT: + { + Object n = segment.get(ptr); + if (n instanceof Integer) { + gen.writeNumber((Integer) n); + } else if (n instanceof BigInteger) { + gen.writeNumber((BigInteger) n); + } else if (n instanceof Long) { + gen.writeNumber((Long) n); + } else if (n instanceof Short) { + gen.writeNumber((Short) n); + } else { + gen.writeNumber(((Number) n).intValue()); + } + } + break; + case VALUE_NUMBER_FLOAT: + { + Object n = segment.get(ptr); + if (n instanceof Double) { + gen.writeNumber(((Double) n).doubleValue()); + } else if (n instanceof BigDecimal) { + gen.writeNumber((BigDecimal) n); + } else if (n instanceof Float) { + gen.writeNumber(((Float) n).floatValue()); + } else if (n == null) { + gen.writeNull(); + } else if (n instanceof String) { + gen.writeNumber((String) n); + } else { + throw new JsonGenerationException(String.format( + "Unrecognized value type for VALUE_NUMBER_FLOAT: %s, can not serialize", + n.getClass().getName()), gen); + } + } + break; + case VALUE_TRUE: + gen.writeBoolean(true); + break; + case VALUE_FALSE: + gen.writeBoolean(false); + break; + case VALUE_NULL: + gen.writeNull(); + break; + case VALUE_EMBEDDED_OBJECT: + { + Object value = segment.get(ptr); + if (value instanceof RawValue) { + ((RawValue) value).serialize(gen); + } else { + gen.writeObject(value); + } + } + break; + default: + throw new RuntimeException("Internal error: should never end up through this code path"); + } + } + } + + /** + * Helper method used by standard deserializer. + * + * @since 2.3 + */ + public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + if (p.getCurrentTokenId() != JsonToken.FIELD_NAME.id()) { + copyCurrentStructure(p); + return this; + } + /* 28-Oct-2014, tatu: As per [databind#592], need to support a special case of starting from + * FIELD_NAME, which is taken to mean that we are missing START_OBJECT, but need + * to assume one did exist. + */ + JsonToken t; + writeStartObject(); + do { + copyCurrentStructure(p); + } while ((t = p.nextToken()) == JsonToken.FIELD_NAME); + if (t != JsonToken.END_OBJECT) { + throw ctxt.mappingException("Expected END_OBJECT after copying contents of a JsonParser into TokenBuffer, got "+t); + } + writeEndObject(); + return this; + } + + @Override + @SuppressWarnings("resource") + public String toString() + { + // Let's print up to 100 first tokens... + final int MAX_COUNT = 100; + + StringBuilder sb = new StringBuilder(); + sb.append("[TokenBuffer: "); + + /* +sb.append("NativeTypeIds=").append(_hasNativeTypeIds).append(","); +sb.append("NativeObjectIds=").append(_hasNativeObjectIds).append(","); +*/ + + JsonParser jp = asParser(); + int count = 0; + final boolean hasNativeIds = _hasNativeTypeIds || _hasNativeObjectIds; + + while (true) { + JsonToken t; + try { + t = jp.nextToken(); + if (t == null) break; + + if (hasNativeIds) { + _appendNativeIds(sb); + } + + if (count < MAX_COUNT) { + if (count > 0) { + sb.append(", "); + } + sb.append(t.toString()); + if (t == JsonToken.FIELD_NAME) { + sb.append('('); + sb.append(jp.getCurrentName()); + sb.append(')'); + } + } + } catch (IOException ioe) { // should never occur + throw new IllegalStateException(ioe); + } + ++count; + } + + if (count >= MAX_COUNT) { + sb.append(" ... (truncated ").append(count-MAX_COUNT).append(" entries)"); + } + sb.append(']'); + return sb.toString(); + } + + private final void _appendNativeIds(StringBuilder sb) + { + Object objectId = _last.findObjectId(_appendAt-1); + if (objectId != null) { + sb.append("[objectId=").append(String.valueOf(objectId)).append(']'); + } + Object typeId = _last.findTypeId(_appendAt-1); + if (typeId != null) { + sb.append("[typeId=").append(String.valueOf(typeId)).append(']'); + } + } + + /* + /********************************************************** + /* JsonGenerator implementation: configuration + /********************************************************** + */ + + @Override + public JsonGenerator enable(Feature f) { + _generatorFeatures |= f.getMask(); + return this; + } + + @Override + public JsonGenerator disable(Feature f) { + _generatorFeatures &= ~f.getMask(); + return this; + } + + //public JsonGenerator configure(SerializationFeature f, boolean state) { } + + @Override + public boolean isEnabled(Feature f) { + return (_generatorFeatures & f.getMask()) != 0; + } + + @Override + public int getFeatureMask() { + return _generatorFeatures; + } + + @Override + @Deprecated + public JsonGenerator setFeatureMask(int mask) { + _generatorFeatures = mask; + return this; + } + + @Override + public JsonGenerator overrideStdFeatures(int values, int mask) { + int oldState = getFeatureMask(); + _generatorFeatures = (oldState & ~mask) | (values & mask); + return this; + } + + @Override + public JsonGenerator useDefaultPrettyPrinter() { + // No-op: we don't indent + return this; + } + + @Override + public JsonGenerator setCodec(ObjectCodec oc) { + _objectCodec = oc; + return this; + } + + @Override + public ObjectCodec getCodec() { return _objectCodec; } + + @Override + public final JsonWriteContext getOutputContext() { return _writeContext; } + + /* + /********************************************************** + /* JsonGenerator implementation: capability introspection + /********************************************************** + */ + + /** + * Since we can efficiently store byte[], yes. + */ + @Override + public boolean canWriteBinaryNatively() { + return true; + } + + /* + /********************************************************** + /* JsonGenerator implementation: low-level output handling + /********************************************************** + */ + + @Override + public void flush() throws IOException { /* NOP */ } + + @Override + public void close() throws IOException { + _closed = true; + } + + @Override + public boolean isClosed() { return _closed; } + + /* + /********************************************************** + /* JsonGenerator implementation: write methods, structural + /********************************************************** + */ + + @Override + public final void writeStartArray() throws IOException + { + _append(JsonToken.START_ARRAY); + _writeContext = _writeContext.createChildArrayContext(); + } + + @Override + public final void writeEndArray() throws IOException + { + _append(JsonToken.END_ARRAY); + // Let's allow unbalanced tho... i.e. not run out of root level, ever + JsonWriteContext c = _writeContext.getParent(); + if (c != null) { + _writeContext = c; + } + } + + @Override + public final void writeStartObject() throws IOException + { + _append(JsonToken.START_OBJECT); + _writeContext = _writeContext.createChildObjectContext(); + } + + @Override + public final void writeEndObject() throws IOException + { + _append(JsonToken.END_OBJECT); + // Let's allow unbalanced tho... i.e. not run out of root level, ever + JsonWriteContext c = _writeContext.getParent(); + if (c != null) { + _writeContext = c; + } + } + + @Override + public final void writeFieldName(String name) throws IOException + { + _append(JsonToken.FIELD_NAME, name); + _writeContext.writeFieldName(name); + } + + @Override + public void writeFieldName(SerializableString name) throws IOException + { + _append(JsonToken.FIELD_NAME, name); + _writeContext.writeFieldName(name.getValue()); + } + + /* + /********************************************************** + /* JsonGenerator implementation: write methods, textual + /********************************************************** + */ + + @Override + public void writeString(String text) throws IOException { + if (text == null) { + writeNull(); + } else { + _appendValue(JsonToken.VALUE_STRING, text); + } + } + + @Override + public void writeString(char[] text, int offset, int len) throws IOException { + writeString(new String(text, offset, len)); + } + + @Override + public void writeString(SerializableString text) throws IOException { + if (text == null) { + writeNull(); + } else { + _appendValue(JsonToken.VALUE_STRING, text); + } + } + + @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(); + } + + @Override + public void writeRaw(String text) throws IOException { + _reportUnsupportedOperation(); + } + + @Override + public void writeRaw(String text, int offset, int len) throws IOException { + _reportUnsupportedOperation(); + } + + @Override + public void writeRaw(SerializableString text) throws IOException { + _reportUnsupportedOperation(); + } + + @Override + public void writeRaw(char[] text, int offset, int len) throws IOException { + _reportUnsupportedOperation(); + } + + @Override + public void writeRaw(char c) throws IOException { + _reportUnsupportedOperation(); + } + + @Override + public void writeRawValue(String text) throws IOException { + _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text)); + } + + @Override + public void writeRawValue(String text, int offset, int len) throws IOException { + if (offset > 0 || len != text.length()) { + text = text.substring(offset, offset+len); + } + _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text)); + } + + @Override + public void writeRawValue(char[] text, int offset, int len) throws IOException { + _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new String(text, offset, len)); + } + + /* + /********************************************************** + /* JsonGenerator implementation: write methods, primitive types + /********************************************************** + */ + + @Override + public void writeNumber(short i) throws IOException { + _appendValue(JsonToken.VALUE_NUMBER_INT, Short.valueOf(i)); + } + + @Override + public void writeNumber(int i) throws IOException { + _appendValue(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i)); + } + + @Override + public void writeNumber(long l) throws IOException { + _appendValue(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l)); + } + + @Override + public void writeNumber(double d) throws IOException { + _appendValue(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d)); + } + + @Override + public void writeNumber(float f) throws IOException { + _appendValue(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f)); + } + + @Override + public void writeNumber(BigDecimal dec) throws IOException { + if (dec == null) { + writeNull(); + } else { + _appendValue(JsonToken.VALUE_NUMBER_FLOAT, dec); + } + } + + @Override + public void writeNumber(BigInteger v) throws IOException { + if (v == null) { + writeNull(); + } else { + _appendValue(JsonToken.VALUE_NUMBER_INT, v); + } + } + + @Override + public void writeNumber(String encodedValue) throws IOException { + /* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric + * identity as long as possible + */ + _appendValue(JsonToken.VALUE_NUMBER_FLOAT, encodedValue); + } + + @Override + public void writeBoolean(boolean state) throws IOException { + _appendValue(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE); + } + + @Override + public void writeNull() throws IOException { + _appendValue(JsonToken.VALUE_NULL); + } + + /* + /*********************************************************** + /* JsonGenerator implementation: write methods for POJOs/trees + /*********************************************************** + */ + + @Override + public void writeObject(Object value) throws IOException + { + if (value == null) { + writeNull(); + return; + } + Class raw = value.getClass(); + if (raw == byte[].class || (value instanceof RawValue)) { + _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value); + return; + } + if (_objectCodec == null) { + /* 28-May-2014, tatu: Tricky choice here; if no codec, should we + * err out, or just embed? For now, do latter. + */ +// throw new JsonMappingException("No ObjectCodec configured for TokenBuffer, writeObject() called"); + _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value); + } else { + _objectCodec.writeValue(this, value); + } + } + + @Override + public void writeTree(TreeNode node) throws IOException + { + if (node == null) { + writeNull(); + return; + } + + if (_objectCodec == null) { + // as with 'writeObject()', is codec optional? + _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, node); + } else { + _objectCodec.writeTree(this, node); + } + } + + /* + /*********************************************************** + /* JsonGenerator implementation; binary + /*********************************************************** + */ + + @Override + public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException + { + /* 31-Dec-2009, tatu: can do this using multiple alternatives; but for + * now, let's try to limit number of conversions. + * The only (?) tricky thing is that of whether to preserve variant, + * seems pointless, so let's not worry about it unless there's some + * compelling reason to. + */ + byte[] copy = new byte[len]; + System.arraycopy(data, offset, copy, 0, len); + writeObject(copy); + } + + /** + * Although we could support this method, it does not necessarily make + * sense: we can not make good use of streaming because buffer must + * hold all the data. Because of this, currently this will simply + * throw {@link UnsupportedOperationException} + */ + @Override + public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) { + throw new UnsupportedOperationException(); + } + + /* + /*********************************************************** + /* JsonGenerator implementation: native ids + /*********************************************************** + */ + + @Override + public boolean canWriteTypeId() { + return _hasNativeTypeIds; + } + + @Override + public boolean canWriteObjectId() { + return _hasNativeObjectIds; + } + + @Override + public void writeTypeId(Object id) { + _typeId = id; + _hasNativeId = true; + } + + @Override + public void writeObjectId(Object id) { + _objectId = id; + _hasNativeId = true; + } + + /* + /********************************************************** + /* JsonGenerator implementation; pass-through copy + /********************************************************** + */ + + @Override + public void copyCurrentEvent(JsonParser p) throws IOException + { + if (_mayHaveNativeIds) { + _checkNativeIds(p); + } + switch (p.getCurrentToken()) { + case START_OBJECT: + writeStartObject(); + break; + case END_OBJECT: + writeEndObject(); + break; + case START_ARRAY: + writeStartArray(); + break; + case END_ARRAY: + writeEndArray(); + break; + case FIELD_NAME: + writeFieldName(p.getCurrentName()); + break; + case VALUE_STRING: + if (p.hasTextCharacters()) { + writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength()); + } else { + writeString(p.getText()); + } + break; + case VALUE_NUMBER_INT: + switch (p.getNumberType()) { + case INT: + writeNumber(p.getIntValue()); + break; + case BIG_INTEGER: + writeNumber(p.getBigIntegerValue()); + break; + default: + writeNumber(p.getLongValue()); + } + break; + case VALUE_NUMBER_FLOAT: + if (_forceBigDecimal) { + /* 10-Oct-2015, tatu: Ideally we would first determine whether underlying + * number is already decoded into a number (in which case might as well + * access as number); or is still retained as text (in which case we + * should further defer decoding that may not need BigDecimal): + */ + writeNumber(p.getDecimalValue()); + } else { + switch (p.getNumberType()) { + case BIG_DECIMAL: + writeNumber(p.getDecimalValue()); + break; + case FLOAT: + writeNumber(p.getFloatValue()); + break; + default: + writeNumber(p.getDoubleValue()); + } + } + break; + case VALUE_TRUE: + writeBoolean(true); + break; + case VALUE_FALSE: + writeBoolean(false); + break; + case VALUE_NULL: + writeNull(); + break; + case VALUE_EMBEDDED_OBJECT: + writeObject(p.getEmbeddedObject()); + break; + default: + throw new RuntimeException("Internal error: should never end up through this code path"); + } + } + + @Override + public void copyCurrentStructure(JsonParser jp) throws IOException + { + JsonToken t = jp.getCurrentToken(); + + // Let's handle field-name separately first + if (t == JsonToken.FIELD_NAME) { + if (_mayHaveNativeIds) { + _checkNativeIds(jp); + } + writeFieldName(jp.getCurrentName()); + t = jp.nextToken(); + // fall-through to copy the associated value + } + + if (_mayHaveNativeIds) { + _checkNativeIds(jp); + } + + switch (t) { + case START_ARRAY: + writeStartArray(); + while (jp.nextToken() != JsonToken.END_ARRAY) { + copyCurrentStructure(jp); + } + writeEndArray(); + break; + case START_OBJECT: + writeStartObject(); + while (jp.nextToken() != JsonToken.END_OBJECT) { + copyCurrentStructure(jp); + } + writeEndObject(); + break; + default: // others are simple: + copyCurrentEvent(jp); + } + } + + + private final void _checkNativeIds(JsonParser jp) throws IOException + { + if ((_typeId = jp.getTypeId()) != null) { + _hasNativeId = true; + } + if ((_objectId = jp.getObjectId()) != null) { + _hasNativeId = true; + } + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected final void _append(JsonToken type) + { + Segment next = _hasNativeId + ? _last.append(_appendAt, type, _objectId, _typeId) + : _last.append(_appendAt, type); + if (next == null) { + ++_appendAt; + } else { + _last = next; + _appendAt = 1; // since we added first at 0 + } + } + + protected final void _append(JsonToken type, Object value) + { + Segment next = _hasNativeId + ? _last.append(_appendAt, type, value, _objectId, _typeId) + : _last.append(_appendAt, type, value); + if (next == null) { + ++_appendAt; + } else { + _last = next; + _appendAt = 1; + } + } + + /** + * Similar to {@link #_append(JsonToken)} but also updates context with + * knowledge that a scalar value was written + * + * @since 2.6.4 + */ + protected final void _appendValue(JsonToken type) + { + _writeContext.writeValue(); + Segment next = _hasNativeId + ? _last.append(_appendAt, type, _objectId, _typeId) + : _last.append(_appendAt, type); + if (next == null) { + ++_appendAt; + } else { + _last = next; + _appendAt = 1; // since we added first at 0 + } + } + + /** + * Similar to {@link #_append(JsonToken,Object)} but also updates context with + * knowledge that a scalar value was written + * + * @since 2.6.4 + */ + protected final void _appendValue(JsonToken type, Object value) + { + _writeContext.writeValue(); + Segment next = _hasNativeId + ? _last.append(_appendAt, type, value, _objectId, _typeId) + : _last.append(_appendAt, type, value); + if (next == null) { + ++_appendAt; + } else { + _last = next; + _appendAt = 1; + } + } + + protected final void _appendRaw(int rawType, Object value) + { + Segment next = _hasNativeId + ? _last.appendRaw(_appendAt, rawType, value, _objectId, _typeId) + : _last.appendRaw(_appendAt, rawType, value); + if (next == null) { + ++_appendAt; + } else { + _last = next; + _appendAt = 1; + } + } + + @Override + protected void _reportUnsupportedOperation() { + throw new UnsupportedOperationException("Called operation not supported for TokenBuffer"); + } + + /* + /********************************************************** + /* Supporting classes + /********************************************************** + */ + + protected final static class Parser + extends ParserMinimalBase + { + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected ObjectCodec _codec; + + /** + * @since 2.3 + */ + protected final boolean _hasNativeTypeIds; + + /** + * @since 2.3 + */ + protected final boolean _hasNativeObjectIds; + + protected final boolean _hasNativeIds; + + /* + /********************************************************** + /* Parsing state + /********************************************************** + */ + + /** + * Currently active segment + */ + protected Segment _segment; + + /** + * Pointer to current token within current segment + */ + protected int _segmentPtr; + + /** + * Information about parser context, context in which + * the next token is to be parsed (root, array, object). + */ + protected JsonReadContext _parsingContext; + + protected boolean _closed; + + protected transient ByteArrayBuilder _byteBuilder; + + protected JsonLocation _location = null; + + /* + /********************************************************** + /* Construction, init + /********************************************************** + */ + + public Parser(Segment firstSeg, ObjectCodec codec, + boolean hasNativeTypeIds, + boolean hasNativeObjectIds) + { + super(0); + _segment = firstSeg; + _segmentPtr = -1; // not yet read + _codec = codec; + _parsingContext = JsonReadContext.createRootContext(null); + _hasNativeTypeIds = hasNativeTypeIds; + _hasNativeObjectIds = hasNativeObjectIds; + _hasNativeIds = (hasNativeTypeIds | hasNativeObjectIds); + } + + public void setLocation(JsonLocation l) { + _location = l; + } + + @Override + public ObjectCodec getCodec() { return _codec; } + + @Override + public void setCodec(ObjectCodec c) { _codec = c; } + + @Override + public Version version() { + return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Extended API beyond JsonParser + /********************************************************** + */ + + public JsonToken peekNextToken() throws IOException + { + // closed? nothing more to peek, either + if (_closed) return null; + Segment seg = _segment; + int ptr = _segmentPtr+1; + if (ptr >= Segment.TOKENS_PER_SEGMENT) { + ptr = 0; + seg = (seg == null) ? null : seg.next(); + } + return (seg == null) ? null : seg.type(ptr); + } + + /* + /********************************************************** + /* Closeable implementation + /********************************************************** + */ + + @Override + public void close() throws IOException { + if (!_closed) { + _closed = true; + } + } + + /* + /********************************************************** + /* Public API, traversal + /********************************************************** + */ + + @Override + public JsonToken nextToken() throws IOException + { + // If we are closed, nothing more to do + if (_closed || (_segment == null)) return null; + + // Ok, then: any more tokens? + if (++_segmentPtr >= Segment.TOKENS_PER_SEGMENT) { + _segmentPtr = 0; + _segment = _segment.next(); + if (_segment == null) { + return null; + } + } + _currToken = _segment.type(_segmentPtr); + // Field name? Need to update context + if (_currToken == JsonToken.FIELD_NAME) { + Object ob = _currentObject(); + String name = (ob instanceof String) ? ((String) ob) : ob.toString(); + _parsingContext.setCurrentName(name); + } else if (_currToken == JsonToken.START_OBJECT) { + _parsingContext = _parsingContext.createChildObjectContext(-1, -1); + } else if (_currToken == JsonToken.START_ARRAY) { + _parsingContext = _parsingContext.createChildArrayContext(-1, -1); + } else if (_currToken == JsonToken.END_OBJECT + || _currToken == JsonToken.END_ARRAY) { + // Closing JSON Object/Array? Close matching context + _parsingContext = _parsingContext.getParent(); + // but allow unbalanced cases too (more close markers) + if (_parsingContext == null) { + _parsingContext = JsonReadContext.createRootContext(null); + } + } + return _currToken; + } + + @Override + public String nextFieldName() throws IOException + { + // inlined common case from nextToken() + if (_closed || (_segment == null)) return null; + + int ptr = _segmentPtr+1; + if (ptr < Segment.TOKENS_PER_SEGMENT && _segment.type(ptr) == JsonToken.FIELD_NAME) { + _segmentPtr = ptr; + Object ob = _segment.get(ptr); // inlined _currentObject(); + String name = (ob instanceof String) ? ((String) ob) : ob.toString(); + _parsingContext.setCurrentName(name); + return name; + } + return (nextToken() == JsonToken.FIELD_NAME) ? getCurrentName() : null; + } + + @Override + public boolean isClosed() { return _closed; } + + /* + /********************************************************** + /* Public API, token accessors + /********************************************************** + */ + + @Override + public JsonStreamContext getParsingContext() { return _parsingContext; } + + @Override + public JsonLocation getTokenLocation() { return getCurrentLocation(); } + + @Override + public JsonLocation getCurrentLocation() { + return (_location == null) ? JsonLocation.NA : _location; + } + + @Override + public String getCurrentName() { + // 25-Jun-2015, tatu: as per [databind#838], needs to be same as ParserBase + if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { + JsonReadContext parent = _parsingContext.getParent(); + return parent.getCurrentName(); + } + return _parsingContext.getCurrentName(); + } + + @Override + public void overrideCurrentName(String name) + { + // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing: + JsonReadContext ctxt = _parsingContext; + if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { + ctxt = ctxt.getParent(); + } + try { + ctxt.setCurrentName(name); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /* + /********************************************************** + /* Public API, access to token information, text + /********************************************************** + */ + + @Override + public String getText() + { + // common cases first: + if (_currToken == JsonToken.VALUE_STRING + || _currToken == JsonToken.FIELD_NAME) { + Object ob = _currentObject(); + if (ob instanceof String) { + return (String) ob; + } + return (ob == null) ? null : ob.toString(); + } + if (_currToken == null) { + return null; + } + switch (_currToken) { + case VALUE_NUMBER_INT: + case VALUE_NUMBER_FLOAT: + Object ob = _currentObject(); + return (ob == null) ? null : ob.toString(); + default: + return _currToken.asString(); + } + } + + @Override + public char[] getTextCharacters() { + String str = getText(); + return (str == null) ? null : str.toCharArray(); + } + + @Override + public int getTextLength() { + String str = getText(); + return (str == null) ? 0 : str.length(); + } + + @Override + public int getTextOffset() { return 0; } + + @Override + public boolean hasTextCharacters() { + // We never have raw buffer available, so: + return false; + } + + /* + /********************************************************** + /* Public API, access to token information, numeric + /********************************************************** + */ + + @Override + public BigInteger getBigIntegerValue() throws IOException + { + Number n = getNumberValue(); + if (n instanceof BigInteger) { + return (BigInteger) n; + } + if (getNumberType() == NumberType.BIG_DECIMAL) { + return ((BigDecimal) n).toBigInteger(); + } + // int/long is simple, but let's also just truncate float/double: + return BigInteger.valueOf(n.longValue()); + } + + @Override + public BigDecimal getDecimalValue() throws IOException + { + Number n = getNumberValue(); + if (n instanceof BigDecimal) { + return (BigDecimal) n; + } + switch (getNumberType()) { + case INT: + case LONG: + return BigDecimal.valueOf(n.longValue()); + case BIG_INTEGER: + return new BigDecimal((BigInteger) n); + default: + } + // float or double + return BigDecimal.valueOf(n.doubleValue()); + } + + @Override + public double getDoubleValue() throws IOException { + return getNumberValue().doubleValue(); + } + + @Override + public float getFloatValue() throws IOException { + return getNumberValue().floatValue(); + } + + @Override + public int getIntValue() throws IOException + { + // optimize common case: + if (_currToken == JsonToken.VALUE_NUMBER_INT) { + return ((Number) _currentObject()).intValue(); + } + return getNumberValue().intValue(); + } + + @Override + public long getLongValue() throws IOException { + return getNumberValue().longValue(); + } + + @Override + public NumberType getNumberType() throws IOException + { + Number n = getNumberValue(); + if (n instanceof Integer) return NumberType.INT; + if (n instanceof Long) return NumberType.LONG; + if (n instanceof Double) return NumberType.DOUBLE; + if (n instanceof BigDecimal) return NumberType.BIG_DECIMAL; + if (n instanceof BigInteger) return NumberType.BIG_INTEGER; + if (n instanceof Float) return NumberType.FLOAT; + if (n instanceof Short) return NumberType.INT; // should be SHORT + return null; + } + + @Override + public final Number getNumberValue() throws IOException { + _checkIsNumber(); + Object value = _currentObject(); + if (value instanceof Number) { + return (Number) value; + } + // Difficult to really support numbers-as-Strings; but let's try. + // NOTE: no access to DeserializationConfig, unfortunately, so can not + // try to determine Double/BigDecimal preference... + if (value instanceof String) { + String str = (String) value; + if (str.indexOf('.') >= 0) { + return Double.parseDouble(str); + } + return Long.parseLong(str); + } + if (value == null) { + return null; + } + throw new IllegalStateException("Internal error: entry should be a Number, but is of type " + +value.getClass().getName()); + } + + /* + /********************************************************** + /* Public API, access to token information, other + /********************************************************** + */ + + @Override + public Object getEmbeddedObject() + { + if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) { + return _currentObject(); + } + return null; + } + + @Override + @SuppressWarnings("resource") + public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException + { + // First: maybe we some special types? + if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) { + // Embedded byte array would work nicely... + Object ob = _currentObject(); + if (ob instanceof byte[]) { + return (byte[]) ob; + } + // fall through to error case + } + if (_currToken != JsonToken.VALUE_STRING) { + throw _constructError("Current token ("+_currToken+") not VALUE_STRING (or VALUE_EMBEDDED_OBJECT with byte[]), can not access as binary"); + } + final String str = getText(); + if (str == null) { + return null; + } + ByteArrayBuilder builder = _byteBuilder; + if (builder == null) { + _byteBuilder = builder = new ByteArrayBuilder(100); + } else { + _byteBuilder.reset(); + } + _decodeBase64(str, builder, b64variant); + return builder.toByteArray(); + } + + @Override + public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException + { + byte[] data = getBinaryValue(b64variant); + if (data != null) { + out.write(data, 0, data.length); + return data.length; + } + return 0; + } + + /* + /********************************************************** + /* Public API, native ids + /********************************************************** + */ + + @Override + public boolean canReadObjectId() { + return _hasNativeObjectIds; + } + + @Override + public boolean canReadTypeId() { + return _hasNativeTypeIds; + } + + @Override + public Object getTypeId() { + return _segment.findTypeId(_segmentPtr); + } + + @Override + public Object getObjectId() { + return _segment.findObjectId(_segmentPtr); + } + + /* + /********************************************************** + /* Internal methods + /********************************************************** + */ + + protected final Object _currentObject() { + return _segment.get(_segmentPtr); + } + + protected final void _checkIsNumber() throws JsonParseException + { + if (_currToken == null || !_currToken.isNumeric()) { + throw _constructError("Current token ("+_currToken+") not numeric, can not use numeric value accessors"); + } + } + + @Override + protected void _handleEOF() throws JsonParseException { + _throwInternal(); + } + } + + /** + * Individual segment of TokenBuffer that can store up to 16 tokens + * (limited by 4 bits per token type marker requirement). + * Current implementation uses fixed length array; could alternatively + * use 16 distinct fields and switch statement (slightly more efficient + * storage, slightly slower access) + */ + protected final static class Segment + { + public final static int TOKENS_PER_SEGMENT = 16; + + /** + * Static array used for fast conversion between token markers and + * matching {@link JsonToken} instances + */ + private final static JsonToken[] TOKEN_TYPES_BY_INDEX; + static { + // ... here we know that there are <= 15 values in JsonToken enum + TOKEN_TYPES_BY_INDEX = new JsonToken[16]; + JsonToken[] t = JsonToken.values(); + // and reserve entry 0 for "not available" + System.arraycopy(t, 1, TOKEN_TYPES_BY_INDEX, 1, Math.min(15, t.length - 1)); + } + + // // // Linking + + protected Segment _next; + + // // // State + + /** + * Bit field used to store types of buffered tokens; 4 bits per token. + * Value 0 is reserved for "not in use" + */ + protected long _tokenTypes; + + + // Actual tokens + + protected final Object[] _tokens = new Object[TOKENS_PER_SEGMENT]; + + /** + * Lazily constructed Map for storing native type and object ids, if any + */ + protected TreeMap _nativeIds; + + public Segment() { } + + // // // Accessors + + public JsonToken type(int index) + { + long l = _tokenTypes; + if (index > 0) { + l >>= (index << 2); + } + int ix = ((int) l) & 0xF; + return TOKEN_TYPES_BY_INDEX[ix]; + } + + public int rawType(int index) + { + long l = _tokenTypes; + if (index > 0) { + l >>= (index << 2); + } + int ix = ((int) l) & 0xF; + return ix; + } + + public Object get(int index) { + return _tokens[index]; + } + + public Segment next() { return _next; } + + /** + * Accessor for checking whether this segment may have native + * type or object ids. + */ + public boolean hasIds() { + return _nativeIds != null; + } + + // // // Mutators + + public Segment append(int index, JsonToken tokenType) + { + if (index < TOKENS_PER_SEGMENT) { + set(index, tokenType); + return null; + } + _next = new Segment(); + _next.set(0, tokenType); + return _next; + } + + public Segment append(int index, JsonToken tokenType, + Object objectId, Object typeId) + { + if (index < TOKENS_PER_SEGMENT) { + set(index, tokenType, objectId, typeId); + return null; + } + _next = new Segment(); + _next.set(0, tokenType, objectId, typeId); + return _next; + } + + public Segment append(int index, JsonToken tokenType, Object value) + { + if (index < TOKENS_PER_SEGMENT) { + set(index, tokenType, value); + return null; + } + _next = new Segment(); + _next.set(0, tokenType, value); + return _next; + } + + public Segment append(int index, JsonToken tokenType, Object value, + Object objectId, Object typeId) + { + if (index < TOKENS_PER_SEGMENT) { + set(index, tokenType, value, objectId, typeId); + return null; + } + _next = new Segment(); + _next.set(0, tokenType, value, objectId, typeId); + return _next; + } + + public Segment appendRaw(int index, int rawTokenType, Object value) + { + if (index < TOKENS_PER_SEGMENT) { + set(index, rawTokenType, value); + return null; + } + _next = new Segment(); + _next.set(0, rawTokenType, value); + return _next; + } + + public Segment appendRaw(int index, int rawTokenType, Object value, + Object objectId, Object typeId) + { + if (index < TOKENS_PER_SEGMENT) { + set(index, rawTokenType, value, objectId, typeId); + return null; + } + _next = new Segment(); + _next.set(0, rawTokenType, value, objectId, typeId); + return _next; + } + + private void set(int index, JsonToken tokenType) + { + /* Assumption here is that there are no overwrites, just appends; + * and so no masking is needed (nor explicit setting of null) + */ + long typeCode = tokenType.ordinal(); + if (index > 0) { + typeCode <<= (index << 2); + } + _tokenTypes |= typeCode; + } + + private void set(int index, JsonToken tokenType, + Object objectId, Object typeId) + { + long typeCode = tokenType.ordinal(); + if (index > 0) { + typeCode <<= (index << 2); + } + _tokenTypes |= typeCode; + assignNativeIds(index, objectId, typeId); + } + + private void set(int index, JsonToken tokenType, Object value) + { + _tokens[index] = value; + long typeCode = tokenType.ordinal(); + if (index > 0) { + typeCode <<= (index << 2); + } + _tokenTypes |= typeCode; + } + + private void set(int index, JsonToken tokenType, Object value, + Object objectId, Object typeId) + { + _tokens[index] = value; + long typeCode = tokenType.ordinal(); + if (index > 0) { + typeCode <<= (index << 2); + } + _tokenTypes |= typeCode; + assignNativeIds(index, objectId, typeId); + } + + private void set(int index, int rawTokenType, Object value) + { + _tokens[index] = value; + long typeCode = (long) rawTokenType; + if (index > 0) { + typeCode <<= (index << 2); + } + _tokenTypes |= typeCode; + } + + private void set(int index, int rawTokenType, Object value, Object objectId, Object typeId) + { + _tokens[index] = value; + long typeCode = (long) rawTokenType; + if (index > 0) { + typeCode <<= (index << 2); + } + _tokenTypes |= typeCode; + assignNativeIds(index, objectId, typeId); + } + + private final void assignNativeIds(int index, Object objectId, Object typeId) + { + if (_nativeIds == null) { + _nativeIds = new TreeMap(); + } + if (objectId != null) { + _nativeIds.put(_objectIdIndex(index), objectId); + } + if (typeId != null) { + _nativeIds.put(_typeIdIndex(index), typeId); + } + } + + /** + * @since 2.3 + */ + public Object findObjectId(int index) { + return (_nativeIds == null) ? null : _nativeIds.get(_objectIdIndex(index)); + } + + /** + * @since 2.3 + */ + public Object findTypeId(int index) { + return (_nativeIds == null) ? null : _nativeIds.get(_typeIdIndex(index)); + } + + private final int _typeIdIndex(int i) { return i+i; } + private final int _objectIdIndex(int i) { return i+i+1; } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/TypeKey.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/TypeKey.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/TypeKey.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,130 @@ +package com.fasterxml.jackson.databind.util; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Key that offers two "modes"; one with raw class, as used for + * cases were raw class type is available (for example, when using + * runtime type); and one with full generics-including. + */ +public class TypeKey +{ + protected int _hashCode; + + protected Class _class; + + protected JavaType _type; + + /** + * Indicator of whether serializer stored has a type serializer + * wrapper around it or not; if not, it is "untyped" serializer; + * if it has, it is "typed" + */ + protected boolean _isTyped; + + public TypeKey() { } + + public TypeKey(TypeKey src) { + _hashCode = src._hashCode; + _class = src._class; + _type = src._type; + _isTyped = src._isTyped; + } + + public TypeKey(Class key, boolean typed) { + _class = key; + _type = null; + _isTyped = typed; + _hashCode = typed ? typedHash(key) : untypedHash(key); + } + + public TypeKey(JavaType key, boolean typed) { + _type = key; + _class = null; + _isTyped = typed; + _hashCode = typed ? typedHash(key) : untypedHash(key); + } + + public final static int untypedHash(Class cls) { + return cls.getName().hashCode(); + } + + public final static int typedHash(Class cls) { + return cls.getName().hashCode()+1; + } + + public final static int untypedHash(JavaType type) { + return type.hashCode() - 1; + } + + public final static int typedHash(JavaType type) { + return type.hashCode() - 2; + } + + public final void resetTyped(Class cls) { + _type = null; + _class = cls; + _isTyped = true; + _hashCode = typedHash(cls); + } + + public final void resetUntyped(Class cls) { + _type = null; + _class = cls; + _isTyped = false; + _hashCode = untypedHash(cls); + } + + public final void resetTyped(JavaType type) { + _type = type; + _class = null; + _isTyped = true; + _hashCode = typedHash(type); + } + + public final void resetUntyped(JavaType type) { + _type = type; + _class = null; + _isTyped = false; + _hashCode = untypedHash(type); + } + + public boolean isTyped() { + return _isTyped; + } + + public Class getRawType() { + return _class; + } + + public JavaType getType() { + return _type; + } + + @Override public final int hashCode() { return _hashCode; } + + @Override public final String toString() { + if (_class != null) { + return "{class: "+_class.getName()+", typed? "+_isTyped+"}"; + } + return "{type: "+_type+", typed? "+_isTyped+"}"; + } + + // note: we assume key is never used for anything other than as map key, so: + @Override public final boolean equals(Object o) + { + if (o == null) return false; + if (o == this) return true; + if (o.getClass() != getClass()) { + return false; + } + TypeKey other = (TypeKey) o; + if (other._isTyped == _isTyped) { + if (_class != null) { + return other._class == _class; + } + return _type.equals(other._type); + } + return false; + } +} \ No newline at end of file Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ViewMatcher.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ViewMatcher.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/ViewMatcher.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,68 @@ +package com.fasterxml.jackson.databind.util; + +/** + * Helper class used for checking whether a property is visible + * in the active view + */ +public class ViewMatcher implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + protected final static ViewMatcher EMPTY = new ViewMatcher(); + + public boolean isVisibleForView(Class activeView) { return false; } + + public static ViewMatcher construct(Class[] views) + { + if (views == null) { + return EMPTY; + } + switch (views.length) { + case 0: + return EMPTY; + case 1: + return new Single(views[0]); + } + return new Multi(views); + } + + /* + /********************************************************** + /* Concrete sub-classes + /********************************************************** + */ + + private final static class Single extends ViewMatcher + { + private static final long serialVersionUID = 1L; + + private final Class _view; + public Single(Class v) { _view = v; } + @Override + public boolean isVisibleForView(Class activeView) { + return (activeView == _view) || _view.isAssignableFrom(activeView); + } + } + + private final static class Multi extends ViewMatcher + implements java.io.Serializable + { + private static final long serialVersionUID = 1L; + + private final Class[] _views; + + public Multi(Class[] v) { _views = v; } + + @Override + public boolean isVisibleForView(Class activeView) + { + for (int i = 0, len = _views.length; i < len; ++i) { + Class view = _views[i]; + if ((activeView == view) || view.isAssignableFrom(activeView)) { + return true; + } + } + return false; + } + } +} Index: 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/package-info.java =================================================================== diff -u --- 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/package-info.java (revision 0) +++ 3rdParty_sources/jackson/com/fasterxml/jackson/databind/util/package-info.java (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -0,0 +1,4 @@ +/** + * Utility classes for Mapper package. + */ +package com.fasterxml.jackson.databind.util; Index: 3rdParty_sources/versions.txt =================================================================== diff -u -rc3f6885c57b0a91b0a58e9126310131bc37b2998 -r38430d27ece1cd540759760d4c30dd4eb29cdd03 --- 3rdParty_sources/versions.txt (.../versions.txt) (revision c3f6885c57b0a91b0a58e9126310131bc37b2998) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision 38430d27ece1cd540759760d4c30dd4eb29cdd03) @@ -31,6 +31,8 @@ httpunit 1.7 +Jackson 2.7.4 + JBoss Web 2.1.3 jLaTexMath 1.0.6