jackson-src-1.9.2/ 0000755 0001750 0001750 00000000000 11672662540 014423 5 ustar jamespage jamespage jackson-src-1.9.2/ant/ 0000755 0001750 0001750 00000000000 11672662540 015205 5 ustar jamespage jamespage jackson-src-1.9.2/ant/build-mini.xml 0000644 0001750 0001750 00000003757 11655120726 017770 0 ustar jamespage jamespage
jackson-src-1.9.2/ant/build-coverage.xml 0000644 0001750 0001750 00000007306 11655120726 020621 0 ustar jamespage jamespage
jackson-src-1.9.2/ant/build-clirr.xml 0000644 0001750 0001750 00000001245 11655120726 020135 0 ustar jamespage jamespage
jackson-src-1.9.2/ant/build-test.xml 0000644 0001750 0001750 00000026724 11655120726 020012 0 ustar jamespage jamespage
jackson-src-1.9.2/ant/build-jarjar-asm.xml 0000644 0001750 0001750 00000001131 11655120726 021043 0 ustar jamespage jamespage
jackson-src-1.9.2/ant/build-maven-deploy.xml 0000644 0001750 0001750 00000024075 11655120726 021430 0 ustar jamespage jamespage
jackson-src-1.9.2/ant/build-osgi.xml 0000644 0001750 0001750 00000044035 11655120726 017767 0 ustar jamespage jamespage
jackson-src-1.9.2/src/ 0000755 0001750 0001750 00000000000 11672662540 015212 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/ 0000755 0001750 0001750 00000000000 11655120726 016317 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/java/ 0000755 0001750 0001750 00000000000 11655120726 017240 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/java/org/ 0000755 0001750 0001750 00000000000 11655120726 020027 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/java/org/codehaus/ 0000755 0001750 0001750 00000000000 11655120726 021622 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/ 0000755 0001750 0001750 00000000000 11655120726 023252 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/ 0000755 0001750 0001750 00000000000 11672662540 024367 5 ustar jamespage jamespage jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileConstants.java 0000644 0001750 0001750 00000031557 11655120726 030207 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
/**
* Constants used by {@link SmileGenerator} and {@link SmileParser}
*
* @author tatu
*/
public final class SmileConstants
{
/*
/**********************************************************
/* Thresholds
/**********************************************************
*/
/**
* Encoding has special "short" forms for value Strings that can
* be represented by 64 bytes of UTF-8 or less.
*/
public final static int MAX_SHORT_VALUE_STRING_BYTES = 64;
/**
* Encoding has special "short" forms for field names that can
* be represented by 64 bytes of UTF-8 or less.
*/
public final static int MAX_SHORT_NAME_ASCII_BYTES = 64;
/**
* Maximum byte length for short non-ASCII names is slightly
* less due to having to reserve bytes 0xF8 and above (but
* we get one more as values 0 and 1 are not valid)
*/
public final static int MAX_SHORT_NAME_UNICODE_BYTES = 56;
/**
* Longest back reference we use for field names is 10 bits; no point
* in keeping much more around
*/
public final static int MAX_SHARED_NAMES = 1024;
/**
* Longest back reference we use for short shared String values is 10 bits,
* so up to (1 << 10) values to keep track of.
*/
public final static int MAX_SHARED_STRING_VALUES = 1024;
/**
* Also: whereas we can refer to names of any length, we will only consider
* text values that are considered "tiny" or "short" (ones encoded with
* length prefix); this value thereby has to be maximum length of Strings
* that can be encoded as such.
*/
public final static int MAX_SHARED_STRING_LENGTH_BYTES = 65;
/**
* And to make encoding logic tight and simple, we can always
* require that output buffer has this amount of space
* available before encoding possibly short String (3 bytes since
* longest UTF-8 encoded Java char is 3 bytes).
* Two extra bytes need to be reserved as well; first for token indicator,
* and second for terminating null byte (in case it's not a short String after all)
*/
public final static int MIN_BUFFER_FOR_POSSIBLE_SHORT_STRING = 1 + (3 * 65);
/*
/**********************************************************
/* Byte markers
/**********************************************************
*/
/**
* We need a byte marker to denote end of variable-length Strings. Although
* null byte is commonly used, let's try to avoid using it since it can't
* be embedded in Web Sockets content (similarly, 0xFF can't). There are
* multiple candidates for bytes UTF-8 can not have; 0xFC is chosen to
* allow reasonable ordering (highest values meaning most significant
* framing function; 0xFF being end-of-content and so on)
*/
public final static int INT_MARKER_END_OF_STRING = 0xFC;
public final static byte BYTE_MARKER_END_OF_STRING = (byte) INT_MARKER_END_OF_STRING;
/**
* In addition we can use a marker to allow simple framing; splitting
* of physical data (like file) into distinct logical sections like
* JSON documents. 0xFF makes sense here since it is also used
* as end marker for Web Sockets.
*/
public final static byte BYTE_MARKER_END_OF_CONTENT = (byte) 0xFF;
/*
/**********************************************************
/* Format header: put smile on your data...
/**********************************************************
*/
/**
* First byte of data header
*/
public final static byte HEADER_BYTE_1 = (byte) ':';
/**
* Second byte of data header
*/
public final static byte HEADER_BYTE_2 = (byte) ')';
/**
* Third byte of data header
*/
public final static byte HEADER_BYTE_3 = (byte) '\n';
/**
* Current version consists of four zero bits (nibble)
*/
public final static int HEADER_VERSION_0 = 0x0;
/**
* Fourth byte of data header; contains version nibble, may
* have flags
*/
public final static byte HEADER_BYTE_4 = (HEADER_VERSION_0 << 4);
/**
* Indicator bit that indicates whether encoded content may
* have Shared names (back references to recently encoded field
* names). If no header available, must be
* processed as if this was set to true.
* If (and only if) header exists, and value is 0, can parser
* omit storing of seen names, as it is guaranteed that no back
* references exist.
*/
public final static int HEADER_BIT_HAS_SHARED_NAMES = 0x01;
/**
* Indicator bit that indicates whether encoded content may
* have shared String values (back references to recently encoded
* 'short' String values, where short is defined as 64 bytes or less).
* If no header available, can be assumed to be 0 (false).
* If header exists, and bit value is 1, parsers has to store up
* to 1024 most recently seen distinct short String values.
*/
public final static int HEADER_BIT_HAS_SHARED_STRING_VALUES = 0x02;
/**
* Indicator bit that indicates whether encoded content may
* contain raw (unquoted) binary values.
* If no header available, can be assumed to be 0 (false).
* If header exists, and bit value is 1, parser can not assume that
* specific byte values always have default meaning (specifically,
* content end marker 0xFF and header signature can be contained
* in binary values)
*
* Note that this bit being true does not automatically mean that
* such raw binary content indeed exists; just that it may exist.
* This because header is written before any binary data may be
* written.
*/
public final static int HEADER_BIT_HAS_RAW_BINARY = 0x04;
/*
/**********************************************************
/* Type prefixes: 3 MSB of token byte
/**********************************************************
*/
// Shared strings are back references for last 63 short (< 64 byte) string values
// NOTE: 0x00 is reserved, not used with current version (may be used in future)
public final static int TOKEN_PREFIX_SHARED_STRING_SHORT = 0x00;
// literals are put between 0x20 and 0x3F to reserve markers (smiley), along with ints/doubles
//public final static int TOKEN_PREFIX_MISC_NUMBERS = 0x20;
public final static int TOKEN_PREFIX_TINY_ASCII = 0x40;
public final static int TOKEN_PREFIX_SMALL_ASCII = 0x60;
public final static int TOKEN_PREFIX_TINY_UNICODE = 0x80;
public final static int TOKEN_PREFIX_SHORT_UNICODE = 0xA0;
// Small ints are 4-bit (-16 to +15) integer constants
public final static int TOKEN_PREFIX_SMALL_INT = 0xC0;
// And misc types have empty at the end too, to reserve 0xF8 - 0xFF
public final static int TOKEN_PREFIX_MISC_OTHER = 0xE0;
/*
/**********************************************************
/* Token literals, normal mode
/**********************************************************
*/
// First, non-structured literals
public final static byte TOKEN_LITERAL_EMPTY_STRING = 0x20;
public final static byte TOKEN_LITERAL_NULL = 0x21;
public final static byte TOKEN_LITERAL_FALSE = 0x22;
public final static byte TOKEN_LITERAL_TRUE = 0x23;
// And then structured literals
public final static byte TOKEN_LITERAL_START_ARRAY = (byte) 0xF8;
public final static byte TOKEN_LITERAL_END_ARRAY = (byte) 0xF9;
public final static byte TOKEN_LITERAL_START_OBJECT = (byte) 0xFA;
public final static byte TOKEN_LITERAL_END_OBJECT = (byte) 0xFB;
/*
/**********************************************************
/* Subtype constants for misc text/binary types
/**********************************************************
*/
/**
* Type (for misc, other) used
* for regular integral types (byte/short/int/long)
*/
public final static int TOKEN_MISC_INTEGER = 0x24;
/**
* Type (for misc, other) used
* for regular floating-point types (float, double)
*/
public final static int TOKEN_MISC_FP = 0x28;
/**
* Type (for misc, other) used for
* variable length UTF-8 encoded text, when it is known to only contain ASCII chars.
* Note: 2 LSB are reserved for future use; must be zeroes for now
*/
public final static int TOKEN_MISC_LONG_TEXT_ASCII = 0xE0;
/**
* Type (for misc, other) used
* for variable length UTF-8 encoded text, when it is NOT known to only contain ASCII chars
* (which means it MAY have multi-byte characters)
* Note: 2 LSB are reserved for future use; must be zeroes for now
*/
public final static int TOKEN_MISC_LONG_TEXT_UNICODE = 0xE4;
/**
* Type (for misc, other) used
* for "safe" (encoded by only using 7 LSB, giving 8/7 expansion ratio).
* This is usually done to ensure that certain bytes are never included
* in encoded data (like 0xFF)
* Note: 2 LSB are reserved for future use; must be zeroes for now
*/
public final static int TOKEN_MISC_BINARY_7BIT = 0xE8;
/**
* Type (for misc, other) used for shared String values where index
* does not fit in "short" reference range (which is 0 - 30). If so,
* 2 LSB from here and full following byte are used to get 10-bit
* index. Values
*/
public final static int TOKEN_MISC_SHARED_STRING_LONG = 0xEC;
/**
* Raw binary data marker is specifically chosen as separate from
* other types, since it can have significant impact on framing
* (or rather fast scanning based on structure and framing markers).
*/
public final static int TOKEN_MISC_BINARY_RAW = 0xFD;
/*
/**********************************************************
/* Modifiers for numeric entries
/**********************************************************
*/
/**
* Numeric subtype (2 LSB) for {@link #TOKEN_MISC_INTEGER},
* indicating 32-bit integer (int)
*/
public final static int TOKEN_MISC_INTEGER_32 = 0x00;
/**
* Numeric subtype (2 LSB) for {@link #TOKEN_MISC_INTEGER},
* indicating 32-bit integer (long)
*/
public final static int TOKEN_MISC_INTEGER_64 = 0x01;
/**
* Numeric subtype (2 LSB) for {@link #TOKEN_MISC_INTEGER},
* indicating {@link java.math.BigInteger} type.
*/
public final static int TOKEN_MISC_INTEGER_BIG = 0x02;
// Note: type 3 (0xF3) reserved for future use
/**
* Numeric subtype (2 LSB) for {@link #TOKEN_MISC_FP},
* indicating 32-bit IEEE single precision floating point number.
*/
public final static int TOKEN_MISC_FLOAT_32 = 0x00;
/**
* Numeric subtype (2 LSB) for {@link #TOKEN_MISC_FP},
* indicating 64-bit IEEE double precision floating point number.
*/
public final static int TOKEN_MISC_FLOAT_64 = 0x01;
/**
* Numeric subtype (2 LSB) for {@link #TOKEN_MISC_FP},
* indicating {@link java.math.BigDecimal} type.
*/
public final static int TOKEN_MISC_FLOAT_BIG = 0x02;
// Note: type 3 (0xF7) reserved for future use
/*
/**********************************************************
/* Token types for keys
/**********************************************************
*/
/**
* Let's use same code for empty key as for empty String value
*/
public final static byte TOKEN_KEY_EMPTY_STRING = 0x20;
public final static int TOKEN_PREFIX_KEY_SHARED_LONG = 0x30;
public final static byte TOKEN_KEY_LONG_STRING = 0x34;
public final static int TOKEN_PREFIX_KEY_SHARED_SHORT = 0x40;
public final static int TOKEN_PREFIX_KEY_ASCII = 0x80;
public final static int TOKEN_PREFIX_KEY_UNICODE = 0xC0;
/*
/**********************************************************
/* Basic UTF-8 decode/encode table
/**********************************************************
*/
/**
* Additionally we can combine UTF-8 decoding info into similar
* data table.
* Values indicate "byte length - 1"; meaning -1 is used for
* invalid bytes, 0 for single-byte codes, 1 for 2-byte codes
* and 2 for 3-byte codes.
*/
public final static int[] sUtf8UnitLengths;
static {
int[] table = new int[256];
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 = 1;
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
code = 2;
} else if ((c & 0xF8) == 0xF0) {
// 4 bytes; double-char with surrogates and all...
code = 3;
} else {
// And -1 seems like a good "universal" error marker...
code = -1;
}
table[c] = code;
}
sUtf8UnitLengths = table;
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileUtil.java 0000644 0001750 0001750 00000002247 11655120726 027142 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
/**
* Class for miscellaneous helper methods.
*/
public class SmileUtil
{
public static int zigzagEncode(int input) {
// Canonical version:
//return (input << 1) ^ (input >> 31);
// but this is even better
if (input < 0) {
return (input << 1) ^ -1;
}
return (input << 1);
}
public static int zigzagDecode(int encoded) {
// canonical:
//return (encoded >>> 1) ^ (-(encoded & 1));
if ((encoded & 1) == 0) { // positive
return (encoded >>> 1);
}
// negative
return (encoded >>> 1) ^ -1;
}
public static long zigzagEncode(long input) {
// Canonical version
//return (input << 1) ^ (input >> 63);
if (input < 0L) {
return (input << 1) ^ -1L;
}
return (input << 1);
}
public static long zigzagDecode(long encoded) {
// canonical:
//return (encoded >>> 1) ^ (-(encoded & 1));
if ((encoded & 1) == 0) { // positive
return (encoded >>> 1);
}
// negative
return (encoded >>> 1) ^ -1L;
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileGenerator.java 0000644 0001750 0001750 00000237523 11655120726 030162 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
import java.io.*;
import java.lang.ref.SoftReference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.io.SerializedString;
import org.codehaus.jackson.impl.JsonGeneratorBase;
import org.codehaus.jackson.impl.JsonWriteContext;
import static org.codehaus.jackson.smile.SmileConstants.*;
/**
* {@link JsonGenerator} implementation for the experimental "Binary JSON Infoset".
*
* @author tatu
*/
public class SmileGenerator
extends JsonGeneratorBase
{
/**
* Enumeration that defines all togglable features for Smile generators.
*/
public enum Feature {
/**
* Whether to write 4-byte header sequence when starting output or not.
* If disabled, no header is written; this may be useful in embedded cases
* where context is enough to know that content is encoded using this format.
* Note, however, that omitting header means that default settings for
* shared names/string values can not be changed.
*
* Default setting is true, meaning that header will be written.
*/
WRITE_HEADER(true),
/**
* Whether write byte marker that signifies end of logical content segment
* ({@link SmileConstants#BYTE_MARKER_END_OF_CONTENT}) when
* {@link #close} is called or not. This can be useful when outputting
* multiple adjacent logical content segments (documents) into single
* physical output unit (file).
*
* Default setting is false meaning that such marker is not written.
*/
WRITE_END_MARKER(false),
/**
* Whether to use simple 7-bit per byte encoding for binary content when output.
* This is necessary ensure that byte 0xFF will never be included in content output.
* For other data types this limitation is handled automatically; but since overhead
* for binary data (14% size expansion, processing overhead) is non-negligible,
* it is not enabled by default. If no binary data is output, feature has no effect.
*
* Default setting is true, indicating that binary data is quoted as 7-bit bytes
* instead of written raw.
*/
ENCODE_BINARY_AS_7BIT(true),
/**
* Whether generator should check if it can "share" field names during generating
* content or not. If enabled, can replace repeating field names with back references,
* which are more compact and should faster to decode. Downside is that there is some
* overhead for writing (need to track existing values, check), as well as decoding.
*
* Since field names tend to repeat quite often, this setting is enabled by default.
*/
CHECK_SHARED_NAMES(true),
/**
* Whether generator should check if it can "share" short (at most 64 bytes encoded)
* String value during generating
* content or not. If enabled, can replace repeating Short String values with back references,
* which are more compact and should faster to decode. Downside is that there is some
* overhead for writing (need to track existing values, check), as well as decoding.
*
* Since efficiency of this option depends a lot on type of content being produced,
* this option is disabled by default, and should only be enabled if it is likely that
* same values repeat relatively often.
*/
CHECK_SHARED_STRING_VALUES(false)
;
protected final boolean _defaultState;
protected 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; }
public int getMask() { return _mask; }
}
/**
* Helper class used for keeping track of possibly shareable String
* references (for field names and/or short String values)
*/
protected final static class SharedStringNode
{
public final String value;
public final int index;
public SharedStringNode next;
public SharedStringNode(String value, int index, SharedStringNode next)
{
this.value = value;
this.index = index;
this.next = next;
}
}
/**
* To simplify certain operations, we require output buffer length
* to allow outputting of contiguous 256 character UTF-8 encoded String
* value. Length of the longest UTF-8 code point (from Java char) is 3 bytes,
* and we need both initial token byte and single-byte end marker
* so we get following value.
*
* Note: actually we could live with shorter one; absolute minimum would
* be for encoding 64-character Strings.
*/
private final static int MIN_BUFFER_LENGTH = (3 * 256) + 2;
protected final static byte TOKEN_BYTE_LONG_STRING_ASCII = (byte) TOKEN_MISC_LONG_TEXT_ASCII;
protected final static byte TOKEN_BYTE_LONG_STRING_UNICODE = (byte) TOKEN_MISC_LONG_TEXT_UNICODE;
protected final static byte TOKEN_BYTE_INT_32 = (byte) (TOKEN_MISC_INTEGER | TOKEN_MISC_INTEGER_32);
protected final static byte TOKEN_BYTE_INT_64 = (byte) (TOKEN_MISC_INTEGER | TOKEN_MISC_INTEGER_64);
protected final static byte TOKEN_BYTE_BIG_INTEGER = (byte) (TOKEN_MISC_INTEGER | TOKEN_MISC_INTEGER_BIG);
protected final static byte TOKEN_BYTE_FLOAT_32 = (byte) (TOKEN_MISC_FP | TOKEN_MISC_FLOAT_32);
protected final static byte TOKEN_BYTE_FLOAT_64 = (byte) (TOKEN_MISC_FP | TOKEN_MISC_FLOAT_64);
protected final static byte TOKEN_BYTE_BIG_DECIMAL = (byte) (TOKEN_MISC_FP | TOKEN_MISC_FLOAT_BIG);
protected final static int SURR1_FIRST = 0xD800;
protected final static int SURR1_LAST = 0xDBFF;
protected final static int SURR2_FIRST = 0xDC00;
protected final static int SURR2_LAST = 0xDFFF;
protected final static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
protected final static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
final protected IOContext _ioContext;
final protected OutputStream _out;
/**
* Bit flag composed of bits that indicate which
* {@link org.codehaus.jackson.smile.SmileGenerator.Feature}s
* are enabled.
*/
protected int _smileFeatures;
/**
* Helper object used for low-level recycling of Smile-generator
* specific buffers.
*
* @since 1.7
*/
final protected SmileBufferRecycler _smileBufferRecycler;
/*
/**********************************************************
/* Output buffering
/**********************************************************
*/
/**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_out}.
*/
protected byte[] _outputBuffer;
/**
* Pointer to the next available byte in {@link #_outputBuffer}
*/
protected int _outputTail = 0;
/**
* Offset to index after the last valid index in {@link #_outputBuffer}.
* Typically same as length of the buffer.
*/
protected final int _outputEnd;
/**
* Intermediate buffer in which characters of a String are copied
* before being encoded.
*/
protected char[] _charBuffer;
protected final int _charBufferLength;
/**
* Let's keep track of how many bytes have been output, may prove useful
* when debugging. This does not include bytes buffered in
* the output buffer, just bytes that have been written using underlying
* stream writer.
*/
protected int _bytesWritten;
/*
/**********************************************************
/* Shared String detection
/**********************************************************
*/
/**
* Raw data structure used for checking whether field name to
* write can be output using back reference or not.
*/
protected SharedStringNode[] _seenNames;
/**
* Number of entries in {@link #_seenNames}; -1 if no shared name
* detection is enabled
*/
protected int _seenNameCount;
/**
* Raw data structure used for checking whether String value to
* write can be output using back reference or not.
*/
protected SharedStringNode[] _seenStringValues;
/**
* Number of entries in {@link #_seenStringValues}; -1 if no shared text value
* detection is enabled
*/
protected int _seenStringValueCount;
/**
* 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;
/*
/**********************************************************
/* Thread-local recycling
/**********************************************************
*/
/**
* This ThreadLocal
contains a {@link java.lang.ref.SoftRerefence}
* to a buffer recycler used to provide a low-cost
* buffer recycling for Smile-specific buffers.
*/
final protected static ThreadLocal>> _smileRecyclerRef
= new ThreadLocal>>();
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public SmileGenerator(IOContext ctxt, int jsonFeatures, int smileFeatures,
ObjectCodec codec, OutputStream out)
{
super(jsonFeatures, codec);
_smileFeatures = smileFeatures;
_ioContext = ctxt;
_smileBufferRecycler = _smileBufferRecycler();
_out = out;
_bufferRecyclable = true;
_outputBuffer = ctxt.allocWriteEncodingBuffer();
_outputEnd = _outputBuffer.length;
_charBuffer = ctxt.allocConcatBuffer();
_charBufferLength = _charBuffer.length;
// let's just sanity check to prevent nasty odd errors
if (_outputEnd < MIN_BUFFER_LENGTH) {
throw new IllegalStateException("Internal encoding buffer length ("+_outputEnd
+") too short, must be at least "+MIN_BUFFER_LENGTH);
}
if ((smileFeatures & Feature.CHECK_SHARED_NAMES.getMask()) == 0) {
_seenNames = null;
_seenNameCount = -1;
} else {
_seenNames = _smileBufferRecycler.allocSeenNamesBuffer();
if (_seenNames == null) {
_seenNames = new SharedStringNode[SmileBufferRecycler.DEFAULT_NAME_BUFFER_LENGTH];
}
_seenNameCount = 0;
}
if ((smileFeatures & Feature.CHECK_SHARED_STRING_VALUES.getMask()) == 0) {
_seenStringValues = null;
_seenStringValueCount = -1;
} else {
_seenStringValues = _smileBufferRecycler.allocSeenStringValuesBuffer();
if (_seenStringValues == null) {
_seenStringValues = new SharedStringNode[SmileBufferRecycler.DEFAULT_STRING_VALUE_BUFFER_LENGTH];
}
_seenStringValueCount = 0;
}
}
public SmileGenerator(IOContext ctxt, int jsonFeatures, int smileFeatures,
ObjectCodec codec, OutputStream out, byte[] outputBuffer, int offset, boolean bufferRecyclable)
{
super(jsonFeatures, codec);
_smileFeatures = smileFeatures;
_ioContext = ctxt;
_smileBufferRecycler = _smileBufferRecycler();
_out = out;
_bufferRecyclable = bufferRecyclable;
_outputTail = offset;
_outputBuffer = outputBuffer;
_outputEnd = _outputBuffer.length;
_charBuffer = ctxt.allocConcatBuffer();
_charBufferLength = _charBuffer.length;
// let's just sanity check to prevent nasty odd errors
if (_outputEnd < MIN_BUFFER_LENGTH) {
throw new IllegalStateException("Internal encoding buffer length ("+_outputEnd
+") too short, must be at least "+MIN_BUFFER_LENGTH);
}
if ((smileFeatures & Feature.CHECK_SHARED_NAMES.getMask()) == 0) {
_seenNames = null;
_seenNameCount = -1;
} else {
_seenNames = _smileBufferRecycler.allocSeenNamesBuffer();
if (_seenNames == null) {
_seenNames = new SharedStringNode[SmileBufferRecycler.DEFAULT_NAME_BUFFER_LENGTH];
}
_seenNameCount = 0;
}
if ((smileFeatures & Feature.CHECK_SHARED_STRING_VALUES.getMask()) == 0) {
_seenStringValues = null;
_seenStringValueCount = -1;
} else {
_seenStringValues = _smileBufferRecycler.allocSeenStringValuesBuffer();
if (_seenStringValues == null) {
_seenStringValues = new SharedStringNode[SmileBufferRecycler.DEFAULT_STRING_VALUE_BUFFER_LENGTH];
}
_seenStringValueCount = 0;
}
}
/**
* Method that can be called to explicitly write Smile document header.
* Note that usually you do not need to call this for first document to output,
* but rather only if you intend to write multiple root-level documents
* with same generator (and even in that case this is optional thing to do).
* As a result usually only {@link SmileFactory} calls this method.
*/
public void writeHeader() throws IOException
{
int last = HEADER_BYTE_4;
if ((_smileFeatures & Feature.CHECK_SHARED_NAMES.getMask()) != 0) {
last |= SmileConstants.HEADER_BIT_HAS_SHARED_NAMES;
}
if ((_smileFeatures & Feature.CHECK_SHARED_STRING_VALUES.getMask()) != 0) {
last |= SmileConstants.HEADER_BIT_HAS_SHARED_STRING_VALUES;
}
if ((_smileFeatures & Feature.ENCODE_BINARY_AS_7BIT.getMask()) == 0) {
last |= SmileConstants.HEADER_BIT_HAS_RAW_BINARY;
}
_writeBytes(HEADER_BYTE_1, HEADER_BYTE_2, HEADER_BYTE_3, (byte) last);
}
protected final static SmileBufferRecycler _smileBufferRecycler()
{
SoftReference> ref = _smileRecyclerRef.get();
SmileBufferRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new SmileBufferRecycler();
_smileRecyclerRef.set(new SoftReference>(br));
}
return br;
}
/*
/**********************************************************
/* Overridden methods, configuration
/**********************************************************
*/
/**
* No way (or need) to indent anything, so let's block any attempts.
* (should we throw an exception instead?)
*/
@Override
public JsonGenerator useDefaultPrettyPrinter()
{
return this;
}
/**
* No way (or need) to indent anything, so let's block any attempts.
* (should we throw an exception instead?)
*/
@Override
public JsonGenerator setPrettyPrinter(PrettyPrinter pp) {
return this;
}
@Override
public Object getOutputTarget() {
return _out;
}
/*
/**********************************************************
/* Overridden methods, write methods
/**********************************************************
*/
/* And then methods overridden to make final, streamline some
* aspects...
*/
@Override
public final void writeFieldName(String name) throws IOException, JsonGenerationException
{
if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name);
}
@Override
public final void writeFieldName(SerializedString name)
throws IOException, JsonGenerationException
{
// Object is a value, need to verify it's allowed
if (_writeContext.writeFieldName(name.getValue()) == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name);
}
@Override
public final void writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
// Object is a value, need to verify it's allowed
if (_writeContext.writeFieldName(name.getValue()) == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name);
}
@Override
public final void writeStringField(String fieldName, String value)
throws IOException, JsonGenerationException
{
if (_writeContext.writeFieldName(fieldName) == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(fieldName);
writeString(value);
}
/*
/**********************************************************
/* Extended API, configuration
/**********************************************************
*/
public SmileGenerator enable(Feature f) {
_smileFeatures |= f.getMask();
return this;
}
public SmileGenerator disable(Feature f) {
_smileFeatures &= ~f.getMask();
return this;
}
public final boolean isEnabled(Feature f) {
return (_smileFeatures & f.getMask()) != 0;
}
public SmileGenerator configure(Feature f, boolean state) {
if (state) {
enable(f);
} else {
disable(f);
}
return this;
}
/*
/**********************************************************
/* Extended API, other
/**********************************************************
*/
/**
* Method for directly inserting specified byte in output at
* current position.
*
* NOTE: only use this method if you really know what you are doing.
*/
public void writeRaw(byte b) throws IOException, JsonGenerationException
{
_writeByte(TOKEN_LITERAL_START_ARRAY);
}
/**
* Method for directly inserting specified bytes in output at
* current position.
*
* NOTE: only use this method if you really know what you are doing.
*/
public void writeBytes(byte[] data, int offset, int len) throws IOException
{
_writeBytes(data, offset, len);
}
/*
/**********************************************************
/* Output method implementations, structural
/**********************************************************
*/
@Override
public final void writeStartArray() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
_writeByte(TOKEN_LITERAL_START_ARRAY);
}
@Override
public final void writeEndArray() throws IOException, JsonGenerationException
{
if (!_writeContext.inArray()) {
_reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
}
_writeByte(TOKEN_LITERAL_END_ARRAY);
_writeContext = _writeContext.getParent();
}
@Override
public final void writeStartObject() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
_writeByte(TOKEN_LITERAL_START_OBJECT);
}
@Override
public final void writeEndObject() throws IOException, JsonGenerationException
{
if (!_writeContext.inObject()) {
_reportError("Current context not an object but "+_writeContext.getTypeDesc());
}
_writeContext = _writeContext.getParent();
_writeByte(TOKEN_LITERAL_END_OBJECT);
}
private final void _writeFieldName(String name)
throws IOException, JsonGenerationException
{
int len = name.length();
if (len == 0) {
_writeByte(TOKEN_KEY_EMPTY_STRING);
return;
}
// First: is it something we can share?
if (_seenNameCount >= 0) {
int ix = _findSeenName(name);
if (ix >= 0) {
_writeSharedNameReference(ix);
return;
}
}
if (len > MAX_SHORT_NAME_UNICODE_BYTES) { // can not be a 'short' String; off-line (rare case)
_writeNonShortFieldName(name, len);
return;
}
// first: ensure we have enough space
if ((_outputTail + MIN_BUFFER_FOR_POSSIBLE_SHORT_STRING) >= _outputEnd) {
_flushBuffer();
}
// then let's copy String chars to char buffer, faster than using getChar (measured, profiled)
name.getChars(0, len, _charBuffer, 0);
int origOffset = _outputTail;
++_outputTail; // to reserve space for type token
int byteLen = _shortUTF8Encode(_charBuffer, 0, len);
byte typeToken;
// ASCII?
if (byteLen == len) {
if (byteLen <= MAX_SHORT_NAME_ASCII_BYTES) { // yes, is short indeed
typeToken = (byte) ((TOKEN_PREFIX_KEY_ASCII - 1) + byteLen);
} else { // longer albeit ASCII
typeToken = TOKEN_KEY_LONG_STRING;
// and we will need String end marker byte
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
} else { // not all ASCII
if (byteLen <= MAX_SHORT_NAME_UNICODE_BYTES) { // yes, is short indeed
// note: since 2 is smaller allowed length, offset differs from one used for
typeToken = (byte) ((TOKEN_PREFIX_KEY_UNICODE - 2) + byteLen);
} else { // nope, longer non-ASCII Strings
typeToken = TOKEN_KEY_LONG_STRING;
// and we will need String end marker byte
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
}
// and then sneak in type token now that know the details
_outputBuffer[origOffset] = typeToken;
// Also, keep track if we can use back-references (shared names)
if (_seenNameCount >= 0) {
_addSeenName(name);
}
}
private final void _writeNonShortFieldName(final String name, final int len)
throws IOException, JsonGenerationException
{
_writeByte(TOKEN_KEY_LONG_STRING);
// can we still make a temp copy?
if (len > _charBufferLength) { // nah, not even that
_slowUTF8Encode(name);
} else { // yep.
name.getChars(0, len, _charBuffer, 0);
// but will encoded version fit in buffer?
int maxLen = len + len + len;
if (maxLen <= _outputBuffer.length) { // yes indeed
if ((_outputTail + maxLen) >= _outputEnd) {
_flushBuffer();
}
_shortUTF8Encode(_charBuffer, 0, len);
} else { // nope, need bit slower variant
_mediumUTF8Encode(_charBuffer, 0, len);
}
}
if (_seenNameCount >= 0) {
_addSeenName(name);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
protected final void _writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
final int charLen = name.charLength();
if (charLen == 0) {
_writeByte(TOKEN_KEY_EMPTY_STRING);
return;
}
final byte[] bytes = name.asUnquotedUTF8();
final int byteLen = bytes.length;
if (byteLen != charLen) {
_writeFieldNameUnicode(name, bytes);
return;
}
// Then: is it something we can share?
if (_seenNameCount >= 0) {
int ix = _findSeenName(name.getValue());
if (ix >= 0) {
_writeSharedNameReference(ix);
return;
}
}
// Common case: short ASCII name that fits in buffer as is
if (byteLen <= MAX_SHORT_NAME_ASCII_BYTES) {
// output buffer is bigger than what we need, always, so
if ((_outputTail + byteLen) >= _outputEnd) { // need marker byte and actual bytes
_flushBuffer();
}
_outputBuffer[_outputTail++] = (byte) ((TOKEN_PREFIX_KEY_ASCII - 1) + byteLen);
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
} else {
_writeLongAsciiFieldName(bytes);
}
// Also, keep track if we can use back-references (shared names)
if (_seenNameCount >= 0) {
_addSeenName(name.getValue());
}
}
private final void _writeLongAsciiFieldName(byte[] bytes)
throws IOException, JsonGenerationException
{
final int byteLen = bytes.length;
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = TOKEN_KEY_LONG_STRING;
// Ok. Enough room?
if ((_outputTail + byteLen + 1) < _outputEnd) {
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
} else {
_flushBuffer();
// either way, do intermediate copy if name is relatively short
// Need to copy?
if (byteLen < MIN_BUFFER_LENGTH) {
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
} else {
// otherwise, just write as is
if (_outputTail > 0) {
_flushBuffer();
}
_out.write(bytes, 0, byteLen);
}
}
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
protected final void _writeFieldNameUnicode(SerializableString name, byte[] bytes)
throws IOException, JsonGenerationException
{
// Then: is it something we can share?
if (_seenNameCount >= 0) {
int ix = _findSeenName(name.getValue());
if (ix >= 0) {
_writeSharedNameReference(ix);
return;
}
}
final int byteLen = bytes.length;
// Common case: short Unicode name that fits in output buffer
if (byteLen <= MAX_SHORT_NAME_UNICODE_BYTES) {
if ((_outputTail + byteLen) >= _outputEnd) { // need marker byte and actual bytes
_flushBuffer();
}
// note: since 2 is smaller allowed length, offset differs from one used for
_outputBuffer[_outputTail++] = (byte) ((TOKEN_PREFIX_KEY_UNICODE - 2) + byteLen);
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
// Also, keep track if we can use back-references (shared names)
if (_seenNameCount >= 0) {
_addSeenName(name.getValue());
}
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = TOKEN_KEY_LONG_STRING;
// Ok. Enough room?
if ((_outputTail + byteLen + 1) < _outputEnd) {
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
} else {
_flushBuffer();
// either way, do intermediate copy if name is relatively short
// Need to copy?
if (byteLen < MIN_BUFFER_LENGTH) {
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
} else {
// otherwise, just write as is
if (_outputTail > 0) {
_flushBuffer();
}
_out.write(bytes, 0, byteLen);
}
}
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
// Also, keep track if we can use back-references (shared names)
if (_seenNameCount >= 0) {
_addSeenName(name.getValue());
}
}
private final void _writeSharedNameReference(int ix)
throws IOException,JsonGenerationException
{
// 03-Mar-2011, tatu: Related to [JACKSON-525], let's add a sanity check here
if (ix >= _seenNameCount) {
throw new IllegalArgumentException("Internal error: trying to write shared name with index "+ix
+"; but have only seen "+_seenNameCount+" so far!");
}
if (ix < 64) {
_writeByte((byte) (TOKEN_PREFIX_KEY_SHARED_SHORT + ix));
} else {
_writeBytes(((byte) (TOKEN_PREFIX_KEY_SHARED_LONG + (ix >> 8))), (byte) ix);
}
}
/*
/**********************************************************
/* Output method implementations, textual
/**********************************************************
*/
@Override
public void writeString(String text) throws IOException,JsonGenerationException
{
if (text == null) {
writeNull();
return;
}
_verifyValueWrite("write String value");
int len = text.length();
if (len == 0) {
_writeByte(TOKEN_LITERAL_EMPTY_STRING);
return;
}
// Longer string handling off-lined
if (len > MAX_SHARED_STRING_LENGTH_BYTES) {
_writeNonSharedString(text, len);
return;
}
// Then: is it something we can share?
if (_seenStringValueCount >= 0) {
int ix = _findSeenStringValue(text);
if (ix >= 0) {
_writeSharedStringValueReference(ix);
return;
}
}
// possibly short string (but not necessarily)
// first: ensure we have enough space
if ((_outputTail + MIN_BUFFER_FOR_POSSIBLE_SHORT_STRING) >= _outputEnd) {
_flushBuffer();
}
// then let's copy String chars to char buffer, faster than using getChar (measured, profiled)
text.getChars(0, len, _charBuffer, 0);
int origOffset = _outputTail;
++_outputTail; // to leave room for type token
int byteLen = _shortUTF8Encode(_charBuffer, 0, len);
if (byteLen <= MAX_SHORT_VALUE_STRING_BYTES) { // yes, is short indeed
// plus keep reference, if it could be shared:
if (_seenStringValueCount >= 0) {
_addSeenStringValue(text);
}
if (byteLen == len) { // and all ASCII
_outputBuffer[origOffset] = (byte) ((TOKEN_PREFIX_TINY_ASCII - 1) + byteLen);
} else { // not just ASCII
// note: since length 1 can not be used here, value range is offset by 2, not 1
_outputBuffer[origOffset] = (byte) ((TOKEN_PREFIX_TINY_UNICODE - 2) + byteLen);
}
} else { // nope, longer String
_outputBuffer[origOffset] = (byteLen == len) ? TOKEN_BYTE_LONG_STRING_ASCII : TOKEN_BYTE_LONG_STRING_UNICODE;
// and we will need String end marker byte
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
}
private final void _writeSharedStringValueReference(int ix)
throws IOException,JsonGenerationException
{
// 03-Mar-2011, tatu: Related to [JACKSON-525], let's add a sanity check here
if (ix >= _seenStringValueCount) {
throw new IllegalArgumentException("Internal error: trying to write shared String value with index "+ix
+"; but have only seen "+_seenStringValueCount+" so far!");
}
if (ix < 31) { // add 1, as byte 0 is omitted
_writeByte((byte) (TOKEN_PREFIX_SHARED_STRING_SHORT + 1 + ix));
} else {
_writeBytes(((byte) (TOKEN_MISC_SHARED_STRING_LONG + (ix >> 8))), (byte) ix);
}
}
/**
* Helper method called to handle cases where String value to write is known
* to be long enough not to be shareable.
*/
private final void _writeNonSharedString(final String text, final int len)
throws IOException,JsonGenerationException
{
// First: can we at least make a copy to char[]?
if (len > _charBufferLength) { // nope; need to skip copy step (alas; this is slower)
_writeByte(TOKEN_BYTE_LONG_STRING_UNICODE);
_slowUTF8Encode(text);
_writeByte(BYTE_MARKER_END_OF_STRING);
return;
}
text.getChars(0, len, _charBuffer, 0);
// Expansion can be 3x for Unicode; and then there's type byte and end marker, so:
int maxLen = len + len + len + 2;
// Next: does it always fit within output buffer?
if (maxLen > _outputBuffer.length) { // nope
// can't rewrite type buffer, so can't speculate it might be all-ASCII
_writeByte(TOKEN_BYTE_LONG_STRING_UNICODE);
_mediumUTF8Encode(_charBuffer, 0, len);
_writeByte(BYTE_MARKER_END_OF_STRING);
return;
}
if ((_outputTail + maxLen) >= _outputEnd) {
_flushBuffer();
}
int origOffset = _outputTail;
// can't say for sure if it's ASCII or Unicode, so:
_writeByte(TOKEN_BYTE_LONG_STRING_ASCII);
int byteLen = _shortUTF8Encode(_charBuffer, 0, len);
// If not ASCII, fix type:
if (byteLen > len) {
_outputBuffer[origOffset] = TOKEN_BYTE_LONG_STRING_UNICODE;
}
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException
{
// Shared strings are tricky; easiest to just construct String, call the other method
if (len <= MAX_SHARED_STRING_LENGTH_BYTES && _seenStringValueCount >= 0 && len > 0) {
writeString(new String(text, offset, len));
return;
}
_verifyValueWrite("write String value");
if (len == 0) {
_writeByte(TOKEN_LITERAL_EMPTY_STRING);
return;
}
if (len <= MAX_SHORT_VALUE_STRING_BYTES) { // possibly short strings (not necessarily)
// first: ensure we have enough space
if ((_outputTail + MIN_BUFFER_FOR_POSSIBLE_SHORT_STRING) >= _outputEnd) {
_flushBuffer();
}
int origOffset = _outputTail;
++_outputTail; // to leave room for type token
int byteLen = _shortUTF8Encode(text, offset, offset+len);
byte typeToken;
if (byteLen <= MAX_SHORT_VALUE_STRING_BYTES) { // yes, is short indeed
if (byteLen == len) { // and all ASCII
typeToken = (byte) ((TOKEN_PREFIX_TINY_ASCII - 1) + byteLen);
} else { // not just ASCII
typeToken = (byte) ((TOKEN_PREFIX_TINY_UNICODE - 2) + byteLen);
}
} else { // nope, longer non-ASCII Strings
typeToken = TOKEN_BYTE_LONG_STRING_UNICODE;
// and we will need String end marker byte
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
}
// and then sneak in type token now that know the details
_outputBuffer[origOffset] = typeToken;
} else { // "long" String, never shared
// but might still fit within buffer?
int maxLen = len + len + len + 2;
if (maxLen <= _outputBuffer.length) { // yes indeed
if ((_outputTail + maxLen) >= _outputEnd) {
_flushBuffer();
}
int origOffset = _outputTail;
_writeByte(TOKEN_BYTE_LONG_STRING_UNICODE);
int byteLen = _shortUTF8Encode(text, offset, offset+len);
// if it's ASCII, let's revise our type determination (to help decoder optimize)
if (byteLen == len) {
_outputBuffer[origOffset] = TOKEN_BYTE_LONG_STRING_ASCII;
}
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
} else {
_writeByte(TOKEN_BYTE_LONG_STRING_UNICODE);
_mediumUTF8Encode(text, offset, offset+len);
_writeByte(BYTE_MARKER_END_OF_STRING);
}
}
}
@Override
public final void writeString(SerializableString sstr)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write String value");
// First: is it empty?
String str = sstr.getValue();
int len = str.length();
if (len == 0) {
_writeByte(TOKEN_LITERAL_EMPTY_STRING);
return;
}
// Second: something we can share?
if (len <= MAX_SHARED_STRING_LENGTH_BYTES && _seenStringValueCount >= 0) {
int ix = _findSeenStringValue(str);
if (ix >= 0) {
_writeSharedStringValueReference(ix);
return;
}
}
// If not, use pre-encoded version
byte[] raw = sstr.asUnquotedUTF8();
final int byteLen = raw.length;
if (byteLen <= MAX_SHORT_VALUE_STRING_BYTES) { // short string
// first: ensure we have enough space
if ((_outputTail + byteLen + 1) >= _outputEnd) {
_flushBuffer();
}
// ASCII or Unicode?
int typeToken = (byteLen == len)
? ((TOKEN_PREFIX_TINY_ASCII - 1) + byteLen)
: ((TOKEN_PREFIX_TINY_UNICODE - 2) + byteLen)
;
_outputBuffer[_outputTail++] = (byte) typeToken;
System.arraycopy(raw, 0, _outputBuffer, _outputTail, byteLen);
_outputTail += byteLen;
// plus keep reference, if it could be shared:
if (_seenStringValueCount >= 0) {
_addSeenStringValue(sstr.getValue());
}
} else { // "long" String, never shared
// but might still fit within buffer?
byte typeToken = (byteLen == len) ? TOKEN_BYTE_LONG_STRING_ASCII : TOKEN_BYTE_LONG_STRING_UNICODE;
_writeByte(typeToken);
_writeBytes(raw, 0, raw.length);
_writeByte(BYTE_MARKER_END_OF_STRING);
}
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write String value");
// first: is it empty String?
if (len == 0) {
_writeByte(TOKEN_LITERAL_EMPTY_STRING);
return;
}
// Sanity check: shared-strings incompatible with raw String writing
if (_seenStringValueCount >= 0) {
throw new UnsupportedOperationException("Can not use direct UTF-8 write methods when 'Feature.CHECK_SHARED_STRING_VALUES' enabled");
}
/* Other practical limitation is that we do not really know if it might be
* ASCII or not; and figuring it out is rather slow. So, best we can do is
* to declare we do not know it is ASCII (i.e. "is Unicode").
*/
if (len <= MAX_SHARED_STRING_LENGTH_BYTES) { // up to 65 Unicode bytes
// first: ensure we have enough space
if ((_outputTail + len) >= _outputEnd) { // bytes, plus one for type indicator
_flushBuffer();
}
/* 11-Feb-2011, tatu: As per [JACKSON-492], mininum length for "Unicode"
* String is 2; 1 byte length must be ASCII.
*/
if (len == 1) {
_outputBuffer[_outputTail++] = TOKEN_PREFIX_TINY_ASCII; // length of 1 cancels out (len-1)
_outputBuffer[_outputTail++] = text[offset];
} else {
_outputBuffer[_outputTail++] = (byte) ((TOKEN_PREFIX_TINY_UNICODE - 2) + len);
System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
}
} else { // "long" String
// but might still fit within buffer?
int maxLen = len + len + len + 2;
if (maxLen <= _outputBuffer.length) { // yes indeed
if ((_outputTail + maxLen) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = TOKEN_BYTE_LONG_STRING_UNICODE;
System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
_outputBuffer[_outputTail++] = BYTE_MARKER_END_OF_STRING;
} else {
_writeByte(TOKEN_BYTE_LONG_STRING_UNICODE);
_writeBytes(text, offset, len);
_writeByte(BYTE_MARKER_END_OF_STRING);
}
}
}
@Override
public final void writeUTF8String(byte[] text, int offset, int len)
throws IOException, JsonGenerationException
{
// Since no escaping is needed, same as 'writeRawUTF8String'
writeRawUTF8String(text, offset, len);
}
/*
/**********************************************************
/* Output method implementations, unprocessed ("raw")
/**********************************************************
*/
@Override
public void writeRaw(String text) throws IOException, JsonGenerationException {
throw _notSupported();
}
@Override
public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
throw _notSupported();
}
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
throw _notSupported();
}
@Override
public void writeRaw(char c) throws IOException, JsonGenerationException {
throw _notSupported();
}
@Override
public void writeRawValue(String text) throws IOException, JsonGenerationException {
throw _notSupported();
}
@Override
public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
throw _notSupported();
}
@Override
public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
throw _notSupported();
}
/*
/**********************************************************
/* Output method implementations, base64-encoded binary
/**********************************************************
*/
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException
{
if (data == null) {
writeNull();
return;
}
_verifyValueWrite("write Binary value");
if (this.isEnabled(Feature.ENCODE_BINARY_AS_7BIT)) {
_writeByte((byte) TOKEN_MISC_BINARY_7BIT);
_write7BitBinaryWithLength(data, offset, len);
} else {
_writeByte((byte) TOKEN_MISC_BINARY_RAW );
_writePositiveVInt(len);
// raw is dead simple of course:
_writeBytes(data, offset, len);
}
}
/*
/**********************************************************
/* Output method implementations, primitive
/**********************************************************
*/
@Override
public void writeBoolean(boolean state) throws IOException, JsonGenerationException
{
_verifyValueWrite("write boolean value");
if (state) {
_writeByte(TOKEN_LITERAL_TRUE);
} else {
_writeByte(TOKEN_LITERAL_FALSE);
}
}
@Override
public void writeNull() throws IOException, JsonGenerationException
{
_verifyValueWrite("write null value");
_writeByte(TOKEN_LITERAL_NULL);
}
@Override
public void writeNumber(int i) throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
// First things first: let's zigzag encode number
i = SmileUtil.zigzagEncode(i);
// tiny (single byte) or small (type + 6-bit value) number?
if (i <= 0x3F && i >= 0) {
if (i <= 0x1F) { // tiny
_writeByte((byte) (TOKEN_PREFIX_SMALL_INT + i));
return;
}
// nope, just small, 2 bytes (type, 1-byte zigzag value) for 6 bit value
_writeBytes(TOKEN_BYTE_INT_32, (byte) (0x80 + i));
return;
}
// Ok: let's find minimal representation then
byte b0 = (byte) (0x80 + (i & 0x3F));
i >>>= 6;
if (i <= 0x7F) { // 13 bits is enough (== 3 byte total encoding)
_writeBytes(TOKEN_BYTE_INT_32, (byte) i, b0);
return;
}
byte b1 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_writeBytes(TOKEN_BYTE_INT_32, (byte) i, b1, b0);
return;
}
byte b2 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_writeBytes(TOKEN_BYTE_INT_32, (byte) i, b2, b1, b0);
return;
}
// no, need all 5 bytes
byte b3 = (byte) (i & 0x7F);
_writeBytes(TOKEN_BYTE_INT_32, (byte) (i >> 7), b3, b2, b1, b0);
}
@Override
public void writeNumber(long l) throws IOException, JsonGenerationException
{
// First: maybe 32 bits is enough?
if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) {
writeNumber((int) l);
return;
}
_verifyValueWrite("write number");
// Then let's zigzag encode it
l = SmileUtil.zigzagEncode(l);
// Ok, well, we do know that 5 lowest-significant bytes are needed
int i = (int) l;
// 4 can be extracted from lower int
byte b0 = (byte) (0x80 + (i & 0x3F)); // sign bit set in the last byte
byte b1 = (byte) ((i >> 6) & 0x7F);
byte b2 = (byte) ((i >> 13) & 0x7F);
byte b3 = (byte) ((i >> 20) & 0x7F);
// fifth one is split between ints:
l >>>= 27;
byte b4 = (byte) (((int) l) & 0x7F);
// which may be enough?
i = (int) (l >> 7);
if (i == 0) {
_writeBytes(TOKEN_BYTE_INT_64, b4, b3, b2, b1, b0);
return;
}
if (i <= 0x7F) {
_writeBytes(TOKEN_BYTE_INT_64, (byte) i);
_writeBytes(b4, b3, b2, b1, b0);
return;
}
byte b5 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_writeBytes(TOKEN_BYTE_INT_64, (byte) i);
_writeBytes(b5, b4, b3, b2, b1, b0);
return;
}
byte b6 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_writeBytes(TOKEN_BYTE_INT_64, (byte) i, b6);
_writeBytes(b5, b4, b3, b2, b1, b0);
return;
}
byte b7 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_writeBytes(TOKEN_BYTE_INT_64, (byte) i, b7, b6);
_writeBytes(b5, b4, b3, b2, b1, b0);
return;
}
byte b8 = (byte) (i & 0x7F);
i >>= 7;
// must be done, with 10 bytes! (9 * 7 + 6 == 69 bits; only need 63)
_writeBytes(TOKEN_BYTE_INT_64, (byte) i, b8, b7, b6);
_writeBytes(b5, b4, b3, b2, b1, b0);
}
@Override
public void writeNumber(BigInteger v) throws IOException, JsonGenerationException
{
if (v == null) {
writeNull();
return;
}
_verifyValueWrite("write number");
// quite simple: type, and then VInt-len prefixed 7-bit encoded binary data:
_writeByte(TOKEN_BYTE_BIG_INTEGER);
byte[] data = v.toByteArray();
_write7BitBinaryWithLength(data, 0, data.length);
}
@Override
public void writeNumber(double d) throws IOException, JsonGenerationException
{
// Ok, now, we needed token type byte plus 10 data bytes (7 bits each)
_ensureRoomForOutput(11);
_verifyValueWrite("write number");
/* 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems more accurate to use
* exact representation; and possibly faster. However, if there are cases
* where collapsing of NaN was needed (for non-Java clients), this can
* be changed
*/
long l = Double.doubleToRawLongBits(d);
_outputBuffer[_outputTail++] = TOKEN_BYTE_FLOAT_64;
// Handle first 29 bits (single bit first, then 4 x 7 bits)
int hi5 = (int) (l >>> 35);
_outputBuffer[_outputTail+4] = (byte) (hi5 & 0x7F);
hi5 >>= 7;
_outputBuffer[_outputTail+3] = (byte) (hi5 & 0x7F);
hi5 >>= 7;
_outputBuffer[_outputTail+2] = (byte) (hi5 & 0x7F);
hi5 >>= 7;
_outputBuffer[_outputTail+1] = (byte) (hi5 & 0x7F);
hi5 >>= 7;
_outputBuffer[_outputTail] = (byte) hi5;
_outputTail += 5;
// Then split byte (one that crosses lo/hi int boundary), 7 bits
{
int mid = (int) (l >> 28);
_outputBuffer[_outputTail++] = (byte) (mid & 0x7F);
}
// and then last 4 bytes (28 bits)
int lo4 = (int) l;
_outputBuffer[_outputTail+3] = (byte) (lo4 & 0x7F);
lo4 >>= 7;
_outputBuffer[_outputTail+2] = (byte) (lo4 & 0x7F);
lo4 >>= 7;
_outputBuffer[_outputTail+1] = (byte) (lo4 & 0x7F);
lo4 >>= 7;
_outputBuffer[_outputTail] = (byte) (lo4 & 0x7F);
_outputTail += 4;
}
@Override
public void writeNumber(float f) throws IOException, JsonGenerationException
{
// Ok, now, we needed token type byte plus 5 data bytes (7 bits each)
_ensureRoomForOutput(6);
_verifyValueWrite("write number");
/* 17-Apr-2010, tatu: could also use 'floatToIntBits', but it seems more accurate to use
* exact representation; and possibly faster. However, if there are cases
* where collapsing of NaN was needed (for non-Java clients), this can
* be changed
*/
int i = Float.floatToRawIntBits(f);
_outputBuffer[_outputTail++] = TOKEN_BYTE_FLOAT_32;
_outputBuffer[_outputTail+4] = (byte) (i & 0x7F);
i >>= 7;
_outputBuffer[_outputTail+3] = (byte) (i & 0x7F);
i >>= 7;
_outputBuffer[_outputTail+2] = (byte) (i & 0x7F);
i >>= 7;
_outputBuffer[_outputTail+1] = (byte) (i & 0x7F);
i >>= 7;
_outputBuffer[_outputTail] = (byte) (i & 0x7F);
_outputTail += 5;
}
@Override
public void writeNumber(BigDecimal dec) throws IOException, JsonGenerationException
{
if (dec == null) {
writeNull();
return;
}
_verifyValueWrite("write number");
_writeByte(TOKEN_BYTE_BIG_DECIMAL);
int scale = dec.scale();
// Ok, first output scale as VInt
_writeSignedVInt(scale);
BigInteger unscaled = dec.unscaledValue();
byte[] data = unscaled.toByteArray();
// And then binary data in "safe" mode (7-bit values)
_write7BitBinaryWithLength(data, 0, data.length);
}
@Override
public void writeNumber(String encodedValue) throws IOException,JsonGenerationException, UnsupportedOperationException
{
/* 17-Apr-2010, tatu: Could try parsing etc; but for now let's not bother, it could
* just be some non-standard representation that caller wants to pass
*/
throw _notSupported();
}
/*
/**********************************************************
/* Implementations for other methods
/**********************************************************
*/
@Override
protected final void _verifyValueWrite(String typeMsg)
throws IOException, JsonGenerationException
{
int status = _writeContext.writeValue();
if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
_reportError("Can not "+typeMsg+", expecting field name");
}
}
/*
/**********************************************************
/* Low-level output handling
/**********************************************************
*/
@Override
public final void flush() throws IOException
{
_flushBuffer();
if (isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)) {
_out.flush();
}
}
@Override
public void close() throws IOException
{
/* 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(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)) {
while (true) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt.inArray()) {
writeEndArray();
} else if (ctxt.inObject()) {
writeEndObject();
} else {
break;
}
}
}
boolean wasClosed = _closed;
super.close();
if (!wasClosed && isEnabled(Feature.WRITE_END_MARKER)) {
_writeByte(BYTE_MARKER_END_OF_CONTENT);
}
_flushBuffer();
if (_ioContext.isResourceManaged() || isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)) {
_out.close();
} else {
// If we can't close it, we should at least flush
_out.flush();
}
// Internal buffer(s) generator has can now be released as well
_releaseBuffers();
}
/*
/**********************************************************
/* Internal methods, UTF-8 encoding
/**********************************************************
*/
/**
* Helper method called when the whole character sequence is known to
* fit in the output buffer regardless of UTF-8 expansion.
*/
private final int _shortUTF8Encode(char[] str, int i, int end)
{
// First: let's see if it's all ASCII: that's rather fast
int ptr = _outputTail;
final byte[] outBuf = _outputBuffer;
do {
int c = str[i];
if (c > 0x7F) {
return _shortUTF8Encode2(str, i, end, ptr);
}
outBuf[ptr++] = (byte) c;
} while (++i < end);
int codedLen = ptr - _outputTail;
_outputTail = ptr;
return codedLen;
}
/**
* Helper method called when the whole character sequence is known to
* fit in the output buffer, but not all characters are single-byte (ASCII)
* characters.
*/
private final int _shortUTF8Encode2(char[] str, int i, int end, int outputPtr)
{
final byte[] outBuf = _outputBuffer;
while (i < end) {
int c = str[i++];
if (c <= 0x7F) {
outBuf[outputPtr++] = (byte) c;
continue;
}
// Nope, multi-byte:
if (c < 0x800) { // 2-byte
outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6));
outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f));
continue;
}
// 3 or 4 bytes (surrogate)
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character
outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12));
outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f));
continue;
}
// Yup, a surrogate pair
if (c > SURR1_LAST) { // must be from first range; second won't do
_throwIllegalSurrogate(c);
}
// ... meaning it must have a pair
if (i >= end) {
_throwIllegalSurrogate(c);
}
c = _convertSurrogate(c, str[i++]);
if (c > 0x10FFFF) { // illegal in JSON as well as in XML
_throwIllegalSurrogate(c);
}
outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18));
outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f));
}
int codedLen = outputPtr - _outputTail;
_outputTail = outputPtr;
return codedLen;
}
private void _slowUTF8Encode(String str) throws IOException
{
final int len = str.length();
int inputPtr = 0;
final int bufferEnd = _outputEnd - 4;
output_loop:
for (; inputPtr < len; ) {
/* First, let's ensure we can output at least 4 bytes
* (longest UTF-8 encoded codepoint):
*/
if (_outputTail >= bufferEnd) {
_flushBuffer();
}
int c = str.charAt(inputPtr++);
// And then see if we have an ASCII char:
if (c <= 0x7F) { // If so, can do a tight inner loop:
_outputBuffer[_outputTail++] = (byte)c;
// Let's calc how many ASCII chars we can copy at most:
int maxInCount = (len - inputPtr);
int maxOutCount = (bufferEnd - _outputTail);
if (maxInCount > maxOutCount) {
maxInCount = maxOutCount;
}
maxInCount += inputPtr;
ascii_loop:
while (true) {
if (inputPtr >= maxInCount) { // done with max. ascii seq
continue output_loop;
}
c = str.charAt(inputPtr++);
if (c > 0x7F) {
break ascii_loop;
}
_outputBuffer[_outputTail++] = (byte) c;
}
}
// Nope, multi-byte:
if (c < 0x800) { // 2-byte
_outputBuffer[_outputTail++] = (byte) (0xc0 | (c >> 6));
_outputBuffer[_outputTail++] = (byte) (0x80 | (c & 0x3f));
} else { // 3 or 4 bytes
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) {
_outputBuffer[_outputTail++] = (byte) (0xe0 | (c >> 12));
_outputBuffer[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
_outputBuffer[_outputTail++] = (byte) (0x80 | (c & 0x3f));
continue;
}
// Yup, a surrogate:
if (c > SURR1_LAST) { // must be from first range
_throwIllegalSurrogate(c);
}
// and if so, followed by another from next range
if (inputPtr >= len) {
_throwIllegalSurrogate(c);
}
c = _convertSurrogate(c, str.charAt(inputPtr++));
if (c > 0x10FFFF) { // illegal, as per RFC 4627
_throwIllegalSurrogate(c);
}
_outputBuffer[_outputTail++] = (byte) (0xf0 | (c >> 18));
_outputBuffer[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f));
_outputBuffer[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
_outputBuffer[_outputTail++] = (byte) (0x80 | (c & 0x3f));
}
}
}
private void _mediumUTF8Encode(char[] str, int inputPtr, int inputEnd) throws IOException
{
final int bufferEnd = _outputEnd - 4;
output_loop:
while (inputPtr < inputEnd) {
/* First, let's ensure we can output at least 4 bytes
* (longest UTF-8 encoded codepoint):
*/
if (_outputTail >= bufferEnd) {
_flushBuffer();
}
int c = str[inputPtr++];
// And then see if we have an ASCII char:
if (c <= 0x7F) { // If so, can do a tight inner loop:
_outputBuffer[_outputTail++] = (byte)c;
// Let's calc how many ASCII chars we can copy at most:
int maxInCount = (inputEnd - inputPtr);
int maxOutCount = (bufferEnd - _outputTail);
if (maxInCount > maxOutCount) {
maxInCount = maxOutCount;
}
maxInCount += inputPtr;
ascii_loop:
while (true) {
if (inputPtr >= maxInCount) { // done with max. ascii seq
continue output_loop;
}
c = str[inputPtr++];
if (c > 0x7F) {
break ascii_loop;
}
_outputBuffer[_outputTail++] = (byte) c;
}
}
// Nope, multi-byte:
if (c < 0x800) { // 2-byte
_outputBuffer[_outputTail++] = (byte) (0xc0 | (c >> 6));
_outputBuffer[_outputTail++] = (byte) (0x80 | (c & 0x3f));
} else { // 3 or 4 bytes
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) {
_outputBuffer[_outputTail++] = (byte) (0xe0 | (c >> 12));
_outputBuffer[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
_outputBuffer[_outputTail++] = (byte) (0x80 | (c & 0x3f));
continue;
}
// Yup, a surrogate:
if (c > SURR1_LAST) { // must be from first range
_throwIllegalSurrogate(c);
}
// and if so, followed by another from next range
if (inputPtr >= inputEnd) {
_throwIllegalSurrogate(c);
}
c = _convertSurrogate(c, str[inputPtr++]);
if (c > 0x10FFFF) { // illegal, as per RFC 4627
_throwIllegalSurrogate(c);
}
_outputBuffer[_outputTail++] = (byte) (0xf0 | (c >> 18));
_outputBuffer[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f));
_outputBuffer[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
_outputBuffer[_outputTail++] = (byte) (0x80 | (c & 0x3f));
}
}
}
/**
* Method called to calculate UTF codepoint, from a surrogate pair.
*/
private int _convertSurrogate(int firstPart, int secondPart)
{
// Ok, then, is the second part valid?
if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
}
return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
}
private void _throwIllegalSurrogate(int code)
{
if (code > 0x10FFFF) { // over max?
throw new IllegalArgumentException("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?)
throw new IllegalArgumentException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
}
throw new IllegalArgumentException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
}
// should we ever get this?
throw new IllegalArgumentException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
}
/*
/**********************************************************
/* Internal methods, writing bytes
/**********************************************************
*/
private final void _ensureRoomForOutput(int needed) throws IOException
{
if ((_outputTail + needed) >= _outputEnd) {
_flushBuffer();
}
}
private final void _writeByte(byte b) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b;
}
private final void _writeBytes(byte b1, byte b2) throws IOException
{
if ((_outputTail + 1) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b2;
}
private final void _writeBytes(byte b1, byte b2, byte b3) throws IOException
{
if ((_outputTail + 2) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b2;
_outputBuffer[_outputTail++] = b3;
}
private final void _writeBytes(byte b1, byte b2, byte b3, byte b4) throws IOException
{
if ((_outputTail + 3) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b2;
_outputBuffer[_outputTail++] = b3;
_outputBuffer[_outputTail++] = b4;
}
private final void _writeBytes(byte b1, byte b2, byte b3, byte b4, byte b5) throws IOException
{
if ((_outputTail + 4) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b2;
_outputBuffer[_outputTail++] = b3;
_outputBuffer[_outputTail++] = b4;
_outputBuffer[_outputTail++] = b5;
}
private final void _writeBytes(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6) throws IOException
{
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b2;
_outputBuffer[_outputTail++] = b3;
_outputBuffer[_outputTail++] = b4;
_outputBuffer[_outputTail++] = b5;
_outputBuffer[_outputTail++] = b6;
}
private final void _writeBytes(byte[] data, int offset, int len) throws IOException
{
if (len == 0) {
return;
}
if ((_outputTail + len) >= _outputEnd) {
_writeBytesLong(data, offset, len);
return;
}
// common case, non-empty, fits in just fine:
System.arraycopy(data, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
}
private final void _writeBytesLong(byte[] data, int offset, int len) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
while (true) {
int currLen = Math.min(len, (_outputEnd - _outputTail));
System.arraycopy(data, offset, _outputBuffer, _outputTail, currLen);
_outputTail += currLen;
if ((len -= currLen) == 0) {
break;
}
offset += currLen;
_flushBuffer();
}
}
/**
* Helper method for writing a 32-bit positive (really 31-bit then) value.
* Value is NOT zigzag encoded (since there is no sign bit to worry about)
*/
private void _writePositiveVInt(int i) throws IOException
{
// At most 5 bytes (4 * 7 + 6 bits == 34 bits)
_ensureRoomForOutput(5);
byte b0 = (byte) (0x80 + (i & 0x3F));
i >>= 6;
if (i <= 0x7F) { // 6 or 13 bits is enough (== 2 or 3 byte total encoding)
if (i > 0) {
_outputBuffer[_outputTail++] = (byte) i;
}
_outputBuffer[_outputTail++] = b0;
return;
}
byte b1 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_outputBuffer[_outputTail++] = (byte) i;
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b0;
} else {
byte b2 = (byte) (i & 0x7F);
i >>= 7;
if (i <= 0x7F) {
_outputBuffer[_outputTail++] = (byte) i;
_outputBuffer[_outputTail++] = b2;
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b0;
} else {
byte b3 = (byte) (i & 0x7F);
_outputBuffer[_outputTail++] = (byte) (i >> 7);
_outputBuffer[_outputTail++] = b3;
_outputBuffer[_outputTail++] = b2;
_outputBuffer[_outputTail++] = b1;
_outputBuffer[_outputTail++] = b0;
}
}
}
/**
* Helper method for writing 32-bit signed value, using
* "zig zag encoding" (see protocol buffers for explanation -- basically,
* sign bit is moved as LSB, rest of value shifted left by one)
* coupled with basic variable length encoding
*/
private void _writeSignedVInt(int input) throws IOException
{
_writePositiveVInt(SmileUtil.zigzagEncode(input));
}
protected void _write7BitBinaryWithLength(byte[] data, int offset, int len) throws IOException
{
_writePositiveVInt(len);
// first, let's handle full 7-byte chunks
while (len >= 7) {
if ((_outputTail + 8) >= _outputEnd) {
_flushBuffer();
}
int i = data[offset++]; // 1st byte
_outputBuffer[_outputTail++] = (byte) ((i >> 1) & 0x7F);
i = (i << 8) | (data[offset++] & 0xFF); // 2nd
_outputBuffer[_outputTail++] = (byte) ((i >> 2) & 0x7F);
i = (i << 8) | (data[offset++] & 0xFF); // 3rd
_outputBuffer[_outputTail++] = (byte) ((i >> 3) & 0x7F);
i = (i << 8) | (data[offset++] & 0xFF); // 4th
_outputBuffer[_outputTail++] = (byte) ((i >> 4) & 0x7F);
i = (i << 8) | (data[offset++] & 0xFF); // 5th
_outputBuffer[_outputTail++] = (byte) ((i >> 5) & 0x7F);
i = (i << 8) | (data[offset++] & 0xFF); // 6th
_outputBuffer[_outputTail++] = (byte) ((i >> 6) & 0x7F);
i = (i << 8) | (data[offset++] & 0xFF); // 7th
_outputBuffer[_outputTail++] = (byte) ((i >> 7) & 0x7F);
_outputBuffer[_outputTail++] = (byte) (i & 0x7F);
len -= 7;
}
// and then partial piece, if any
if (len > 0) {
// up to 6 bytes to output, resulting in at most 7 bytes (which can encode 49 bits)
if ((_outputTail + 7) >= _outputEnd) {
_flushBuffer();
}
int i = data[offset++];
_outputBuffer[_outputTail++] = (byte) ((i >> 1) & 0x7F);
if (len > 1) {
i = ((i & 0x01) << 8) | (data[offset++] & 0xFF); // 2nd
_outputBuffer[_outputTail++] = (byte) ((i >> 2) & 0x7F);
if (len > 2) {
i = ((i & 0x03) << 8) | (data[offset++] & 0xFF); // 3rd
_outputBuffer[_outputTail++] = (byte) ((i >> 3) & 0x7F);
if (len > 3) {
i = ((i & 0x07) << 8) | (data[offset++] & 0xFF); // 4th
_outputBuffer[_outputTail++] = (byte) ((i >> 4) & 0x7F);
if (len > 4) {
i = ((i & 0x0F) << 8) | (data[offset++] & 0xFF); // 5th
_outputBuffer[_outputTail++] = (byte) ((i >> 5) & 0x7F);
if (len > 5) {
i = ((i & 0x1F) << 8) | (data[offset++] & 0xFF); // 6th
_outputBuffer[_outputTail++] = (byte) ((i >> 6) & 0x7F);
_outputBuffer[_outputTail++] = (byte) (i & 0x3F); // last 6 bits
} else {
_outputBuffer[_outputTail++] = (byte) (i & 0x1F); // last 5 bits
}
} else {
_outputBuffer[_outputTail++] = (byte) (i & 0x0F); // last 4 bits
}
} else {
_outputBuffer[_outputTail++] = (byte) (i & 0x07); // last 3 bits
}
} else {
_outputBuffer[_outputTail++] = (byte) (i & 0x03); // last 2 bits
}
} else {
_outputBuffer[_outputTail++] = (byte) (i & 0x01); // last bit
}
}
}
/*
/**********************************************************
/* Internal methods, buffer handling
/**********************************************************
*/
@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);
}
/* Ok: since clearing up of larger arrays is much slower,
* let's only recycle default-sized buffers...
*/
{
SharedStringNode[] nameBuf = _seenNames;
if (nameBuf != null && nameBuf.length == SmileBufferRecycler.DEFAULT_NAME_BUFFER_LENGTH) {
_seenNames = null;
/* 28-Jun-2011, tatu: With 1.9, caller needs to clear the buffer; and note
* that since it's a hash area, must clear all
*/
if (_seenNameCount > 0) {
Arrays.fill(nameBuf, null);
}
_smileBufferRecycler.releaseSeenNamesBuffer(nameBuf);
}
}
{
SharedStringNode[] valueBuf = _seenStringValues;
if (valueBuf != null && valueBuf.length == SmileBufferRecycler.DEFAULT_STRING_VALUE_BUFFER_LENGTH) {
_seenStringValues = null;
/* 28-Jun-2011, tatu: With 1.9, caller needs to clear the buffer; and note
* that since it's a hash area, must clear all
*/
if (_seenStringValueCount > 0) {
Arrays.fill(valueBuf, null);
}
_smileBufferRecycler.releaseSeenStringValuesBuffer(valueBuf);
}
}
}
protected final void _flushBuffer() throws IOException
{
if (_outputTail > 0) {
_bytesWritten += _outputTail;
_out.write(_outputBuffer, 0, _outputTail);
_outputTail = 0;
}
}
/*
/**********************************************************
/* Internal methods, handling shared string "maps"
/**********************************************************
*/
private final int _findSeenName(String name)
{
int hash = name.hashCode();
SharedStringNode head = _seenNames[hash & (_seenNames.length-1)];
if (head == null) {
return -1;
}
SharedStringNode node = head;
// first, identity match; assuming most of the time we get intern()ed String
// And do unrolled initial check; 90+% likelihood head node has all info we need:
if (node.value == name) {
return node.index;
}
while ((node = node.next) != null) {
if (node.value == name) {
return node.index;
}
}
// If not, equality check; we already know head is not null
node = head;
do {
String value = node.value;
if (value.hashCode() == hash && value.equals(name)) {
return node.index;
}
node = node.next;
} while (node != null);
return -1;
}
private final void _addSeenName(String name)
{
// first: do we need to expand?
if (_seenNameCount == _seenNames.length) {
if (_seenNameCount == MAX_SHARED_NAMES) { // we are too full, restart from empty
Arrays.fill(_seenNames, null);
_seenNameCount = 0;
} else { // we always start with modest default size (like 64), so expand to full
SharedStringNode[] old = _seenNames;
_seenNames = new SharedStringNode[MAX_SHARED_NAMES];
final int mask = MAX_SHARED_NAMES-1;
for (SharedStringNode node : old) {
for (; node != null; node = node.next) {
int ix = node.value.hashCode() & mask;
node.next = _seenNames[ix];
_seenNames[ix] = node;
}
}
}
}
// other than that, just slap it there
int ix = name.hashCode() & (_seenNames.length-1);
_seenNames[ix] = new SharedStringNode(name, _seenNameCount, _seenNames[ix]);
++_seenNameCount;
}
private final int _findSeenStringValue(String text)
{
int hash = text.hashCode();
SharedStringNode head = _seenStringValues[hash & (_seenStringValues.length-1)];
if (head != null) {
SharedStringNode node = head;
// first, identity match; assuming most of the time we get intern()ed String
do {
if (node.value == text) {
return node.index;
}
node = node.next;
} while (node != null);
// and then comparison, if no match yet
node = head;
do {
String value = node.value;
if (value.hashCode() == hash && value.equals(text)) {
return node.index;
}
node = node.next;
} while (node != null);
}
return -1;
}
private final void _addSeenStringValue(String text)
{
// first: do we need to expand?
if (_seenStringValueCount == _seenStringValues.length) {
if (_seenStringValueCount == MAX_SHARED_STRING_VALUES) { // we are too full, restart from empty
Arrays.fill(_seenStringValues, null);
_seenStringValueCount = 0;
} else { // we always start with modest default size (like 64), so expand to full
SharedStringNode[] old = _seenStringValues;
_seenStringValues = new SharedStringNode[MAX_SHARED_STRING_VALUES];
final int mask = MAX_SHARED_STRING_VALUES-1;
for (SharedStringNode node : old) {
for (; node != null; node = node.next) {
int ix = node.value.hashCode() & mask;
node.next = _seenStringValues[ix];
_seenStringValues[ix] = node;
}
}
}
}
// other than that, just slap it there
int ix = text.hashCode() & (_seenStringValues.length-1);
_seenStringValues[ix] = new SharedStringNode(text, _seenStringValueCount, _seenStringValues[ix]);
++_seenStringValueCount;
}
/*
/**********************************************************
/* Internal methods, error reporting
/**********************************************************
*/
/**
* Method for accessing offset of the next byte within the whole output
* stream that this generator has produced.
*/
protected long outputOffset() {
return _bytesWritten + _outputTail;
}
protected UnsupportedOperationException _notSupported() {
return new UnsupportedOperationException();
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/Tool.java 0000644 0001750 0001750 00000012751 11655120726 026151 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.smile.SmileFactory;
/**
* Simple command-line utility that can be used to encode JSON as Smile, or
* decode JSON from Smile: direction is indicated by single command-line
* option of either "-e" (encode) or "-d" (decode).
*
* @author tatu
*
* @since 1.6.2
*/
public class Tool
{
public final static String SUFFIX = ".lzf";
public final JsonFactory jsonFactory;
public final SmileFactory smileFactory;
public Tool()
{
jsonFactory = new JsonFactory();
smileFactory = new SmileFactory();
// check all shared refs (-> small size); add header, not trailing marker; do not use raw binary
smileFactory.configure(SmileGenerator.Feature.CHECK_SHARED_NAMES, true);
smileFactory.configure(SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES, true);
smileFactory.configure(SmileGenerator.Feature.ENCODE_BINARY_AS_7BIT, true);
smileFactory.configure(SmileGenerator.Feature.WRITE_HEADER, true);
smileFactory.configure(SmileGenerator.Feature.WRITE_END_MARKER, false);
// also: do not require header
smileFactory.configure(SmileParser.Feature.REQUIRE_HEADER, false);
}
private void process(String[] args) throws IOException
{
String oper = null;
String filename = null;
if (args.length == 2) {
oper = args[0];
filename = args[1];
} else if (args.length == 1) {
oper = args[0];
} else {
showUsage();
}
boolean encode = "-e".equals(oper);
if (encode) {
encode(inputStream(filename));
} else if ("-d".equals(oper)) {
decode(inputStream(filename));
} else if ("-v".equals(oper)) {
// need to read twice (encode, verify/compare)
verify(inputStream(filename), inputStream(filename));
} else {
showUsage();
}
}
private InputStream inputStream(String filename) throws IOException
{
// if no argument given, read from stdin
if (filename == null) {
return System.in;
}
File src = new File(filename);
if (!src.exists()) {
System.err.println("File '"+filename+"' does not exist.");
System.exit(1);
}
return new FileInputStream(src);
}
private void decode(InputStream in) throws IOException
{
JsonParser jp = smileFactory.createJsonParser(in);
JsonGenerator jg = jsonFactory.createJsonGenerator(System.out, JsonEncoding.UTF8);
while (true) {
/* Just one trick: since Smile can have segments (multiple 'documents' in output
* stream), we should not stop at first end marker, only bail out if two are seen
*/
if (jp.nextToken() == null) {
if (jp.nextToken() == null) {
break;
}
}
jg.copyCurrentEvent(jp);
}
jp.close();
jg.close();
}
private void encode(InputStream in) throws IOException
{
JsonParser jp = jsonFactory.createJsonParser(in);
JsonGenerator jg = smileFactory.createJsonGenerator(System.out, JsonEncoding.UTF8);
while ((jp.nextToken()) != null) {
jg.copyCurrentEvent(jp);
}
jp.close();
jg.close();
}
private void verify(InputStream in, InputStream in2) throws IOException
{
JsonParser jp = jsonFactory.createJsonParser(in);
ByteArrayOutputStream bytes = new ByteArrayOutputStream(4000);
JsonGenerator jg = smileFactory.createJsonGenerator(bytes, JsonEncoding.UTF8);
// First, read, encode in memory buffer
while ((jp.nextToken()) != null) {
jg.copyCurrentEvent(jp);
}
jp.close();
jg.close();
// and then re-read both, verify
jp = jsonFactory.createJsonParser(in2);
byte[] smile = bytes.toByteArray();
JsonParser jp2 = smileFactory.createJsonParser(smile);
JsonToken t;
int count = 0;
while ((t = jp.nextToken()) != null) {
JsonToken t2 = jp2.nextToken();
++count;
if (t != t2) {
throw new IOException("Input and encoded differ, token #"+count+"; expected "+t+", got "+t2);
}
// also, need to have same texts...
String text1 = jp.getText();
String text2 = jp2.getText();
if (!text1.equals(text2)) {
throw new IOException("Input and encoded differ, token #"+count+"; expected text '"+text1+"', got '"+text2+"'");
}
}
System.out.println("OK: verified "+count+" tokens (from "+smile.length+" bytes of Smile encoded data), input and encoded contents are identical");
}
protected void showUsage()
{
System.err.println("Usage: java "+getClass().getName()+" -e/-d [file]");
System.err.println(" (if no file given, reads from stdin -- always writes to stdout)");
System.err.println(" -d: decode Smile encoded input as JSON");
System.err.println(" -e: encode JSON (text) input as Smile");
System.err.println(" -v: encode JSON (text) input as Smile; read back, verify, do not write out");
System.exit(1);
}
public static void main(String[] args) throws IOException {
new Tool().process(args);
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileFactory.java 0000644 0001750 0001750 00000030367 11655120726 027640 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
import java.io.*;
import java.net.URL;
import org.codehaus.jackson.*;
import org.codehaus.jackson.format.InputAccessor;
import org.codehaus.jackson.format.MatchStrength;
import org.codehaus.jackson.io.IOContext;
/**
* Factory used for constructing {@link SmileParser} and {@link SmileGenerator}
* instances; both of which handle
* Smile encoded data.
*
* Extends {@link JsonFactory} mostly so that users can actually use it in place
* of regular non-Smile factory instances.
*
* Note on using non-byte-based sources/targets (char based, like
* {@link java.io.Reader} and {@link java.io.Writer}): these can not be
* used for Smile-format documents, and thus will either downgrade to
* textual JSON (when parsing), or throw exception (when trying to create
* generator).
*
* @author tatu
*
* @since 1.6
*/
public class SmileFactory extends JsonFactory
{
/**
* Name used to identify Smile format.
* (and returned by {@link #getFormatName()}
*/
public final static String FORMAT_NAME_SMILE = "Smile";
/**
* Bitfield (set of flags) of all parser features that are enabled
* by default.
*/
final static int DEFAULT_SMILE_PARSER_FEATURE_FLAGS = SmileParser.Feature.collectDefaults();
/**
* Bitfield (set of flags) of all generator features that are enabled
* by default.
*/
final static int DEFAULT_SMILE_GENERATOR_FEATURE_FLAGS = SmileGenerator.Feature.collectDefaults();
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Whether non-supported methods (ones trying to output using
* char-based targets like {@link java.io.Writer}, for example)
* should be delegated to regular Jackson JSON processing
* (if set to true); or throw {@link UnsupportedOperationException}
* (if set to false)
*/
protected boolean _cfgDelegateToTextual;
protected int _smileParserFeatures = DEFAULT_SMILE_PARSER_FEATURE_FLAGS;
protected int _smileGeneratorFeatures = DEFAULT_SMILE_GENERATOR_FEATURE_FLAGS;
/*
/**********************************************************
/* Factory construction, configuration
/**********************************************************
*/
/**
* 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 SmileFactory() { this(null); }
public SmileFactory(ObjectCodec oc) { super(oc); }
public void delegateToTextual(boolean state) {
_cfgDelegateToTextual = state;
}
/*
/**********************************************************
/* Format detection functionality (since 1.8)
/**********************************************************
*/
@Override
public String getFormatName()
{
return FORMAT_NAME_SMILE;
}
/**
* Sub-classes need to override this method (as of 1.8)
*/
@Override
public MatchStrength hasFormat(InputAccessor acc) throws IOException
{
return SmileParserBootstrapper.hasSmileFormat(acc);
}
/*
/**********************************************************
/* Configuration, parser settings
/**********************************************************
*/
/**
* Method for enabling or disabling specified parser feature
* (check {@link SmileParser.Feature} for list of features)
*/
public final SmileFactory configure(SmileParser.Feature f, boolean state)
{
if (state) {
enable(f);
} else {
disable(f);
}
return this;
}
/**
* Method for enabling specified parser feature
* (check {@link SmileParser.Feature} for list of features)
*/
public SmileFactory enable(SmileParser.Feature f) {
_smileParserFeatures |= f.getMask();
return this;
}
/**
* Method for disabling specified parser features
* (check {@link SmileParser.Feature} for list of features)
*/
public SmileFactory disable(SmileParser.Feature f) {
_smileParserFeatures &= ~f.getMask();
return this;
}
/**
* Checked whether specified parser feature is enabled.
*/
public final boolean isEnabled(SmileParser.Feature f) {
return (_smileParserFeatures & f.getMask()) != 0;
}
/*
/**********************************************************
/* Configuration, generator settings
/**********************************************************
*/
/**
* Method for enabling or disabling specified generator feature
* (check {@link org.codehaus.jackson.JsonGenerator.Feature} for list of features)
*
* @since 1.2
*/
public final SmileFactory configure(SmileGenerator.Feature f, boolean state) {
if (state) {
enable(f);
} else {
disable(f);
}
return this;
}
/**
* Method for enabling specified generator features
* (check {@link org.codehaus.jackson.JsonGenerator.Feature} for list of features)
*/
public SmileFactory enable(SmileGenerator.Feature f) {
_smileGeneratorFeatures |= f.getMask();
return this;
}
/**
* Method for disabling specified generator feature
* (check {@link org.codehaus.jackson.JsonGenerator.Feature} for list of features)
*/
public SmileFactory disable(SmileGenerator.Feature f) {
_smileGeneratorFeatures &= ~f.getMask();
return this;
}
/**
* Check whether specified generator feature is enabled.
*/
public final boolean isEnabled(SmileGenerator.Feature f) {
return (_smileGeneratorFeatures & f.getMask()) != 0;
}
/*
/**********************************************************
/* Overridden parser factory methods
/**********************************************************
*/
@Override
public SmileParser createJsonParser(File f)
throws IOException, JsonParseException
{
return _createJsonParser(new FileInputStream(f), _createContext(f, true));
}
@Override
public SmileParser createJsonParser(URL url)
throws IOException, JsonParseException
{
return _createJsonParser(_optimizedStreamFromURL(url), _createContext(url, true));
}
@Override
public SmileParser createJsonParser(InputStream in)
throws IOException, JsonParseException
{
return _createJsonParser(in, _createContext(in, false));
}
//public JsonParser createJsonParser(Reader r)
@Override
public SmileParser createJsonParser(byte[] data)
throws IOException, JsonParseException
{
IOContext ctxt = _createContext(data, true);
return _createJsonParser(data, 0, data.length, ctxt);
}
@Override
public SmileParser createJsonParser(byte[] data, int offset, int len)
throws IOException, JsonParseException
{
return _createJsonParser(data, offset, len, _createContext(data, true));
}
/*
/**********************************************************
/* Overridden generator factory methods
/**********************************************************
*/
/**
*
* note: co-variant return type
*/
@Override
public SmileGenerator createJsonGenerator(OutputStream out, JsonEncoding enc)
throws IOException
{
return createJsonGenerator(out);
}
/**
* Since Smile format always uses UTF-8 internally, no encoding need
* to be passed to this method.
*/
@Override
public SmileGenerator createJsonGenerator(OutputStream out) throws IOException
{
// false -> we won't manage the stream unless explicitly directed to
IOContext ctxt = _createContext(out, false);
return _createJsonGenerator(out, ctxt);
}
/*
/******************************************************
/* Overridden internal factory methods
/******************************************************
*/
//protected IOContext _createContext(Object srcRef, boolean resourceManaged)
/**
* Overridable factory method that actually instantiates desired
* parser.
*/
@Override
protected SmileParser _createJsonParser(InputStream in, IOContext ctxt)
throws IOException, JsonParseException
{
return new SmileParserBootstrapper(ctxt, in).constructParser(_parserFeatures,
_smileParserFeatures, _objectCodec, _rootByteSymbols);
}
/**
* Overridable factory method that actually instantiates desired
* parser.
*/
@Override
protected JsonParser _createJsonParser(Reader r, IOContext ctxt)
throws IOException, JsonParseException
{
if (_cfgDelegateToTextual) {
return super._createJsonParser(r, ctxt);
}
throw new UnsupportedOperationException("Can not create generator for non-byte-based target");
}
/**
* Overridable factory method that actually instantiates desired
* parser.
*/
@Override
protected SmileParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt)
throws IOException, JsonParseException
{
return new SmileParserBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures,
_smileParserFeatures, _objectCodec, _rootByteSymbols);
}
/**
* Overridable factory method that actually instantiates desired
* generator.
*/
@Override
protected JsonGenerator _createJsonGenerator(Writer out, IOContext ctxt)
throws IOException
{
if (_cfgDelegateToTextual) {
return super._createJsonGenerator(out, ctxt);
}
throw new UnsupportedOperationException("Can not create generator for non-byte-based target");
}
//public BufferRecycler _getBufferRecycler()
@Override
protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException
{
if (_cfgDelegateToTextual) {
return super._createWriter(out, enc, ctxt);
}
throw new UnsupportedOperationException("Can not create generator for non-byte-based target");
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected SmileGenerator _createJsonGenerator(OutputStream out, IOContext ctxt)
throws IOException
{
int feats = _smileGeneratorFeatures;
/* One sanity check: MUST write header if shared string values setting is enabled,
* or quoting of binary data disabled.
* But should we force writing, or throw exception, if settings are in conflict?
* For now, let's error out...
*/
SmileGenerator gen = new SmileGenerator(ctxt, _generatorFeatures, feats, _objectCodec, out);
if ((feats & SmileGenerator.Feature.WRITE_HEADER.getMask()) != 0) {
gen.writeHeader();
} else {
if ((feats & SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES.getMask()) != 0) {
throw new JsonGenerationException(
"Inconsistent settings: WRITE_HEADER disabled, but CHECK_SHARED_STRING_VALUES enabled; can not construct generator"
+" due to possible data loss (either enable WRITE_HEADER, or disable CHECK_SHARED_STRING_VALUES to resolve)");
}
if ((feats & SmileGenerator.Feature.ENCODE_BINARY_AS_7BIT.getMask()) == 0) {
throw new JsonGenerationException(
"Inconsistent settings: WRITE_HEADER disabled, but ENCODE_BINARY_AS_7BIT disabled; can not construct generator"
+" due to possible data loss (either enable WRITE_HEADER, or ENCODE_BINARY_AS_7BIT to resolve)");
}
}
return gen;
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/package-info.java 0000644 0001750 0001750 00000000533 11655120726 027553 0 ustar jamespage jamespage /**
* Package that contains experimental implementation of
* "Binary-Encoded JSON-Like" data format handlers (parser,
* generator, factory produce both, supporting constants).
*
* See Smile format specification for more details.
*
* @since 1.6
*/
package org.codehaus.jackson.smile;
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileParser.java 0000644 0001750 0001750 00000257470 11655120726 027473 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
import static org.codehaus.jackson.smile.SmileConstants.BYTE_MARKER_END_OF_STRING;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import org.codehaus.jackson.*;
import org.codehaus.jackson.impl.JsonParserBase;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.sym.BytesToNameCanonicalizer;
import org.codehaus.jackson.sym.Name;
public class SmileParser
extends JsonParserBase
{
/**
* Enumeration that defines all togglable features for Smile generators.
*/
public enum Feature {
/**
* Feature that determines whether 4-byte Smile header is mandatory in input,
* or optional. If enabled, it means that only input that starts with the header
* is accepted as valid; if disabled, header is optional. In latter case,r
* settings for content are assumed to be defaults.
*/
REQUIRE_HEADER(true)
;
final boolean _defaultState;
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; }
public int getMask() { return _mask; }
}
private final static int[] NO_INTS = new int[0];
private final static String[] NO_STRINGS = new String[0];
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Codec used for data binding when (if) requested.
*/
protected ObjectCodec _objectCodec;
/**
* Flag that indicates whether content can legally have raw (unquoted)
* binary data. Since this information is included both in header and
* in actual binary data blocks there is redundancy, and we want to
* ensure settings are compliant. Using application may also want to
* know this setting in case it does some direct (random) access.
*/
protected boolean _mayContainRawBinary;
/**
* Helper object used for low-level recycling of Smile-generator
* specific buffers.
*
* @since 1.7
*/
final protected SmileBufferRecycler _smileBufferRecycler;
/*
/**********************************************************
/* Input source config, state (from ex StreamBasedParserBase)
/**********************************************************
*/
/**
* Input stream that can be used for reading more content, if one
* in use. May be null, if input comes just as a full buffer,
* or if the stream has been closed.
*/
protected InputStream _inputStream;
/**
* 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;
/*
/**********************************************************
/* Additional parsing state
/**********************************************************
*/
/**
* Flag that indicates that the current token has not yet
* been fully processed, and needs to be finished for
* some access (or skipped to obtain the next token)
*/
protected boolean _tokenIncomplete = false;
/**
* Type byte of the current token
*/
protected int _typeByte;
/**
* Specific flag that is set when we encountered a 32-bit
* floating point value; needed since numeric super classes do
* not track distinction between float and double, but Smile
* format does, and we want to retain that separation.
*/
protected boolean _got32BitFloat;
/*
/**********************************************************
/* Symbol handling, decoding
/**********************************************************
*/
/**
* Symbol table that contains field names encountered so far
*/
final protected BytesToNameCanonicalizer _symbols;
/**
* Temporary buffer used for name parsing.
*/
protected int[] _quadBuffer = NO_INTS;
/**
* Quads used for hash calculation
*/
protected int _quad1, _quad2;
/**
* Array of recently seen field names, which may be back referenced
* by later fields.
* Defaults set to enable handling even if no header found.
*/
protected String[] _seenNames = NO_STRINGS;
protected int _seenNameCount = 0;
/**
* Array of recently seen field names, which may be back referenced
* by later fields
* Defaults set to disable handling if no header found.
*/
protected String[] _seenStringValues = null;
protected int _seenStringValueCount = -1;
/*
/**********************************************************
/* Thread-local recycling
/**********************************************************
*/
/**
* ThreadLocal
contains a {@link java.lang.ref.SoftRerefence}
* to a buffer recycler used to provide a low-cost
* buffer recycling for Smile-specific buffers.
*/
final protected static ThreadLocal>> _smileRecyclerRef
= new ThreadLocal>>();
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public SmileParser(IOContext ctxt, int parserFeatures, int smileFeatures,
ObjectCodec codec,
BytesToNameCanonicalizer sym,
InputStream in, byte[] inputBuffer, int start, int end,
boolean bufferRecyclable)
{
super(ctxt, parserFeatures);
_objectCodec = codec;
_symbols = sym;
_inputStream = in;
_inputBuffer = inputBuffer;
_inputPtr = start;
_inputEnd = end;
_bufferRecyclable = bufferRecyclable;
_tokenInputRow = -1;
_tokenInputCol = -1;
_smileBufferRecycler = _smileBufferRecycler();
}
@Override
public ObjectCodec getCodec() {
return _objectCodec;
}
@Override
public void setCodec(ObjectCodec c) {
_objectCodec = c;
}
/**
* Helper method called when it looks like input might contain the signature;
* and it is necessary to detect and handle signature to get configuration
* information it might have.
*
* @return True if valid signature was found and handled; false if not
*/
protected boolean handleSignature(boolean consumeFirstByte, boolean throwException)
throws IOException, JsonParseException
{
if (consumeFirstByte) {
++_inputPtr;
}
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
if (_inputBuffer[_inputPtr] != SmileConstants.HEADER_BYTE_2) {
if (throwException) {
_reportError("Malformed content: signature not valid, starts with 0x3a but followed by 0x"
+Integer.toHexString(_inputBuffer[_inputPtr])+", not 0x29");
}
return false;
}
if (++_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
if (_inputBuffer[_inputPtr] != SmileConstants.HEADER_BYTE_3) {
if (throwException) {
_reportError("Malformed content: signature not valid, starts with 0x3a, 0x29, but followed by 0x"
+Integer.toHexString(_inputBuffer[_inputPtr])+", not 0xA");
}
return false;
}
// Good enough; just need version info from 4th byte...
if (++_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int ch = _inputBuffer[_inputPtr++];
int versionBits = (ch >> 4) & 0x0F;
// but failure with version number is fatal, can not ignore
if (versionBits != SmileConstants.HEADER_VERSION_0) {
_reportError("Header version number bits (0x"+Integer.toHexString(versionBits)+") indicate unrecognized version; only 0x0 handled by parser");
}
// can avoid tracking names, if explicitly disabled
if ((ch & SmileConstants.HEADER_BIT_HAS_SHARED_NAMES) == 0) {
_seenNames = null;
_seenNameCount = -1;
}
// conversely, shared string values must be explicitly enabled
if ((ch & SmileConstants.HEADER_BIT_HAS_SHARED_STRING_VALUES) != 0) {
_seenStringValues = NO_STRINGS;
_seenStringValueCount = 0;
}
_mayContainRawBinary = ((ch & SmileConstants.HEADER_BIT_HAS_RAW_BINARY) != 0);
return true;
}
/**
* @since 1.7
*/
protected final static SmileBufferRecycler _smileBufferRecycler()
{
SoftReference> ref = _smileRecyclerRef.get();
SmileBufferRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new SmileBufferRecycler();
_smileRecyclerRef.set(new SoftReference>(br));
}
return br;
}
/*
/**********************************************************
/* Former StreamBasedParserBase methods
/**********************************************************
*/
@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;
}
/**
* Overridden since we do not really have character-based locations,
* but we do have byte offset to specify.
*/
@Override
public JsonLocation getTokenLocation()
{
return new JsonLocation(_ioContext.getSourceReference(),
_tokenInputTotal, // bytes
-1, -1, -1); // char offset, line, column
}
/**
* Overridden since we do not really have character-based locations,
* but we do have byte offset to specify.
*/
@Override
public JsonLocation getCurrentLocation()
{
return new JsonLocation(_ioContext.getSourceReference(),
_currInputProcessed + _inputPtr, // bytes
-1, -1, -1); // char offset, line, column
}
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
@Override
protected final boolean loadMore()
throws IOException
{
_currInputProcessed += _inputEnd;
//_currInputRowStart -= _inputEnd;
if (_inputStream != null) {
int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes");
}
}
return false;
}
/**
* Helper method that will try to load at least specified number bytes in
* input buffer, possible moving existing data around if necessary
*
* @since 1.6
*/
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) {
_currInputProcessed += _inputPtr;
//_currInputRowStart -= _inputPtr;
System.arraycopy(_inputBuffer, _inputPtr, _inputBuffer, 0, amount);
_inputEnd = amount;
} else {
_inputEnd = 0;
}
_inputPtr = 0;
while (_inputEnd < minAvailable) {
int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
if (count < 1) {
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+amount+" bytes");
}
return false;
}
_inputEnd += count;
}
return true;
}
@Override
protected void _closeInput() throws IOException
{
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying InputStream, unless we "own" it, or auto-closing
* feature is enabled.
*/
if (_inputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)) {
_inputStream.close();
}
_inputStream = null;
}
}
/*
/**********************************************************
/* Overridden methods
/**********************************************************
*/
@Override
protected void _finishString() throws IOException, JsonParseException
{
// should never be called; but must be defined for superclass
_throwInternal();
}
@Override
public void close() throws IOException
{
super.close();
// Merge found symbols, if any:
_symbols.release();
}
@Override
public boolean hasTextCharacters()
{
if (_currToken == JsonToken.VALUE_STRING) {
// yes; is or can be made available efficiently as char[]
return _textBuffer.hasTextAsCharacters();
}
if (_currToken == JsonToken.FIELD_NAME) {
// not necessarily; possible but:
return _nameCopied;
}
// other types, no benefit from accessing as char[]
return false;
}
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers() throws IOException
{
super._releaseBuffers();
if (_bufferRecyclable) {
byte[] buf = _inputBuffer;
if (buf != null) {
_inputBuffer = null;
_ioContext.releaseReadIOBuffer(buf);
}
}
{
String[] nameBuf = _seenNames;
if (nameBuf != null && nameBuf.length > 0) {
_seenNames = null;
/* 28-Jun-2011, tatu: With 1.9, caller needs to clear the buffer;
* but we only need to clear up to count as it is not a hash area
*/
if (_seenNameCount > 0) {
Arrays.fill(nameBuf, 0, _seenNameCount, null);
}
_smileBufferRecycler.releaseSeenNamesBuffer(nameBuf);
}
}
{
String[] valueBuf = _seenStringValues;
if (valueBuf != null && valueBuf.length > 0) {
_seenStringValues = null;
/* 28-Jun-2011, tatu: With 1.9, caller needs to clear the buffer;
* but we only need to clear up to count as it is not a hash area
*/
if (_seenStringValueCount > 0) {
Arrays.fill(valueBuf, 0, _seenStringValueCount, null);
}
_smileBufferRecycler.releaseSeenStringValuesBuffer(valueBuf);
}
}
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public boolean mayContainRawBinary() {
return _mayContainRawBinary;
}
/*
/**********************************************************
/* JsonParser impl
/**********************************************************
*/
@Override
public JsonToken nextToken() throws IOException, JsonParseException
{
_numTypesValid = NR_UNKNOWN;
// For longer tokens (text, binary), we'll only read when requested
if (_tokenIncomplete) {
_skipIncomplete();
}
_tokenInputTotal = _currInputProcessed + _inputPtr;
// also: clear any data retained so far
_binaryValue = null;
// Two main modes: values, and field names.
if (_parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
return (_currToken = _handleFieldName());
}
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_handleEOF();
/* NOTE: here we can and should close input, release buffers,
* since this is "hard" EOF, not a boundary imposed by
* header token.
*/
close();
return (_currToken = null);
}
}
int ch = _inputBuffer[_inputPtr++];
_typeByte = ch;
switch ((ch >> 5) & 0x7) {
case 0: // short shared string value reference
if (ch == 0) { // important: this is invalid, don't accept
_reportError("Invalid token byte 0x00");
}
return _handleSharedString(ch-1);
case 1: // simple literals, numbers
{
int typeBits = ch & 0x1F;
if (typeBits < 4) {
switch (typeBits) {
case 0x00:
_textBuffer.resetWithEmpty();
return (_currToken = JsonToken.VALUE_STRING);
case 0x01:
return (_currToken = JsonToken.VALUE_NULL);
case 0x02: // false
return (_currToken = JsonToken.VALUE_FALSE);
default: // 0x03 == true
return (_currToken = JsonToken.VALUE_TRUE);
}
}
// next 3 bytes define subtype
if (typeBits < 8) { // VInt (zigzag), BigInteger
if ((typeBits & 0x3) <= 0x2) { // 0x3 reserved (should never occur)
_tokenIncomplete = true;
_numTypesValid = 0;
return (_currToken = JsonToken.VALUE_NUMBER_INT);
}
break;
}
if (typeBits < 12) { // floating-point
int subtype = typeBits & 0x3;
if (subtype <= 0x2) { // 0x3 reserved (should never occur)
_tokenIncomplete = true;
_numTypesValid = 0;
_got32BitFloat = (subtype == 0);
return (_currToken = JsonToken.VALUE_NUMBER_FLOAT);
}
break;
}
if (typeBits == 0x1A) { // == 0x3A == ':' -> possibly header signature for next chunk?
if (handleSignature(false, false)) {
/* Ok, now; end-marker and header both imply doc boundary and a
* 'null token'; but if both are seen, they are collapsed.
* We can check this by looking at current token; if it's null,
* need to get non-null token
*/
if (_currToken == null) {
return nextToken();
}
return (_currToken = null);
}
}
_reportError("Unrecognized token byte 0x3A (malformed segment header?");
}
// and everything else is reserved, for now
break;
case 2: // tiny ASCII
// fall through
case 3: // short ASCII
// fall through
case 4: // tiny Unicode
// fall through
case 5: // short Unicode
// No need to decode, unless we have to keep track of back-references (for shared string values)
_currToken = JsonToken.VALUE_STRING;
if (_seenStringValueCount >= 0) { // shared text values enabled
_addSeenStringValue();
} else {
_tokenIncomplete = true;
}
return _currToken;
case 6: // small integers; zigzag encoded
_numberInt = SmileUtil.zigzagDecode(ch & 0x1F);
_numTypesValid = NR_INT;
return (_currToken = JsonToken.VALUE_NUMBER_INT);
case 7: // binary/long-text/long-shared/start-end-markers
switch (ch & 0x1F) {
case 0x00: // long variable length ASCII
case 0x04: // long variable length unicode
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_STRING);
case 0x08: // binary, 7-bit
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_EMBEDDED_OBJECT);
case 0x0C: // long shared string
case 0x0D:
case 0x0E:
case 0x0F:
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
return _handleSharedString(((ch & 0x3) << 8) + (_inputBuffer[_inputPtr++] & 0xFF));
case 0x18: // START_ARRAY
_parsingContext = _parsingContext.createChildArrayContext(-1, -1);
return (_currToken = JsonToken.START_ARRAY);
case 0x19: // END_ARRAY
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(']', '}');
}
_parsingContext = _parsingContext.getParent();
return (_currToken = JsonToken.END_ARRAY);
case 0x1A: // START_OBJECT
_parsingContext = _parsingContext.createChildObjectContext(-1, -1);
return (_currToken = JsonToken.START_OBJECT);
case 0x1B: // not used in this mode; would be END_OBJECT
_reportError("Invalid type marker byte 0xFB in value mode (would be END_OBJECT in key mode)");
case 0x1D: // binary, raw
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_EMBEDDED_OBJECT);
case 0x1F: // 0xFF, end of content
return (_currToken = null);
}
break;
}
// If we get this far, type byte is corrupt
_reportError("Invalid type marker byte 0x"+Integer.toHexString(ch & 0xFF)+" for expected value token");
return null;
}
private final JsonToken _handleSharedString(int index)
throws IOException, JsonParseException
{
if (index >= _seenStringValueCount) {
_reportInvalidSharedStringValue(index);
}
_textBuffer.resetWithString(_seenStringValues[index]);
return (_currToken = JsonToken.VALUE_STRING);
}
private final void _addSeenStringValue()
throws IOException, JsonParseException
{
_finishToken();
if (_seenStringValueCount < _seenStringValues.length) {
// !!! TODO: actually only store char[], first time around?
_seenStringValues[_seenStringValueCount++] = _textBuffer.contentsAsString();
return;
}
_expandSeenStringValues();
}
private final void _expandSeenStringValues()
{
String[] oldShared = _seenStringValues;
int len = oldShared.length;
String[] newShared;
if (len == 0) {
newShared = _smileBufferRecycler.allocSeenStringValuesBuffer();
if (newShared == null) {
newShared = new String[SmileBufferRecycler.DEFAULT_STRING_VALUE_BUFFER_LENGTH];
}
} else if (len == SmileConstants.MAX_SHARED_STRING_VALUES) { // too many? Just flush...
newShared = oldShared;
_seenStringValueCount = 0; // could also clear, but let's not yet bother
} else {
int newSize = (len == SmileBufferRecycler.DEFAULT_NAME_BUFFER_LENGTH) ? 256 : SmileConstants.MAX_SHARED_STRING_VALUES;
newShared = new String[newSize];
System.arraycopy(oldShared, 0, newShared, 0, oldShared.length);
}
_seenStringValues = newShared;
_seenStringValues[_seenStringValueCount++] = _textBuffer.contentsAsString();
}
@Override
public String getCurrentName() throws IOException, JsonParseException
{
return _parsingContext.getCurrentName();
}
@Override
public NumberType getNumberType()
throws IOException, JsonParseException
{
if (_got32BitFloat) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
/*
/**********************************************************
/* Public API, traversal, nextXxxValue/nextFieldName
/**********************************************************
*/
@Override
public boolean nextFieldName(SerializableString str)
throws IOException, JsonParseException
{
// Two parsing modes; can only succeed if expecting field name, so handle that first:
if (_parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
byte[] nameBytes = str.asQuotedUTF8();
final int byteLen = nameBytes.length;
// need room for type byte, name bytes, possibly end marker, so:
if ((_inputPtr + byteLen + 1) < _inputEnd) { // maybe...
int ptr = _inputPtr;
int ch = _inputBuffer[ptr++];
_typeByte = ch;
main_switch:
switch ((ch >> 6) & 3) {
case 0: // misc, including end marker
switch (ch) {
case 0x20: // empty String as name, legal if unusual
_currToken = JsonToken.FIELD_NAME;
_inputPtr = ptr;
_parsingContext.setCurrentName("");
return (byteLen == 0);
case 0x30: // long shared
case 0x31:
case 0x32:
case 0x33:
{
int index = ((ch & 0x3) << 8) + (_inputBuffer[ptr++] & 0xFF);
if (index >= _seenNameCount) {
_reportInvalidSharedName(index);
}
String name = _seenNames[index];
_parsingContext.setCurrentName(name);
_inputPtr = ptr;
_currToken = JsonToken.FIELD_NAME;
return (name.equals(str.getValue()));
}
//case 0x34: // long ASCII/Unicode name; let's not even try...
}
break;
case 1: // short shared, can fully process
{
int index = (ch & 0x3F);
if (index >= _seenNameCount) {
_reportInvalidSharedName(index);
}
_parsingContext.setCurrentName(_seenNames[index]);
String name = _seenNames[index];
_parsingContext.setCurrentName(name);
_inputPtr = ptr;
_currToken = JsonToken.FIELD_NAME;
return (name.equals(str.getValue()));
}
case 2: // short ASCII
{
int len = 1 + (ch & 0x3f);
if (len == byteLen) {
int i = 0;
for (; i < len; ++i) {
if (nameBytes[i] != _inputBuffer[ptr+i]) {
break main_switch;
}
}
// yes, does match...
_inputPtr = ptr + len;
final String name = str.getValue();
if (_seenNames != null) {
if (_seenNameCount >= _seenNames.length) {
_seenNames = _expandSeenNames(_seenNames);
}
_seenNames[_seenNameCount++] = name;
}
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
return true;
}
}
break;
case 3: // short Unicode
// all valid, except for 0xFF
{
int len = (ch & 0x3F);
if (len > 0x37) {
if (len == 0x3B) {
_currToken = JsonToken.END_OBJECT;
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker('}', ']');
}
_inputPtr = ptr;
_parsingContext = _parsingContext.getParent();
return false;
}
// error, but let's not worry about that here
break;
}
len += 2; // values from 2 to 57...
if (len == byteLen) {
int i = 0;
for (; i < len; ++i) {
if (nameBytes[i] != _inputBuffer[ptr+i]) {
break main_switch;
}
}
// yes, does match...
_inputPtr = ptr + len;
final String name = str.getValue();
if (_seenNames != null) {
if (_seenNameCount >= _seenNames.length) {
_seenNames = _expandSeenNames(_seenNames);
}
_seenNames[_seenNameCount++] = name;
}
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
return true;
}
}
break;
}
}
// otherwise fall back to default processing:
JsonToken t = _handleFieldName();
_currToken = t;
return (t == JsonToken.FIELD_NAME) && str.getValue().equals(_parsingContext.getCurrentName());
}
// otherwise just fall back to default handling; should occur rarely
return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
}
@Override
public String nextTextValue()
throws IOException, JsonParseException
{
// can't get text value if expecting name, so
if (!_parsingContext.inObject() || _currToken == JsonToken.FIELD_NAME) {
if (_tokenIncomplete) {
_skipIncomplete();
}
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
if (!loadMore()) {
_handleEOF();
close();
_currToken = null;
return null;
}
ptr = _inputPtr;
}
int ch = _inputBuffer[ptr++];
_tokenInputTotal = _currInputProcessed + _inputPtr;
// also: clear any data retained so far
_binaryValue = null;
_typeByte = ch;
switch ((ch >> 5) & 0x7) {
case 0: // short shared string value reference
if (ch == 0) { // important: this is invalid, don't accept
_reportError("Invalid token byte 0x00");
}
// _handleSharedString...
{
--ch;
if (ch >= _seenStringValueCount) {
_reportInvalidSharedStringValue(ch);
}
_inputPtr = ptr;
String text = _seenStringValues[ch];
_textBuffer.resetWithString(text);
_currToken = JsonToken.VALUE_STRING;
return text;
}
case 1: // simple literals, numbers
{
int typeBits = ch & 0x1F;
if (typeBits == 0x00) {
_inputPtr = ptr;
_textBuffer.resetWithEmpty();
_currToken = JsonToken.VALUE_STRING;
return "";
}
}
break;
case 2: // tiny ASCII
// fall through
case 3: // short ASCII
_currToken = JsonToken.VALUE_STRING;
_inputPtr = ptr;
_decodeShortAsciiValue(1 + (ch & 0x3F));
{
// No need to decode, unless we have to keep track of back-references (for shared string values)
String text;
if (_seenStringValueCount >= 0) { // shared text values enabled
if (_seenStringValueCount < _seenStringValues.length) {
text = _textBuffer.contentsAsString();
_seenStringValues[_seenStringValueCount++] = text;
} else {
_expandSeenStringValues();
text = _textBuffer.contentsAsString();
}
} else {
text = _textBuffer.contentsAsString();
}
return text;
}
case 4: // tiny Unicode
// fall through
case 5: // short Unicode
_currToken = JsonToken.VALUE_STRING;
_inputPtr = ptr;
_decodeShortUnicodeValue(2 + (ch & 0x3F));
{
// No need to decode, unless we have to keep track of back-references (for shared string values)
String text;
if (_seenStringValueCount >= 0) { // shared text values enabled
if (_seenStringValueCount < _seenStringValues.length) {
text = _textBuffer.contentsAsString();
_seenStringValues[_seenStringValueCount++] = text;
} else {
_expandSeenStringValues();
text = _textBuffer.contentsAsString();
}
} else {
text = _textBuffer.contentsAsString();
}
return text;
}
case 6: // small integers; zigzag encoded
break;
case 7: // binary/long-text/long-shared/start-end-markers
// TODO: support longer strings too?
/*
switch (ch & 0x1F) {
case 0x00: // long variable length ASCII
case 0x04: // long variable length unicode
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_STRING);
case 0x08: // binary, 7-bit
break main;
case 0x0C: // long shared string
case 0x0D:
case 0x0E:
case 0x0F:
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
return _handleSharedString(((ch & 0x3) << 8) + (_inputBuffer[_inputPtr++] & 0xFF));
}
break;
*/
break;
}
}
// otherwise fall back to generic handling:
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
@Override
public int nextIntValue(int defaultValue)
throws IOException, JsonParseException
{
if (nextToken() == JsonToken.VALUE_NUMBER_INT) {
return getIntValue();
}
return defaultValue;
}
@Override
public long nextLongValue(long defaultValue)
throws IOException, JsonParseException
{
if (nextToken() == JsonToken.VALUE_NUMBER_INT) {
return getLongValue();
}
return defaultValue;
}
@Override
public Boolean nextBooleanValue()
throws IOException, JsonParseException
{
switch (nextToken()) {
case VALUE_TRUE:
return Boolean.TRUE;
case VALUE_FALSE:
return Boolean.FALSE;
}
return null;
}
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
/**
* Method for accessing textual representation of the current event;
* if no current event (before first call to {@link #nextToken}, or
* after encountering end-of-input), returns null.
* Method can be called for any event.
*/
@Override
public String getText()
throws IOException, JsonParseException
{
if (_tokenIncomplete) {
_tokenIncomplete = false;
// Let's inline part of "_finishToken", common case
int tb = _typeByte;
int type = (tb >> 5) & 0x7;
if (type == 2 || type == 3) { // tiny & short ASCII
_decodeShortAsciiValue(1 + (tb & 0x3F));
return _textBuffer.contentsAsString();
}
if (type == 4 || type == 5) { // tiny & short Unicode
// short unicode; note, lengths 2 - 65 (off-by-one compared to ASCII)
_decodeShortUnicodeValue(2 + (tb & 0x3F));
return _textBuffer.contentsAsString();
}
_finishToken();
}
if (_currToken == JsonToken.VALUE_STRING) {
return _textBuffer.contentsAsString();
}
JsonToken t = _currToken;
if (t == null) { // null only before/after document
return null;
}
if (t == JsonToken.FIELD_NAME) {
return _parsingContext.getCurrentName();
}
if (t.isNumeric()) {
// TODO: optimize?
return getNumberValue().toString();
}
return _currToken.asString();
}
@Override
public char[] getTextCharacters()
throws IOException, JsonParseException
{
if (_currToken != null) { // null only before/after document
if (_tokenIncomplete) {
_finishToken();
}
switch (_currToken) {
case VALUE_STRING:
return _textBuffer.getTextBuffer();
case FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
// TODO: optimize
return getNumberValue().toString().toCharArray();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public int getTextLength()
throws IOException, JsonParseException
{
if (_currToken != null) { // null only before/after document
if (_tokenIncomplete) {
_finishToken();
}
switch (_currToken) {
case VALUE_STRING:
return _textBuffer.size();
case FIELD_NAME:
return _parsingContext.getCurrentName().length();
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
// TODO: optimize
return getNumberValue().toString().length();
default:
return _currToken.asCharArray().length;
}
}
return 0;
}
@Override
public int getTextOffset() throws IOException, JsonParseException
{
return 0;
}
/*
/**********************************************************
/* Public API, access to token information, binary
/**********************************************************
*/
@Override
public byte[] getBinaryValue(Base64Variant b64variant)
throws IOException, JsonParseException
{
if (_tokenIncomplete) {
_finishToken();
}
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT ) {
// Todo, maybe: support base64 for text?
_reportError("Current token ("+_currToken+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
}
return _binaryValue;
}
@Override
protected byte[] _decodeBase64(Base64Variant b64variant)
throws IOException, JsonParseException
{
// Should never get called, but must be defined for base class
_throwInternal();
return null;
}
/*
/**********************************************************
/* Internal methods, field name parsing
/**********************************************************
*/
/**
* Method that handles initial token type recognition for token
* that has to be either FIELD_NAME or END_OBJECT.
*/
protected final JsonToken _handleFieldName() throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int ch = _inputBuffer[_inputPtr++];
// is this needed?
_typeByte = ch;
switch ((ch >> 6) & 3) {
case 0: // misc, including end marker
switch (ch) {
case 0x20: // empty String as name, legal if unusual
_parsingContext.setCurrentName("");
return JsonToken.FIELD_NAME;
case 0x30: // long shared
case 0x31:
case 0x32:
case 0x33:
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int index = ((ch & 0x3) << 8) + (_inputBuffer[_inputPtr++] & 0xFF);
if (index >= _seenNameCount) {
_reportInvalidSharedName(index);
}
_parsingContext.setCurrentName(_seenNames[index]);
}
return JsonToken.FIELD_NAME;
case 0x34: // long ASCII/Unicode name
_handleLongFieldName();
return JsonToken.FIELD_NAME;
}
break;
case 1: // short shared, can fully process
{
int index = (ch & 0x3F);
if (index >= _seenNameCount) {
_reportInvalidSharedName(index);
}
_parsingContext.setCurrentName(_seenNames[index]);
}
return JsonToken.FIELD_NAME;
case 2: // short ASCII
{
int len = 1 + (ch & 0x3f);
String name;
Name n = _findDecodedFromSymbols(len);
if (n != null) {
name = n.getName();
_inputPtr += len;
} else {
name = _decodeShortAsciiName(len);
name = _addDecodedToSymbols(len, name);
}
if (_seenNames != null) {
if (_seenNameCount >= _seenNames.length) {
_seenNames = _expandSeenNames(_seenNames);
}
_seenNames[_seenNameCount++] = name;
}
_parsingContext.setCurrentName(name);
}
return JsonToken.FIELD_NAME;
case 3: // short Unicode
// all valid, except for 0xFF
ch &= 0x3F;
{
if (ch > 0x37) {
if (ch == 0x3B) {
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker('}', ']');
}
_parsingContext = _parsingContext.getParent();
return JsonToken.END_OBJECT;
}
} else {
final int len = ch + 2; // values from 2 to 57...
String name;
Name n = _findDecodedFromSymbols(len);
if (n != null) {
name = n.getName();
_inputPtr += len;
} else {
name = _decodeShortUnicodeName(len);
name = _addDecodedToSymbols(len, name);
}
if (_seenNames != null) {
if (_seenNameCount >= _seenNames.length) {
_seenNames = _expandSeenNames(_seenNames);
}
_seenNames[_seenNameCount++] = name;
}
_parsingContext.setCurrentName(name);
return JsonToken.FIELD_NAME;
}
}
break;
}
// Other byte values are illegal
_reportError("Invalid type marker byte 0x"+Integer.toHexString(_typeByte)+" for expected field name (or END_OBJECT marker)");
return null;
}
/**
* Method called to try to expand shared name area to fit one more potentially
* shared String. If area is already at its biggest size, will just clear
* the area (by setting next-offset to 0)
*/
private final String[] _expandSeenNames(String[] oldShared)
{
int len = oldShared.length;
String[] newShared;
if (len == 0) {
newShared = _smileBufferRecycler.allocSeenNamesBuffer();
if (newShared == null) {
newShared = new String[SmileBufferRecycler.DEFAULT_NAME_BUFFER_LENGTH];
}
} else if (len == SmileConstants.MAX_SHARED_NAMES) { // too many? Just flush...
newShared = oldShared;
_seenNameCount = 0; // could also clear, but let's not yet bother
} else {
int newSize = (len == SmileBufferRecycler.DEFAULT_STRING_VALUE_BUFFER_LENGTH) ? 256 : SmileConstants.MAX_SHARED_NAMES;
newShared = new String[newSize];
System.arraycopy(oldShared, 0, newShared, 0, oldShared.length);
}
return newShared;
}
private final String _addDecodedToSymbols(int len, String name)
{
if (len < 5) {
return _symbols.addName(name, _quad1, 0).getName();
}
if (len < 9) {
return _symbols.addName(name, _quad1, _quad2).getName();
}
int qlen = (len + 3) >> 2;
return _symbols.addName(name, _quadBuffer, qlen).getName();
}
private final String _decodeShortAsciiName(int len)
throws IOException, JsonParseException
{
// note: caller ensures we have enough bytes available
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
final byte[] inBuf = _inputBuffer;
int inPtr = _inputPtr;
// loop unrolling seems to help here:
for (int inEnd = inPtr + len - 3; inPtr < inEnd; ) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
outBuf[outPtr++] = (char) inBuf[inPtr++];
outBuf[outPtr++] = (char) inBuf[inPtr++];
outBuf[outPtr++] = (char) inBuf[inPtr++];
}
int left = (len & 3);
if (left > 0) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
if (left > 1) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
if (left > 2) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
}
}
}
_inputPtr = inPtr;
_textBuffer.setCurrentLength(len);
return _textBuffer.contentsAsString();
}
/**
* Helper method used to decode short Unicode string, length for which actual
* length (in bytes) is known
*
* @param len Length between 1 and 64
*/
private final String _decodeShortUnicodeName(int len)
throws IOException, JsonParseException
{
// note: caller ensures we have enough bytes available
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int inPtr = _inputPtr;
_inputPtr += len;
final int[] codes = SmileConstants.sUtf8UnitLengths;
final byte[] inBuf = _inputBuffer;
for (int end = inPtr + len; inPtr < end; ) {
int i = inBuf[inPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
// trickiest one, need surrogate handling
switch (code) {
case 1:
i = ((i & 0x1F) << 6) | (inBuf[inPtr++] & 0x3F);
break;
case 2:
i = ((i & 0x0F) << 12)
| ((inBuf[inPtr++] & 0x3F) << 6)
| (inBuf[inPtr++] & 0x3F);
break;
case 3:
i = ((i & 0x07) << 18)
| ((inBuf[inPtr++] & 0x3F) << 12)
| ((inBuf[inPtr++] & 0x3F) << 6)
| (inBuf[inPtr++] & 0x3F);
// note: this is the codepoint value; need to split, too
i -= 0x10000;
outBuf[outPtr++] = (char) (0xD800 | (i >> 10));
i = 0xDC00 | (i & 0x3FF);
break;
default: // invalid
_reportError("Invalid byte "+Integer.toHexString(i)+" in short Unicode text block");
}
}
outBuf[outPtr++] = (char) i;
}
_textBuffer.setCurrentLength(outPtr);
return _textBuffer.contentsAsString();
}
// note: slightly edited copy of UTF8StreamParser.addName()
private final Name _decodeLongUnicodeName(int[] quads, int byteLen, int quadLen)
throws IOException, JsonParseException
{
int lastQuadBytes = byteLen & 3;
// Ok: must decode UTF-8 chars. No other validation SHOULD be needed (except bounds checks?)
/* Note: last quad 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[quadLen-1];
// 8/16/24 bit left shift
quads[quadLen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
} else {
lastQuad = 0;
}
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 chars
_reportInvalidInitial(ch);
needed = ch = 1; // never really gets this far
}
if ((ix + needed) > byteLen) {
_reportInvalidEOF(" in long 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[quadLen-1] = lastQuad;
}
return _symbols.addName(baseName, quads, quadLen);
}
private final void _handleLongFieldName() throws IOException, JsonParseException
{
// First: gather quads we need, looking for end marker
final byte[] inBuf = _inputBuffer;
int quads = 0;
int bytes = 0;
int q = 0;
while (true) {
byte b = inBuf[_inputPtr++];
if (BYTE_MARKER_END_OF_STRING == b) {
bytes = 0;
break;
}
q = ((int) b) & 0xFF;
b = inBuf[_inputPtr++];
if (BYTE_MARKER_END_OF_STRING == b) {
bytes = 1;
break;
}
q = (q << 8) | (b & 0xFF);
b = inBuf[_inputPtr++];
if (BYTE_MARKER_END_OF_STRING == b) {
bytes = 2;
break;
}
q = (q << 8) | (b & 0xFF);
b = inBuf[_inputPtr++];
if (BYTE_MARKER_END_OF_STRING == b) {
bytes = 3;
break;
}
q = (q << 8) | (b & 0xFF);
if (quads >= _quadBuffer.length) {
_quadBuffer = _growArrayTo(_quadBuffer, _quadBuffer.length + 256); // grow by 1k
}
_quadBuffer[quads++] = q;
}
// and if we have more bytes, append those too
int byteLen = (quads << 2);
if (bytes > 0) {
if (quads >= _quadBuffer.length) {
_quadBuffer = _growArrayTo(_quadBuffer, _quadBuffer.length + 256);
}
_quadBuffer[quads++] = q;
byteLen += bytes;
}
// Know this name already?
String name;
Name n = _symbols.findName(_quadBuffer, quads);
if (n != null) {
name = n.getName();
} else {
name = _decodeLongUnicodeName(_quadBuffer, byteLen, quads).getName();
}
if (_seenNames != null) {
if (_seenNameCount >= _seenNames.length) {
_seenNames = _expandSeenNames(_seenNames);
}
_seenNames[_seenNameCount++] = name;
}
_parsingContext.setCurrentName(name);
}
/**
* Helper method for trying to find specified encoded UTF-8 byte sequence
* from symbol table; if successful avoids actual decoding to String
*/
private final Name _findDecodedFromSymbols(int len)
throws IOException, JsonParseException
{
if ((_inputEnd - _inputPtr) < len) {
_loadToHaveAtLeast(len);
}
// First: maybe we already have this name decoded?
if (len < 5) {
int inPtr = _inputPtr;
final byte[] inBuf = _inputBuffer;
int q = inBuf[inPtr] & 0xFF;
if (--len > 0) {
q = (q << 8) + (inBuf[++inPtr] & 0xFF);
if (--len > 0) {
q = (q << 8) + (inBuf[++inPtr] & 0xFF);
if (--len > 0) {
q = (q << 8) + (inBuf[++inPtr] & 0xFF);
}
}
}
_quad1 = q;
return _symbols.findName(q);
}
if (len < 9) {
int inPtr = _inputPtr;
final byte[] inBuf = _inputBuffer;
// First quadbyte is easy
int q1 = (inBuf[inPtr] & 0xFF) << 8;
q1 += (inBuf[++inPtr] & 0xFF);
q1 <<= 8;
q1 += (inBuf[++inPtr] & 0xFF);
q1 <<= 8;
q1 += (inBuf[++inPtr] & 0xFF);
int q2 = (inBuf[++inPtr] & 0xFF);
len -= 5;
if (len > 0) {
q2 = (q2 << 8) + (inBuf[++inPtr] & 0xFF);
if (--len > 0) {
q2 = (q2 << 8) + (inBuf[++inPtr] & 0xFF);
if (--len > 0) {
q2 = (q2 << 8) + (inBuf[++inPtr] & 0xFF);
}
}
}
_quad1 = q1;
_quad2 = q2;
return _symbols.findName(q1, q2);
}
return _findDecodedMedium(len);
}
/**
* Method for locating names longer than 8 bytes (in UTF-8)
*/
private final Name _findDecodedMedium(int len)
throws IOException, JsonParseException
{
// first, need enough buffer to store bytes as ints:
{
int bufLen = (len + 3) >> 2;
if (bufLen > _quadBuffer.length) {
_quadBuffer = _growArrayTo(_quadBuffer, bufLen);
}
}
// then decode, full quads first
int offset = 0;
int inPtr = _inputPtr;
final byte[] inBuf = _inputBuffer;
do {
int q = (inBuf[inPtr++] & 0xFF) << 8;
q |= inBuf[inPtr++] & 0xFF;
q <<= 8;
q |= inBuf[inPtr++] & 0xFF;
q <<= 8;
q |= inBuf[inPtr++] & 0xFF;
_quadBuffer[offset++] = q;
} while ((len -= 4) > 3);
// and then leftovers
if (len > 0) {
int q = inBuf[inPtr] & 0xFF;
if (--len > 0) {
q = (q << 8) + (inBuf[++inPtr] & 0xFF);
if (--len > 0) {
q = (q << 8) + (inBuf[++inPtr] & 0xFF);
}
}
_quadBuffer[offset++] = q;
}
return _symbols.findName(_quadBuffer, offset);
}
private static int[] _growArrayTo(int[] arr, int minSize)
{
int[] newArray = new int[minSize + 4];
if (arr != null) {
// !!! TODO: JDK 1.6, Arrays.copyOf
System.arraycopy(arr, 0, newArray, 0, arr.length);
}
return newArray;
}
/*
/**********************************************************
/* Internal methods, secondary parsing
/**********************************************************
*/
@Override
protected void _parseNumericValue(int expType)
throws IOException, JsonParseException
{
if (_tokenIncomplete) {
int tb = _typeByte;
// ensure we got a numeric type with value that is lazily parsed
if (((tb >> 5) & 0x7) != 1) {
_reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
}
_tokenIncomplete = false;
_finishNumberToken(tb);
}
}
/**
* Method called to finish parsing of a token so that token contents
* are retriable
*/
protected void _finishToken()
throws IOException, JsonParseException
{
_tokenIncomplete = false;
int tb = _typeByte;
int type = ((tb >> 5) & 0x7);
if (type == 1) { // simple literals, numbers
_finishNumberToken(tb);
return;
}
if (type <= 3) { // tiny & short ASCII
_decodeShortAsciiValue(1 + (tb & 0x3F));
return;
}
if (type <= 5) { // tiny & short Unicode
// short unicode; note, lengths 2 - 65 (off-by-one compared to ASCII)
_decodeShortUnicodeValue(2 + (tb & 0x3F));
return;
}
if (type == 7) {
tb &= 0x1F;
// next 3 bytes define subtype
switch (tb >> 2) {
case 0: // long variable length ASCII
_decodeLongAscii();
return;
case 1: // long variable length unicode
_decodeLongUnicode();
return;
case 2: // binary, 7-bit
_binaryValue = _read7BitBinaryWithLength();
return;
case 7: // binary, raw
_finishRawBinary();
return;
}
}
// sanity check
_throwInternal();
}
protected final void _finishNumberToken(int tb)
throws IOException, JsonParseException
{
tb &= 0x1F;
int type = (tb >> 2);
if (type == 1) { // VInt (zigzag) or BigDecimal
int subtype = tb & 0x03;
if (subtype == 0) { // (v)int
_finishInt();
} else if (subtype == 1) { // (v)long
_finishLong();
} else if (subtype == 2) {
_finishBigInteger();
} else {
_throwInternal();
}
return;
}
if (type == 2) { // other numbers
switch (tb & 0x03) {
case 0: // float
_finishFloat();
return;
case 1: // double
_finishDouble();
return;
case 2: // big-decimal
_finishBigDecimal();
return;
}
}
_throwInternal();
}
/*
/**********************************************************
/* Internal methods, secondary Number parsing
/**********************************************************
*/
private final void _finishInt() throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int value = _inputBuffer[_inputPtr++];
int i;
if (value < 0) { // 6 bits
value &= 0x3F;
} else {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = _inputBuffer[_inputPtr++];
if (i >= 0) { // 13 bits
value = (value << 7) + i;
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = _inputBuffer[_inputPtr++];
if (i >= 0) {
value = (value << 7) + i;
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = _inputBuffer[_inputPtr++];
if (i >= 0) {
value = (value << 7) + i;
// and then we must get negative
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = _inputBuffer[_inputPtr++];
if (i >= 0) {
_reportError("Corrupt input; 32-bit VInt extends beyond 5 data bytes");
}
}
}
}
value = (value << 6) + (i & 0x3F);
}
_numberInt = SmileUtil.zigzagDecode(value);
_numTypesValid = NR_INT;
}
private final void _finishLong()
throws IOException, JsonParseException
{
// Ok, first, will always get 4 full data bytes first; 1 was already passed
long l = (long) _fourBytesToInt();
// and loop for the rest
while (true) {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int value = _inputBuffer[_inputPtr++];
if (value < 0) {
l = (l << 6) + (value & 0x3F);
_numberLong = SmileUtil.zigzagDecode(l);
_numTypesValid = NR_LONG;
return;
}
l = (l << 7) + value;
}
}
private final void _finishBigInteger()
throws IOException, JsonParseException
{
byte[] raw = _read7BitBinaryWithLength();
_numberBigInt = new BigInteger(raw);
_numTypesValid = NR_BIGINT;
}
private final void _finishFloat()
throws IOException, JsonParseException
{
// just need 5 bytes to get int32 first; all are unsigned
int i = _fourBytesToInt();
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = (i << 7) + _inputBuffer[_inputPtr++];
float f = Float.intBitsToFloat(i);
_numberDouble = (double) f;
_numTypesValid = NR_DOUBLE;
}
private final void _finishDouble()
throws IOException, JsonParseException
{
// ok; let's take two sets of 4 bytes (each is int)
long hi = _fourBytesToInt();
long value = (hi << 28) + (long) _fourBytesToInt();
// and then remaining 2 bytes
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
value = (value << 7) + _inputBuffer[_inputPtr++];
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
value = (value << 7) + _inputBuffer[_inputPtr++];
_numberDouble = Double.longBitsToDouble(value);
_numTypesValid = NR_DOUBLE;
}
private final int _fourBytesToInt()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int i = _inputBuffer[_inputPtr++]; // first 7 bits
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = (i << 7) + _inputBuffer[_inputPtr++]; // 14 bits
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = (i << 7) + _inputBuffer[_inputPtr++]; // 21
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
return (i << 7) + _inputBuffer[_inputPtr++];
}
private final void _finishBigDecimal()
throws IOException, JsonParseException
{
int scale = SmileUtil.zigzagDecode(_readUnsignedVInt());
byte[] raw = _read7BitBinaryWithLength();
_numberBigDecimal = new BigDecimal(new BigInteger(raw), scale);
_numTypesValid = NR_BIGDECIMAL;
}
private final int _readUnsignedVInt()
throws IOException, JsonParseException
{
int value = 0;
while (true) {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int i = _inputBuffer[_inputPtr++];
if (i < 0) { // last byte
value = (value << 6) + (i & 0x3F);
return value;
}
value = (value << 7) + i;
}
}
private final byte[] _read7BitBinaryWithLength()
throws IOException, JsonParseException
{
int byteLen = _readUnsignedVInt();
byte[] result = new byte[byteLen];
int ptr = 0;
int lastOkPtr = byteLen - 7;
// first, read all 7-by-8 byte chunks
while (ptr <= lastOkPtr) {
if ((_inputEnd - _inputPtr) < 8) {
_loadToHaveAtLeast(8);
}
int i1 = (_inputBuffer[_inputPtr++] << 25)
+ (_inputBuffer[_inputPtr++] << 18)
+ (_inputBuffer[_inputPtr++] << 11)
+ (_inputBuffer[_inputPtr++] << 4);
int x = _inputBuffer[_inputPtr++];
i1 += x >> 3;
int i2 = ((x & 0x7) << 21)
+ (_inputBuffer[_inputPtr++] << 14)
+ (_inputBuffer[_inputPtr++] << 7)
+ _inputBuffer[_inputPtr++];
// Ok: got our 7 bytes, just need to split, copy
result[ptr++] = (byte)(i1 >> 24);
result[ptr++] = (byte)(i1 >> 16);
result[ptr++] = (byte)(i1 >> 8);
result[ptr++] = (byte)i1;
result[ptr++] = (byte)(i2 >> 16);
result[ptr++] = (byte)(i2 >> 8);
result[ptr++] = (byte)i2;
}
// and then leftovers: n+1 bytes to decode n bytes
int toDecode = (result.length - ptr);
if (toDecode > 0) {
if ((_inputEnd - _inputPtr) < (toDecode+1)) {
_loadToHaveAtLeast(toDecode+1);
}
int value = _inputBuffer[_inputPtr++];
for (int i = 1; i < toDecode; ++i) {
value = (value << 7) + _inputBuffer[_inputPtr++];
result[ptr++] = (byte) (value >> (7 - i));
}
// last byte is different, has remaining 1 - 6 bits, right-aligned
value <<= toDecode;
result[ptr] = (byte) (value + _inputBuffer[_inputPtr++]);
}
return result;
}
/*
/**********************************************************
/* Internal methods, secondary String parsing
/**********************************************************
*/
protected final void _decodeShortAsciiValue(int len)
throws IOException, JsonParseException
{
if ((_inputEnd - _inputPtr) < len) {
_loadToHaveAtLeast(len);
}
// Note: we count on fact that buffer must have at least 'len' (<= 64) empty char slots
final char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
final byte[] inBuf = _inputBuffer;
int inPtr = _inputPtr;
// loop unrolling SHOULD be faster (as with _decodeShortAsciiName), but somehow
// is NOT; as per testing, benchmarking... very weird.
/*
for (int inEnd = inPtr + len - 3; inPtr < inEnd; ) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
outBuf[outPtr++] = (char) inBuf[inPtr++];
outBuf[outPtr++] = (char) inBuf[inPtr++];
outBuf[outPtr++] = (char) inBuf[inPtr++];
}
int left = (len & 3);
if (left > 0) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
if (left > 1) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
if (left > 2) {
outBuf[outPtr++] = (char) inBuf[inPtr++];
}
}
}
*/
// meaning: regular tight loop is no slower, typically faster here:
for (final int end = inPtr + len; inPtr < end; ++inPtr) {
outBuf[outPtr++] = (char) inBuf[inPtr];
}
_inputPtr = inPtr;
_textBuffer.setCurrentLength(len);
}
protected final void _decodeShortUnicodeValue(int len)
throws IOException, JsonParseException
{
if ((_inputEnd - _inputPtr) < len) {
_loadToHaveAtLeast(len);
}
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int inPtr = _inputPtr;
_inputPtr += len;
final int[] codes = SmileConstants.sUtf8UnitLengths;
final byte[] inputBuf = _inputBuffer;
for (int end = inPtr + len; inPtr < end; ) {
int i = inputBuf[inPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
// trickiest one, need surrogate handling
switch (code) {
case 1:
i = ((i & 0x1F) << 6) | (inputBuf[inPtr++] & 0x3F);
break;
case 2:
i = ((i & 0x0F) << 12)
| ((inputBuf[inPtr++] & 0x3F) << 6)
| (inputBuf[inPtr++] & 0x3F);
break;
case 3:
i = ((i & 0x07) << 18)
| ((inputBuf[inPtr++] & 0x3F) << 12)
| ((inputBuf[inPtr++] & 0x3F) << 6)
| (inputBuf[inPtr++] & 0x3F);
// note: this is the codepoint value; need to split, too
i -= 0x10000;
outBuf[outPtr++] = (char) (0xD800 | (i >> 10));
i = 0xDC00 | (i & 0x3FF);
break;
default: // invalid
_reportError("Invalid byte "+Integer.toHexString(i)+" in short Unicode text block");
}
}
outBuf[outPtr++] = (char) i;
}
_textBuffer.setCurrentLength(outPtr);
}
private final void _decodeLongAscii()
throws IOException, JsonParseException
{
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
main_loop:
while (true) {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int inPtr = _inputPtr;
int left = _inputEnd - inPtr;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
left = Math.min(left, outBuf.length - outPtr);
do {
byte b = _inputBuffer[inPtr++];
if (b == SmileConstants.BYTE_MARKER_END_OF_STRING) {
_inputPtr = inPtr;
break main_loop;
}
outBuf[outPtr++] = (char) b;
} while (--left > 0);
_inputPtr = inPtr;
}
_textBuffer.setCurrentLength(outPtr);
}
private final void _decodeLongUnicode()
throws IOException, JsonParseException
{
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = SmileConstants.sUtf8UnitLengths;
int c;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
// First the tight ASCII loop:
ascii_loop:
while (true) {
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
loadMoreGuaranteed();
ptr = _inputPtr;
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
int max = _inputEnd;
{
int max2 = ptr + (outBuf.length - outPtr);
if (max2 < max) {
max = max2;
}
}
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 == SmileConstants.INT_MARKER_END_OF_STRING) {
break main_loop;
}
switch (codes[c]) {
case 1: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 2: // 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:
// 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);
}
private final void _finishRawBinary()
throws IOException, JsonParseException
{
int byteLen = _readUnsignedVInt();
_binaryValue = new byte[byteLen];
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int ptr = 0;
while (true) {
int toAdd = Math.min(byteLen, _inputEnd - _inputPtr);
System.arraycopy(_inputBuffer, _inputPtr, _binaryValue, ptr, toAdd);
_inputPtr += toAdd;
ptr += toAdd;
byteLen -= toAdd;
if (byteLen <= 0) {
return;
}
loadMoreGuaranteed();
}
}
/*
/**********************************************************
/* Internal methods, skipping
/**********************************************************
*/
/**
* Method called to skip remainders of an incomplete token, when
* contents themselves will not be needed any more
*/
protected void _skipIncomplete() throws IOException, JsonParseException
{
_tokenIncomplete = false;
int tb = _typeByte;
switch ((tb >> 5) & 0x7) {
case 1: // simple literals, numbers
tb &= 0x1F;
// next 3 bytes define subtype
switch (tb >> 2) {
case 1: // VInt (zigzag)
// easy, just skip until we see sign bit... (should we try to limit damage?)
switch (tb & 0x3) {
case 1: // vlong
_skipBytes(4); // min 5 bytes
// fall through
case 0: // vint
while (true) {
final int end = _inputEnd;
final byte[] buf = _inputBuffer;
while (_inputPtr < end) {
if (buf[_inputPtr++] < 0) {
return;
}
}
loadMoreGuaranteed();
}
case 2: // big-int
// just has binary data
_skip7BitBinary();
return;
}
break;
case 2: // other numbers
switch (tb & 0x3) {
case 0: // float
_skipBytes(5);
return;
case 1: // double
_skipBytes(10);
return;
case 2: // big-decimal
// first, skip scale
_readUnsignedVInt();
// then length-prefixed binary serialization
_skip7BitBinary();
return;
}
break;
}
break;
case 2: // tiny ASCII
// fall through
case 3: // short ASCII
_skipBytes(1 + (tb & 0x3F));
return;
case 4: // tiny unicode
// fall through
case 5: // short unicode
_skipBytes(2 + (tb & 0x3F));
return;
case 7:
tb &= 0x1F;
// next 3 bytes define subtype
switch (tb >> 2) {
case 0: // long variable length ASCII
case 1: // long variable length unicode
/* Doesn't matter which one, just need to find the end marker
* (note: can potentially skip invalid UTF-8 too)
*/
while (true) {
final int end = _inputEnd;
final byte[] buf = _inputBuffer;
while (_inputPtr < end) {
if (buf[_inputPtr++] == BYTE_MARKER_END_OF_STRING) {
return;
}
}
loadMoreGuaranteed();
}
// never gets here
case 2: // binary, 7-bit
_skip7BitBinary();
return;
case 7: // binary, raw
_skipBytes(_readUnsignedVInt());
return;
}
}
_throwInternal();
}
protected void _skipBytes(int len)
throws IOException, JsonParseException
{
while (true) {
int toAdd = Math.min(len, _inputEnd - _inputPtr);
_inputPtr += toAdd;
len -= toAdd;
if (len <= 0) {
return;
}
loadMoreGuaranteed();
}
}
/**
* Helper method for skipping length-prefixed binary data
* section
*/
protected void _skip7BitBinary()
throws IOException, JsonParseException
{
int origBytes = _readUnsignedVInt();
// Ok; 8 encoded bytes for 7 payload bytes first
int chunks = origBytes / 7;
int encBytes = chunks * 8;
// and for last 0 - 6 bytes, last+1 (except none if no leftovers)
origBytes -= 7 * chunks;
if (origBytes > 0) {
encBytes += 1 + origBytes;
}
_skipBytes(encBytes);
}
/*
/**********************************************************
/* Internal methods, UTF8 decoding
/**********************************************************
*/
private final int _decodeUtf8_2(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
return ((c & 0x1F) << 6) | (d & 0x3F);
}
private final int _decodeUtf8_3(int c1)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
int c = (c1 << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
return c;
}
private final int _decodeUtf8_3fast(int c1)
throws IOException, JsonParseException
{
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
int c = (c1 << 6) | (d & 0x3F);
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
return c;
}
/**
* @return Character value minus 0x10000; this so that caller
* can readily expand it to actual surrogates
*/
private final int _decodeUtf8_4(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = ((c & 0x07) << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
/* note: won't change it to negative here, since caller
* already knows it'll need a surrogate
*/
return ((c << 6) | (d & 0x3F)) - 0x10000;
}
/*
/**********************************************************
/* Internal methods, error reporting
/**********************************************************
*/
protected void _reportInvalidSharedName(int index) throws IOException
{
if (_seenNames == null) {
_reportError("Encountered shared name reference, even though document header explicitly declared no shared name references are included");
}
_reportError("Invalid shared name reference "+index+"; only got "+_seenNameCount+" names in buffer (invalid content)");
}
protected void _reportInvalidSharedStringValue(int index) throws IOException
{
if (_seenStringValues == null) {
_reportError("Encountered shared text value reference, even though document header did not declared shared text value references may be included");
}
_reportError("Invalid shared text value reference "+index+"; only got "+_seenStringValueCount+" names in buffer (invalid content)");
}
protected void _reportInvalidChar(int c) throws JsonParseException
{
// Either invalid WS or illegal UTF-8 start char
if (c < ' ') {
_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);
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileParserBootstrapper.java 0000644 0001750 0001750 00000022534 11655120726 032067 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
import java.io.*;
import org.codehaus.jackson.JsonLocation;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.ObjectCodec;
import org.codehaus.jackson.format.InputAccessor;
import org.codehaus.jackson.format.MatchStrength;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.sym.BytesToNameCanonicalizer;
import static org.codehaus.jackson.smile.SmileConstants.*;
/**
* Simple bootstrapper version used with Smile format parser.
*/
public class SmileParserBootstrapper
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
final IOContext _context;
final InputStream _in;
/*
/**********************************************************
/* Input buffering
/**********************************************************
*/
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
/**********************************************************
*/
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public SmileParserBootstrapper(IOContext ctxt, InputStream in)
{
_context = ctxt;
_in = in;
_inputBuffer = ctxt.allocReadIOBuffer();
_inputEnd = _inputPtr = 0;
_inputProcessed = 0;
_bufferRecyclable = true;
}
public SmileParserBootstrapper(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;
}
public SmileParser constructParser(int generalParserFeatures, int smileFeatures,
ObjectCodec codec, BytesToNameCanonicalizer rootByteSymbols)
throws IOException, JsonParseException
{
boolean intern = JsonParser.Feature.INTERN_FIELD_NAMES.enabledIn(generalParserFeatures);
BytesToNameCanonicalizer can = rootByteSymbols.makeChild(true, intern);
// We just need a single byte, really, to know if it starts with header
ensureLoaded(1);
SmileParser p = new SmileParser(_context, generalParserFeatures, smileFeatures,
codec, can,
_in, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable);
boolean hadSig = false;
if (_inputPtr < _inputEnd) { // only false for empty doc
if (_inputBuffer[_inputPtr] == SmileConstants.HEADER_BYTE_1) {
// need to ensure it gets properly handled so caller won't see the signature
hadSig = p.handleSignature(true, true);
}
}
if (!hadSig && (smileFeatures & SmileParser.Feature.REQUIRE_HEADER.getMask()) != 0) {
// Ok, first, let's see if it looks like plain JSON...
String msg;
byte firstByte = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr] : 0;
if (firstByte == '{' || firstByte == '[') {
msg = "Input does not start with Smile format header (first byte = 0x"
+Integer.toHexString(firstByte & 0xFF)+") -- rather, it starts with '"+((char) firstByte)
+"' (plain JSON input?) -- can not parse";
} else {
msg = "Input does not start with Smile format header (first byte = 0x"
+Integer.toHexString(firstByte & 0xFF)+") and parser has REQUIRE_HEADER enabled: can not parse";
}
throw new JsonParseException(msg, JsonLocation.NA);
}
return p;
}
/*
/**********************************************************
/* Encoding detection for data format auto-detection
/**********************************************************
*/
/**
* Helper
*
* @since 1.8
*/
public static MatchStrength hasSmileFormat(InputAccessor acc) throws IOException
{
// Ok: ideally we start with the header -- if so, we are golden
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
// We always need at least two bytes to determine, so
byte b1 = acc.nextByte();
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
byte b2 = acc.nextByte();
// First: do we see 3 "magic bytes"? If so, we are golden
if (b1 == SmileConstants.HEADER_BYTE_1) { // yeah, looks like marker
if (b2 != SmileConstants.HEADER_BYTE_2) {
return MatchStrength.NO_MATCH;
}
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
return (acc.nextByte() == SmileConstants.HEADER_BYTE_3) ?
MatchStrength.FULL_MATCH : MatchStrength.NO_MATCH;
}
// Otherwise: ideally either Object or Array:
if (b1 == SmileConstants.TOKEN_LITERAL_START_OBJECT) {
/* Object is bit easier, because now we need to get new name; i.e. can
* rule out name back-refs
*/
if (b2 == SmileConstants.TOKEN_KEY_LONG_STRING) {
return MatchStrength.SOLID_MATCH;
}
int ch = (int) b2 & 0xFF;
if (ch >= 0x80 && ch < 0xF8) {
return MatchStrength.SOLID_MATCH;
}
return MatchStrength.NO_MATCH;
}
// Array bit trickier
if (b1 == SmileConstants.TOKEN_LITERAL_START_ARRAY) {
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
/* For arrays, we will actually accept much wider range of values (including
* things that could otherwise collide)
*/
if (likelySmileValue(b2) || possibleSmileValue(b2, true)) {
return MatchStrength.SOLID_MATCH;
}
return MatchStrength.NO_MATCH;
}
// Scalar values are pretty weak, albeit possible; require more certain match, consider it weak:
if (likelySmileValue(b1) || possibleSmileValue(b2, false)) {
return MatchStrength.SOLID_MATCH;
}
return MatchStrength.NO_MATCH;
}
private static boolean likelySmileValue(byte b)
{
int ch = (int) b & 0xFF;
if (ch >= 0xE0) { // good range for known values
switch (ch) {
case TOKEN_MISC_LONG_TEXT_ASCII: // 0xE0
case TOKEN_MISC_LONG_TEXT_UNICODE: // 0xE4
case TOKEN_MISC_BINARY_7BIT: // 0xE8
case TOKEN_LITERAL_START_ARRAY: // 0xF8
case TOKEN_LITERAL_START_OBJECT: // 0xFA
return true;
}
// Others will not work (end object/array; reserved; shared strings)
return false;
}
// ASCII ctrl char range is pretty good match too
if (ch >= 0x80 && ch <= 0x9F) {
return true;
}
return false;
}
/**
* @param lenient Whether to consider more speculative matches or not
* (typically true when there is context like start-array)
*/
private static boolean possibleSmileValue(byte b, boolean lenient)
{
int ch = (int) b & 0xFF;
// note: we know that likely matches have been handled already, so...
if (ch >= 0x80) {
return (ch <= 0xE0);
}
if (lenient) {
if (ch >= 0x40) { // tiny/short ASCII
return true;
}
if (ch >- 0x20) { // various constants
return (ch < 0x2C); // many reserved bytes that can't be seen
}
}
return false;
}
/*
/**********************************************************
/* Internal methods, raw input access
/**********************************************************
*/
protected boolean ensureLoaded(int minimum)
throws IOException
{
if (_in == null) { // block source; nothing more to load
return false;
}
/* 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 = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
if (count < 1) {
return false;
}
_inputEnd += count;
gotten += count;
}
return true;
}
}
jackson-src-1.9.2/src/smile/java/org/codehaus/jackson/smile/SmileBufferRecycler.java 0000644 0001750 0001750 00000002777 11655120726 031137 0 ustar jamespage jamespage package org.codehaus.jackson.smile;
/**
* Simple helper class used for implementing simple reuse system for Smile-specific
* buffers that are used.
*
* @param Type of name entries stored in arrays to recycle
*
* @since 1.7
*/
public class SmileBufferRecycler
{
public final static int DEFAULT_NAME_BUFFER_LENGTH = 64;
public final static int DEFAULT_STRING_VALUE_BUFFER_LENGTH = 64;
protected T[] _seenNamesBuffer;
protected T[] _seenStringValuesBuffer;
public SmileBufferRecycler() { }
public T[] allocSeenNamesBuffer()
{
// 11-Feb-2011, tatu: Used to alloc here; but due to generics, can't easily any more
T[] result = _seenNamesBuffer;
if (result != null) {
// let's ensure we don't retain it here, unless returned
_seenNamesBuffer = null;
// note: caller must have cleaned it up before returning
}
return result;
}
public T[] allocSeenStringValuesBuffer()
{
// 11-Feb-2011, tatu: Used to alloc here; but due to generics, can't easily any more
T[] result = _seenStringValuesBuffer;
if (result != null) {
_seenStringValuesBuffer = null;
// note: caller must have cleaned it up before returning
}
return result;
}
public void releaseSeenNamesBuffer(T[] buffer) {
_seenNamesBuffer = buffer;
}
public void releaseSeenStringValuesBuffer(T[] buffer) {
_seenStringValuesBuffer = buffer;
}
}
jackson-src-1.9.2/src/mrbean/ 0000755 0001750 0001750 00000000000 11655120726 016452 5 ustar jamespage jamespage jackson-src-1.9.2/src/mrbean/java/ 0000755 0001750 0001750 00000000000 11655120726 017373 5 ustar jamespage jamespage jackson-src-1.9.2/src/mrbean/java/org/ 0000755 0001750 0001750 00000000000 11655120726 020162 5 ustar jamespage jamespage jackson-src-1.9.2/src/mrbean/java/org/codehaus/ 0000755 0001750 0001750 00000000000 11655120726 021755 5 ustar jamespage jamespage jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/ 0000755 0001750 0001750 00000000000 11655120726 023405 5 ustar jamespage jamespage jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/mrbean/ 0000755 0001750 0001750 00000000000 11672662540 024655 5 ustar jamespage jamespage jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/mrbean/AbstractTypeMaterializer.java 0000644 0001750 0001750 00000020167 11655120726 032500 0 ustar jamespage jamespage package org.codehaus.jackson.mrbean;
import java.lang.reflect.Modifier;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.Versioned;
import org.codehaus.jackson.map.AbstractTypeResolver;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.util.VersionUtil;
/**
* Nifty class for pulling implementations of classes out of thin air.
*
* ... friends call him Mister Bean... :-)
*
* @author tatu
* @author sunny
*
* @since 1.6
*/
public class AbstractTypeMaterializer
extends AbstractTypeResolver
implements Versioned
{
/**
* Enumeration that defines togglable features that guide
* the serialization feature.
*/
public enum Feature {
/**
* Feature that determines what happens if an "unrecognized"
* (non-getter, non-setter) abstract method is encountered: if set to
* true, will throw an exception during materialization; if false,
* will materialize method that throws exception only if called.
*/
FAIL_ON_UNMATERIALIZED_METHOD(false),
/**
* Feature that determines what happens when attempt is made to
* generate implementation of non-public class or interface.
* If true, an exception is thrown; if false, will just quietly
* ignore attempts.
*
* @since 1.9
*/
FAIL_ON_NON_PUBLIC_TYPES(true)
;
final boolean _defaultState;
// Method that calculates bit set (flags) of all features that are enabled by default.
protected 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 int getMask() { return (1 << ordinal()); }
}
/**
* Bitfield (set of flags) of all Features that are enabled
* by default.
*/
protected final static int DEFAULT_FEATURE_FLAGS = Feature.collectDefaults();
/**
* Default package to use for generated classes.
*/
public final static String DEFAULT_PACKAGE_FOR_GENERATED = "org.codehaus.jackson.generated.";
/**
* We will use per-materializer class loader for now; would be nice
* to find a way to reduce number of class loaders (and hence
* number of generated classes!) constructed...
*/
protected final MyClassLoader _classLoader;
/**
* Bit set that contains all enabled features
*/
protected int _featureFlags = DEFAULT_FEATURE_FLAGS;
/**
* Package name to use as prefix for generated classes.
*/
protected String _defaultPackage = DEFAULT_PACKAGE_FOR_GENERATED;
/*
/**********************************************************
/* Construction, configuration
/**********************************************************
*/
public AbstractTypeMaterializer() {
this(null);
}
public AbstractTypeMaterializer(ClassLoader parentClassLoader)
{
if (parentClassLoader == null) {
parentClassLoader = getClass().getClassLoader();
}
_classLoader = new MyClassLoader(parentClassLoader);
}
/**
* Method that will return version information stored in and read from jar
* that contains this class.
*
* @since 1.6
*/
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/**
* Method for checking whether given feature is enabled or not
*/
public final boolean isEnabled(Feature f) {
return (_featureFlags & f.getMask()) != 0;
}
/**
* Method for enabling specified feature.
*/
public void enable(Feature f) {
_featureFlags |= f.getMask();
}
/**
* Method for disabling specified feature.
*/
public void disable(Feature f) {
_featureFlags &= ~f.getMask();
}
/**
* Method for enabling or disabling specified feature.
*/
public void set(Feature f, boolean state)
{
if (state) {
enable(f);
} else {
disable(f);
}
}
public void setDefaultPackage(String defPkg)
{
if (!defPkg.endsWith(".")) {
defPkg = defPkg + ".";
}
_defaultPackage = defPkg;
}
/*
/**********************************************************
/* Public API
/**********************************************************
*/
@Override
public JavaType resolveAbstractType(DeserializationConfig config, JavaType type)
{
/* 19-Feb-2011, tatu: Future plans may include calling of this method for all kinds
* of abstract types. So as simple precaution, let's limit kinds of types we
* will try materializa implementations for.
*/
/* We won't be handling any container types (Collections, Maps and arrays),
* Throwables or enums.
*/
if (type.isContainerType() || type.isPrimitive() || type.isEnumType() || type.isThrowable()) {
return null;
}
Class> cls = type.getRawClass();
/* [JACKSON-683] Fail on non-public classes, since we can't easily force
* access to such classes (unless we tried to generate impl classes in that
* package)
*/
if (!Modifier.isPublic(cls.getModifiers())) {
if (isEnabled(Feature.FAIL_ON_NON_PUBLIC_TYPES)) {
throw new IllegalArgumentException("Can not materialize implementation of "+cls+" since it is not public ");
}
return null;
}
// might want to skip proxies, local types too... but let them be for now:
//if (intr.findTypeResolver(beanDesc.getClassInfo(), type) == null) {
return config.constructType(materializeClass(config, cls));
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected Class> materializeClass(DeserializationConfig config, Class> cls)
{
// Need to have proper name mangling in future, but for now...
String newName = _defaultPackage+cls.getName();
BeanBuilder builder = new BeanBuilder(config, cls);
byte[] bytecode = builder.implement(isEnabled(Feature.FAIL_ON_UNMATERIALIZED_METHOD)).build(newName);
Class> result = _classLoader.loadAndResolve(newName, bytecode, cls);
return result;
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* To support actual dynamic loading of bytecode we need a simple
* custom classloader.
*/
private static class MyClassLoader extends ClassLoader
{
public MyClassLoader(ClassLoader parent)
{
super(parent);
}
/**
* @param targetClass Interface or abstract class that class to load should extend or
* implement
*/
public Class> loadAndResolve(String className, byte[] byteCode, Class> targetClass)
throws IllegalArgumentException
{
// First things first: just to be sure; maybe we have already loaded it?
Class> old = findLoadedClass(className);
if (old != null && targetClass.isAssignableFrom(old)) {
return old;
}
Class> impl;
try {
impl = defineClass(className, byteCode, 0, byteCode.length);
} catch (LinkageError e) {
throw new IllegalArgumentException("Failed to load class '"+className+"': "+e.getMessage() ,e);
}
// important: must also resolve the class...
resolveClass(impl);
return impl;
}
}
}
jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/mrbean/BeanUtil.java 0000644 0001750 0001750 00000004011 11655120726 027213 0 ustar jamespage jamespage package org.codehaus.jackson.mrbean;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class BeanUtil
{
protected static boolean isConcrete(Member member)
{
int mod = member.getModifiers();
return (mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0;
}
/**
* 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).
*/
public static List> findSuperTypes(Class> cls, Class> endBefore)
{
return findSuperTypes(cls, endBefore, new ArrayList>());
}
public static List> findSuperTypes(Class> cls, Class> endBefore, List> result)
{
_addSuperTypes(cls, endBefore, result, false);
return result;
}
private static void _addSuperTypes(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 : cls.getInterfaces()) {
_addSuperTypes(intCls, endBefore, result, true);
}
_addSuperTypes(cls.getSuperclass(), endBefore, result, true);
}
}
jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/mrbean/MrBeanModule.java 0000644 0001750 0001750 00000002551 11655120726 030031 0 ustar jamespage jamespage package org.codehaus.jackson.mrbean;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.Module;
import org.codehaus.jackson.mrbean.AbstractTypeMaterializer;
public class MrBeanModule extends Module
{
private final String NAME = "MrBeanModule";
// TODO: externalize
private final static Version VERSION = new Version(1, 8, 0, null);
/**
* Configured materializer instance to register with deserializer factory.
*/
protected AbstractTypeMaterializer _materializer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public MrBeanModule() {
this(new AbstractTypeMaterializer());
}
public MrBeanModule(AbstractTypeMaterializer materializer) {
_materializer = materializer;
}
@Override public String getModuleName() { return NAME; }
@Override public Version version() { return VERSION; }
@Override
public void setupModule(SetupContext context)
{
// All we really need to for now is to register materializer:
context.addAbstractTypeResolver(_materializer);
}
/*
/**********************************************************
/* Extended API, configuration
/**********************************************************
*/
}
jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/mrbean/BeanBuilder.java 0000644 0001750 0001750 00000041033 11655120726 027671 0 ustar jamespage jamespage package org.codehaus.jackson.mrbean;
import java.lang.reflect.Method;
import java.util.*;
import org.codehaus.jackson.org.objectweb.asm.*;
import static org.codehaus.jackson.org.objectweb.asm.Opcodes.*;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.type.TypeFactory;
/**
* Heavy lifter of mr Bean package: class that keeps track of logical POJO properties,
* and figures out how to create an implementation class.
*
* @since 1.6
*/
public class BeanBuilder
{
protected Map _beanProperties = new LinkedHashMap();
protected LinkedHashMap _unsupportedMethods = new LinkedHashMap();
/**
* Abstract class or interface that the bean is created to extend or implement.
*/
protected final Class> _implementedType;
protected final TypeFactory _typeFactory;
public BeanBuilder(DeserializationConfig config, Class> implType)
{
_implementedType = implType;
_typeFactory = config.getTypeFactory();
}
/*
/**********************************************************
/* Core public API
/**********************************************************
*/
/**
* @param failOnUnrecognized If true, and an unrecognized (non-getter, non-setter)
* method is encountered, will throw {@link IllegalArgumentException}; if false,
* will implement bogus method that will throw {@link UnsupportedOperationException}
* if called.
*/
public BeanBuilder implement(boolean failOnUnrecognized)
{
ArrayList> implTypes = new ArrayList>();
// First: find all supertypes:
implTypes.add(_implementedType);
BeanUtil.findSuperTypes(_implementedType, Object.class, implTypes);
for (Class> impl : implTypes) {
// and then find all getters, setters, and other non-concrete methods therein:
for (Method m : impl.getDeclaredMethods()) {
String methodName = m.getName();
int argCount = m.getParameterTypes().length;
if (argCount == 0) { // getter?
if (methodName.startsWith("get") || methodName.startsWith("is") && returnsBoolean(m)) {
addGetter(m);
continue;
}
} else if (argCount == 1 && methodName.startsWith("set")) {
addSetter(m);
continue;
}
// Otherwise, if concrete, or already handled, skip:
// !!! note: won't handle overloaded methods properly
if (BeanUtil.isConcrete(m) || _unsupportedMethods.containsKey(methodName)) {
continue;
}
if (failOnUnrecognized) {
throw new IllegalArgumentException("Unrecognized abstract method '"+methodName
+"' (not a getter or setter) -- to avoid exception, disable AbstractTypeMaterializer.Feature.FAIL_ON_UNMATERIALIZED_METHOD");
}
_unsupportedMethods.put(methodName, m);
}
}
return this;
}
/**
* Method that generates byte code for class that implements abstract
* types requested so far.
*
* @param className Fully-qualified name of the class to generate
* @return Byte code Class instance built by this builder
*/
public byte[] build(String className)
{
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
String internalClass = getInternalClassName(className);
String implName = getInternalClassName(_implementedType.getName());
// muchos important: level at least 1.5 to get generics!!!
// Also: abstract class vs interface...
String superName;
if (_implementedType.isInterface()) {
superName = "java/lang/Object";
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, internalClass, null,
superName, new String[] { implName });
} else {
superName = implName;
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, internalClass, null,
implName, null);
}
cw.visitSource(className + ".java", null);
BeanBuilder.generateDefaultConstructor(cw, superName);
for (Property prop : _beanProperties.values()) {
// First: determine type to use; preferably setter (usually more explicit); otherwise getter
TypeDescription type = prop.selectType(_typeFactory);
createField(cw, prop, type);
// since some getters and/or setters may be implemented, check:
if (!prop.hasConcreteGetter()) {
createGetter(cw, internalClass, prop, type);
}
if (!prop.hasConcreteSetter()) {
createSetter(cw, internalClass, prop, type);
}
}
for (Method m : _unsupportedMethods.values()) {
createUnimplementedMethod(cw, internalClass, m);
}
cw.visitEnd();
return cw.toByteArray();
}
/*
/**********************************************************
/* Internal methods, property discovery
/**********************************************************
*/
private static String getPropertyName(String methodName)
{
int prefixLen = methodName.startsWith("is") ? 2 : 3;
String body = methodName.substring(prefixLen);
StringBuilder sb = new StringBuilder(body);
sb.setCharAt(0, Character.toLowerCase(body.charAt(0)));
return sb.toString();
}
private static String buildGetterName(String fieldName) {
return "get" + fieldName.substring(0, 1).toUpperCase()+ fieldName.substring(1);
}
private static String buildSetterName(String fieldName) {
return "set" + fieldName.substring(0, 1).toUpperCase()+ fieldName.substring(1);
}
private static String getInternalClassName(String className) {
return className.replace(".", "/");
}
private void addGetter(Method m)
{
Property prop = findProperty(getPropertyName(m.getName()));
// only set if not yet set; we start with super class:
if (prop.getGetter() == null) {
prop.setGetter(m);
}
}
private void addSetter(Method m)
{
Property prop = findProperty(getPropertyName(m.getName()));
if (prop.getSetter() == null) {
prop.setSetter(m);
}
}
private Property findProperty(String propName)
{
Property prop = _beanProperties.get(propName);
if (prop == null) {
prop = new Property(propName);
_beanProperties.put(propName, prop);
}
return prop;
}
private final static boolean returnsBoolean(Method m)
{
Class> rt = m.getReturnType();
return (rt == Boolean.class || rt == Boolean.TYPE);
}
/*
/**********************************************************
/* Internal methods, bytecode generation
/**********************************************************
*/
private static void generateDefaultConstructor(ClassWriter cw, String superName)
{
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superName, "", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0); // don't care (real values: 1,1)
mv.visitEnd();
}
private static void createField(ClassWriter cw, Property prop, TypeDescription type)
{
String sig = type.hasGenerics() ? type.genericSignature() : null;
String desc = type.erasedSignature();
FieldVisitor fv = cw.visitField(ACC_PUBLIC, prop.getFieldName(), desc, sig, null);
fv.visitEnd();
}
private static void createSetter(ClassWriter cw, String internalClassName,
Property prop, TypeDescription propertyType)
{
String methodName;
String desc;
Method setter = prop.getSetter();
if (setter != null) { // easy, copy as is
desc = Type.getMethodDescriptor(setter);
methodName = setter.getName();
} else { // otherwise need to explicitly construct from property type (close enough)
desc = "("+ propertyType.erasedSignature() + ")V";
methodName = buildSetterName(prop.getName());
}
String sig = propertyType.hasGenerics() ? ("("+propertyType.genericSignature()+")V") : null;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, desc, sig, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // this
mv.visitVarInsn(propertyType.getLoadOpcode(), 1);
mv.visitFieldInsn(PUTFIELD, internalClassName, prop.getFieldName(), propertyType.erasedSignature());
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0); // don't care (real values: 2, 2)
mv.visitEnd();
}
private static void createGetter(ClassWriter cw, String internalClassName,
Property prop, TypeDescription propertyType)
{
String methodName;
String desc;
Method getter = prop.getGetter();
if (getter != null) { // easy, copy as is
desc = Type.getMethodDescriptor(getter);
methodName = getter.getName();
} else { // otherwise need to explicitly construct from property type (close enough)
desc = "()"+propertyType.erasedSignature();
methodName = buildGetterName(prop.getName());
}
String sig = propertyType.hasGenerics() ? ("()"+propertyType.genericSignature()) : null;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, desc, sig, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // load 'this'
mv.visitFieldInsn(GETFIELD, internalClassName, prop.getFieldName(), propertyType.erasedSignature());
mv.visitInsn(propertyType.getReturnOpcode());
mv.visitMaxs(0, 0); // don't care (real values: 1,1)
mv.visitEnd();
}
/**
* Builder for methods that just throw an exception, basically "unsupported
* operation" implementation.
*/
private static void createUnimplementedMethod(ClassWriter cw, String internalClassName,
Method method)
{
String exceptionName = getInternalClassName(UnsupportedOperationException.class.getName());
String sig = Type.getMethodDescriptor(method);
String name = method.getName();
// should we try to pass generic information?
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, sig, null, null);
mv.visitTypeInsn(NEW, exceptionName);
mv.visitInsn(DUP);
mv.visitLdcInsn("Unimplemented method '"+name+"' (not a setter/getter, could not materialize)");
mv.visitMethodInsn(INVOKESPECIAL, exceptionName, "", "(Ljava/lang/String;)V");
mv.visitInsn(ATHROW);
mv.visitMaxs(0, 0); // don't care (real values: 3, 1 + method.getParameterTypes().length);
mv.visitEnd();
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Bean that contains information about a single logical
* property, which consists of a getter and/or setter,
* and is used to generate getter, setter and matching
* backing field.
*/
private static class Property
{
protected final String _name;
protected final String _fieldName;
protected Method _getter;
protected Method _setter;
public Property(String name)
{
_name = name;
// Let's just prefix field name with single underscore for fun...
_fieldName = "_"+name;
}
public String getName() { return _name; }
public void setGetter(Method m) { _getter = m; }
public void setSetter(Method m) { _setter = m; }
public Method getGetter() { return _getter; }
public Method getSetter() { return _setter; }
public String getFieldName() {
return _fieldName;
}
/*
private static boolean isConcrete(Method m)
{
return m.getModifiers()
}
*/
public boolean hasConcreteGetter() {
return (_getter != null) && BeanUtil.isConcrete(_getter);
}
public boolean hasConcreteSetter() {
return (_setter != null) && BeanUtil.isConcrete(_setter);
}
private TypeDescription getterType(TypeFactory tf)
{
Class> context = _getter.getDeclaringClass();
return new TypeDescription(tf.constructType(_getter.getGenericReturnType(), context));
}
private TypeDescription setterType(TypeFactory tf)
{
Class> context = _setter.getDeclaringClass();
return new TypeDescription(tf.constructType(_setter.getGenericParameterTypes()[0], context));
}
public TypeDescription selectType(TypeFactory tf)
{
// First: if only know setter, or getter, use that one:
if (_getter == null) {
return setterType(tf);
}
if (_setter == null) {
return getterType(tf);
}
/* Otherwise must ensure they are compatible, choose more specific
* (most often setter - type)
*/
TypeDescription st = setterType(tf);
TypeDescription gt = getterType(tf);
TypeDescription specificType = TypeDescription.moreSpecificType(st, gt);
if (specificType == null) { // incompatible...
throw new IllegalArgumentException("Invalid property '"+getName()
+"': incompatible types for getter/setter ("
+gt+" vs "+st+")");
}
return specificType;
}
}
/**
* Helper bean used to encapsulate most details of type handling
*/
private static class TypeDescription
{
private final Type _asmType;
private JavaType _jacksonType;
/*
/**********************************************************
/* Construction
/**********************************************************
*/
public TypeDescription(JavaType type)
{
_jacksonType = type;
_asmType = Type.getType(type.getRawClass());
}
/*
/**********************************************************
/* Accessors
/**********************************************************
*/
public Class> getRawClass() { return _jacksonType.getRawClass(); }
public String erasedSignature() {
return _jacksonType.getErasedSignature();
}
public String genericSignature() {
return _jacksonType.getGenericSignature();
}
/**
* @return True if type has direct generic declaration (which may need
* to be copied)
*/
public boolean hasGenerics() {
return _jacksonType.hasGenericTypes();
}
/*
public boolean isPrimitive() {
return _signature.length() == 1;
}
*/
/*
public int getStoreOpcode() {
return _signatureType.getOpcode(ISTORE);
}
*/
public int getLoadOpcode() {
return _asmType.getOpcode(ILOAD);
}
public int getReturnOpcode() {
return _asmType.getOpcode(IRETURN);
}
@Override
public String toString() {
return _jacksonType.toString();
}
/*
/**********************************************************
/* Other methods
/**********************************************************
*/
public static TypeDescription moreSpecificType(TypeDescription desc1, TypeDescription desc2)
{
Class> c1 = desc1.getRawClass();
Class> c2 = desc2.getRawClass();
if (c1.isAssignableFrom(c2)) { // c2 more specific than c1
return desc2;
}
if (c2.isAssignableFrom(c1)) { // c1 more specific than c2
return desc1;
}
// not compatible, so:
return null;
}
}
}
jackson-src-1.9.2/src/mrbean/java/org/codehaus/jackson/mrbean/package-info.java 0000644 0001750 0001750 00000000345 11655120726 030042 0 ustar jamespage jamespage /**
* Package that implements "interface materializer" functionality, whereby
* abstract classes and interfaces can be used as-is, and framework constructs
* implementations as needed.
*/
package org.codehaus.jackson.mrbean;
jackson-src-1.9.2/src/tools/ 0000755 0001750 0001750 00000000000 11672662540 016352 5 ustar jamespage jamespage jackson-src-1.9.2/src/tools/GenerateIntDoc.java 0000644 0001750 0001750 00000003701 11655120726 022045 0 ustar jamespage jamespage
import java.io.*;
import java.util.Random;
import org.codehaus.jackson.*;
public class GenerateIntDoc
{
final static int AVG_ARRAY_LEN = 32;
private GenerateIntDoc() { }
private void generate(OutputStream out, int kbSize)
throws IOException
{
int bsize = kbSize * 1000;
// Let's first buffer in memory, to know exact length
ByteArrayOutputStream bos = new ByteArrayOutputStream(bsize + 500);
Random r = new Random(kbSize);
JsonGenerator gen = new JsonFactory().createJsonGenerator(bos, JsonEncoding.UTF8);
gen.writeStartArray(); // outermost array
gen.writeStartArray(); // inner array
do {
// First, do we close current, start new array?
if (r.nextInt(AVG_ARRAY_LEN) == 3) { // to get average array length of 16
gen.writeEndArray();
if (r.nextBoolean()) {
gen.writeRaw("\n");
}
gen.writeStartArray();
gen.flush();
}
// Then need to calculate number to output
int nr = r.nextInt(32);
if (r.nextBoolean()) {
nr *= r.nextInt(256); // up to 8k
if (r.nextBoolean()) {
nr *= r.nextInt(0x20000); // up to 1G
}
}
if (r.nextBoolean()) {
nr = -nr;
}
gen.writeNumber(nr);
} while (bos.size() < bsize);
gen.writeEndArray();
gen.writeEndArray();
gen.writeRaw("\n");
gen.close();
bos.writeTo(out);
}
public static void main(String[] args)
throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java test.GenerateIntDoc ");
System.exit(1);
}
new GenerateIntDoc().generate(System.out, Integer.parseInt(args[0]));
System.out.flush();
}
}
jackson-src-1.9.2/src/tools/TestSerializationError.java 0000644 0001750 0001750 00000001741 11655120726 023703 0 ustar jamespage jamespage import java.io.*;
import java.util.*;
import org.codehaus.jackson.map.*;
public class TestSerializationError
{
final static class OkBean {
ArrayList _beans;
public OkBean() {
_beans = new ArrayList();
_beans.add(new NonBean[] { new NonBean() });;
}
public List getNonBeans() { return _beans; }
}
final static class NonBean {
public NonBean() { }
}
public static void main(String[] args)
throws Exception
{
ObjectMapper mapper = new ObjectMapper();
ArrayList list = new ArrayList();
list.add(new OkBean());
HashMap map = new HashMap();
map.put("foo", list);
// should error out, but what do we see there?
StringWriter sw = new StringWriter();
mapper.writeValue(sw, map);
System.out.println("Odd, didn't fail, got: "+sw.toString());
}
}
jackson-src-1.9.2/src/tools/CheckNumberSpeed.java 0000644 0001750 0001750 00000005022 11655120726 022357 0 ustar jamespage jamespage import java.math.BigInteger;
/**
* Simple test to see what is the performance difference between converting
* simple int values into different wrapper types.
*/
public final class CheckNumberSpeed
{
final int[] data;
final static int REPS = 2000;
private CheckNumberSpeed() {
data = new int[1200];
for (int i = 0; i < data.length; ++i) {
// mostly small positive numbers, some negative
data[i] = i - 100;
}
}
public void test() throws Exception
{
int nr = 0;
while (true) {
int type = (nr++) % 4;
long start = System.nanoTime();
String str;
Object o;
switch (type) {
case 0:
o = testIntsValueOf(data);
str = "int/share";
break;
case 1:
o = testIntsCreate(data);
str = "int/create";
break;
case 2:
o = testLongs(data);
str = "longs";
break;
default:
o = testBigInts(data);
str = "BigInts";
break;
}
long time = System.nanoTime() - start;
time = time >> 10; // from nano to micro
System.out.println("Time for '"+str+"' -> "+time+" us (hash: "+o.hashCode()+")");
Thread.sleep(200L);
}
}
Integer testIntsValueOf(int[] ints)
{
Integer v = null;
int reps = REPS;
while (--reps > 0) {
for (int value : ints) {
v = Integer.valueOf(value);
}
}
return v;
}
Integer testIntsCreate(int[] ints)
{
Integer v = null;
int reps = REPS;
while (--reps > 0) {
for (int value : ints) {
v = new Integer(value);
}
}
return v;
}
Long testLongs(int[] ints)
{
Long v = null;
int reps = REPS;
while (--reps > 0) {
for (int value : ints) {
v = Long.valueOf(value);
}
}
return v;
}
BigInteger testBigInts(int[] ints)
{
BigInteger v = null;
int reps = REPS;
while (--reps > 0) {
for (int value : ints) {
v = BigInteger.valueOf(value);
}
}
return v;
}
public static void main(String[] args) throws Exception
{
new CheckNumberSpeed().test();
}
}
jackson-src-1.9.2/src/tools/TestReflection.java 0000644 0001750 0001750 00000007655 11655120726 022160 0 ustar jamespage jamespage
import java.lang.reflect.*;
import java.util.*;
/**
* Manually run class used for figuring out exactly how JDK/generics
* expose type information.
*/
@SuppressWarnings("serial")
public class TestReflection
extends ArrayList
{
public static void main(String[] args)
throws Exception
{
TestReflection test = new TestReflection();
test.test0();
}
@SuppressWarnings("unused")
private void test() throws Exception
{
//Class> cls1 = ArrayList.class;
//Class> cls2 = new ArrayList().getClass();
Class> cls1 = ArrayList[].class;
Class> cls2 = List[].class;
System.out.println("Cls 1 = "+cls1);
System.out.println("Cls 2 = "+cls2);
System.out.println("1 === 2? "+(cls1 == cls2));
System.out.println("1 == 2? "+(cls1.equals(cls2)));
}
private void test0() throws Exception
{
testMethod(getClass().getMethod("methodPlain"));
testMethod(getClass().getMethod("methodParam"));
testMethod(getClass().getMethod("methodArrayPlain"));
testMethod(getClass().getMethod("methodArrayParam"));
testMethod(getClass().getMethod("methodWildcard"));
}
public LinkedHashMap, Boolean[]> methodParam() { return null; }
public Integer methodPlain() { return null; }
public int[][][] methodArrayPlain() { return null; }
public LinkedList>>[][][] methodArrayParam() { return null; }
public Set extends List>> methodWildcard() { return null; }
private void testMethod(Method m) throws Exception
{
System.out.println("Return type for ["+m+"]:");
//int ix = 0;
Type type = m.getGenericReturnType();
printType(1, type);
}
private void indent(int amount)
{
for (int i = 0; i < amount; ++i) {
System.out.print(" ");
}
}
private void printType(int indent, Type type)
{
indent(indent);
if (type == null) {
System.out.println("[null]");
}
else if (type instanceof Class>) {
System.out.println("simple class: "+((Class>) type).getName());
}
else if (type instanceof GenericArrayType) {
System.out.println("array type, component:");
GenericArrayType at = (GenericArrayType) type;
printType(indent+1, at.getGenericComponentType());
}
else if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
System.out.println("parameterized type:");
++indent;
indent(indent);
System.out.println("owner -> ");
printType(indent+1, pt.getOwnerType());
indent(indent);
System.out.println("raw -> ");
printType(indent+1, pt.getRawType());
int ix = 0;
for (Type t2 : pt.getActualTypeArguments()) {
++ix;
indent(indent);
System.out.println("actual #"+ix+" -> ");
printType(indent+1, t2);
}
}
else if (type instanceof TypeVariable>) {
TypeVariable> v = (TypeVariable>) type;
System.out.println("type variable '"+v.getName()+"'");
}
else if (type instanceof WildcardType) {
WildcardType w = (WildcardType) type;
System.out.println("wildcard type:");
++indent;
indent(indent);
System.out.println("lower bounds[] -> ");
for (Type t2 : w.getLowerBounds()) {
printType(indent+1, t2);
}
indent(indent);
System.out.println("upper bounds[] -> ");
for (Type t2 : w.getUpperBounds()) {
printType(indent+1, t2);
}
} else {
throw new IllegalArgumentException("Weird type! "+type.getClass());
}
}
}
jackson-src-1.9.2/src/tools/TestObjectMapper.java 0000644 0001750 0001750 00000002342 11655120726 022425 0 ustar jamespage jamespage import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.*;
public class TestObjectMapper
{
private TestObjectMapper() { }
public static void main(String[] args)
throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java test.TestObjectMapper ");
System.exit(1);
}
FileInputStream in = new FileInputStream(new File(args[0]));
JsonFactory f = new JsonFactory();
JsonParser jp = f.createJsonParser(in);
ObjectMapper map = new ObjectMapper();
// 06-Jan-2009, tatu: This will mean "untyped" mapping
Object result = map.readValue(jp, Object.class);
jp.close();
System.out.println("Read result ("+(result.getClass())+"): <"+result+">");
StringWriter sw = new StringWriter();
JsonGenerator jg = f.createJsonGenerator(sw);
try {
map.writeValue(jg, result);
} catch (Exception e) {
try { jg.flush(); } catch (IOException ioe) { }
System.err.println("Error, intermediate result = |"+sw+"|");
throw e;
}
jg.close();
System.out.println("Write result: <"+sw.toString()+">");
}
}
jackson-src-1.9.2/src/tools/TestBeanMapper.java 0000644 0001750 0001750 00000003174 11655120726 022070 0 ustar jamespage jamespage
import java.io.*;
import java.util.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.*;
public class TestBeanMapper
{
private TestBeanMapper() { }
private void test() throws Exception
{
JsonFactory f = new JsonFactory();
ObjectMapper jmap = new ObjectMapper();
Object foo = new MyObject(5, -90, "Desc");
StringWriter sw = new StringWriter();
JsonGenerator jg = f.createJsonGenerator(sw);
try {
jmap.writeValue(jg, foo);
} catch (Exception e) {
try { jg.flush(); } catch (IOException ioe) { }
System.err.println("Error, intermediate result = |"+sw+"|");
throw e;
}
jg.close();
System.out.println("Write result: <"+sw.toString()+">");
}
public static void main(String[] args) throws Exception
{
new TestBeanMapper().test();
}
@SuppressWarnings("unused")
private final class MyObject
{
final int _x, _y;
final String _desc;
public MyObject(int x, int y, String desc)
{
_x = x;
_y = y;
_desc = desc;
}
public String getDesc() { return _desc; }
public int getX() { return _x; }
public int getY() { return _y; }
public int[] getFoobar() { return new int[] { 1, 2, 3 }; }
public MyObject2 getNext() {
return new MyObject2();
}
public Collection> getMisc() {
return new ArrayList();
}
}
final static class MyObject2 {
public String getName() { return "dummy"; }
}
}
jackson-src-1.9.2/src/tools/GenerateDoubleDoc.java 0000644 0001750 0001750 00000003444 11655120726 022531 0 ustar jamespage jamespage
import java.io.*;
import java.util.Random;
import org.codehaus.jackson.*;
public class GenerateDoubleDoc
{
final static int AVG_ARRAY_LEN = 32;
private GenerateDoubleDoc() { }
private void generate(OutputStream out, int kbSize)
throws IOException
{
int bsize = kbSize * 1000;
// Let's first buffer in memory, to know exact length
ByteArrayOutputStream bos = new ByteArrayOutputStream(bsize + 500);
Random r = new Random(kbSize);
JsonGenerator gen = new JsonFactory().createJsonGenerator(bos, JsonEncoding.UTF8);
gen.writeStartArray(); // outermost array
gen.writeStartArray(); // inner array
do {
// First, do we close current, start new array?
if (r.nextInt(AVG_ARRAY_LEN) == 3) { // to get average array length of 16
gen.writeEndArray();
if (r.nextBoolean()) {
gen.writeRaw("\n");
}
gen.writeStartArray();
gen.flush();
}
// Then need to calculate number to output
float f;
do {
f = Float.intBitsToFloat(r.nextInt());
} while (Double.isNaN(f) || Double.isInfinite(f));
gen.writeNumber(f);
} while (bos.size() < bsize);
gen.writeEndArray();
gen.writeEndArray();
gen.writeRaw("\n");
gen.close();
bos.writeTo(out);
}
public static void main(String[] args)
throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java test.GenerateDoubleDoc ");
System.exit(1);
}
new GenerateDoubleDoc().generate(System.out, Integer.parseInt(args[0]));
System.out.flush();
}
}
jackson-src-1.9.2/src/tools/TestParser.java 0000644 0001750 0001750 00000003244 11655120726 021310 0 ustar jamespage jamespage
import java.io.*;
import org.codehaus.jackson.*;
public class TestParser
{
public static void main(String[] args)
throws IOException
{
if (args.length != 1) {
System.err.println("Usage: java ... TestParser [file]");
System.exit(1);
}
JsonFactory f = new JsonFactory();
// [JACKSON-259]: ability to suppress canonicalization
f.disable(JsonParser.Feature.CANONICALIZE_FIELD_NAMES);
JsonParser jp = f.createJsonParser(new File(args[0]));
//JsonParser jp = f.createJsonParser(new InputStreamReader(new FileInputStream(args[0]), "UTF-8"));
jp.enable(JsonParser.Feature.ALLOW_COMMENTS);
System.out.println("Parser: "+jp);
JsonToken t;
while ((t = jp.nextToken()) != null) {
System.out.print("Token: "+t);
if (t == JsonToken.FIELD_NAME) {
//String name = new String(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
String name = jp.getCurrentName();
System.out.print(", name = '"+name+"' (len: "+name.length()+")");
// Troubleshoot:
int ix = name.indexOf('\0');
if (ix >= 0) {
throw new RuntimeException("Null byte for name, at index #"+ix);
}
} else if (t.toString().startsWith("VALUE")) {
if (t == JsonToken.VALUE_STRING) {
System.out.print(" [len: "+jp.getTextLength()+"]");
}
System.out.print(", value = \""+jp.getText()+"\"");
}
System.out.println();
}
jp.close();
}
}
jackson-src-1.9.2/src/tools/TestGenerator.java 0000644 0001750 0001750 00000001432 11655120726 021777 0 ustar jamespage jamespage
import java.io.*;
import java.math.BigDecimal;
import org.codehaus.jackson.*;
public class TestGenerator
{
private TestGenerator() { }
public static void main(String[] args)
throws Exception
{
StringWriter strw = new StringWriter();
JsonGenerator jg = new JsonFactory().createJsonGenerator(strw);
jg.writeStartObject();
jg.writeFieldName("pi");
//jg.writeNumber(new BigDecimal("1.23"));
jg.writeNumber(new BigDecimal("1.23"));
jg.writeFieldName("binary");
byte[] data = "Test string!".getBytes("UTF-8");
jg.writeBinary(data);
// what happens if we leave this out?
//jg.writeEndObject();
jg.close();
System.out.println("Result: <"+strw.toString()+">");
}
}
jackson-src-1.9.2/src/tools/TestMapSpeed.java 0000644 0001750 0001750 00000013014 11655120726 021546 0 ustar jamespage jamespage import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.*;
public final class TestMapSpeed
{
/**
* Number of repetitions to run per test. Dynamically variable,
* based on observed runtime, to try to keep it high enough.
*/
private int REPS;
/**
* Let's keep per-run times above 50 milliseconds
*/
//final static int MIN_RUN_TIME = 50;
final static int MIN_RUN_TIME = 5;
/**
* Let's keep per-run times below 300 milliseconds
*/
//final static int MAX_RUN_TIME = 300;
final static int MAX_RUN_TIME = 1000;
private final static int TEST_PER_GC = 17;
final JsonFactory _factory;
final ObjectMapper _mapper;
final Object _objectToMap;
private TestMapSpeed(byte[] data)
throws Exception
{
_factory = new JsonFactory();
_mapper = new ObjectMapper();
JsonParser jp = _factory.createJsonParser(data, 0, data.length);
_objectToMap = _mapper.readValue(jp, Object.class);
jp.close();
// Ok how should we guestimate speed... perhaps from data size?
REPS = 100 + ((8 * 1000 * 1000) / data.length);
System.out.println("Based on size, will use "+REPS+" repetitions");
}
protected int test()
throws Exception
{
int i = 0;
int total = 0;
final int TEST_CASES = 2;
final ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
while (true) {
try { Thread.sleep(150L); } catch (InterruptedException ie) { }
int round = (i++ % TEST_CASES);
long now = System.currentTimeMillis();
String msg;
int sum = 0;
//round = 1; // testing just old or new?
switch (round) {
case 0:
msg = "Map using OLD";
sum = testOld(REPS, bos);
break;
case 1:
msg = "Map using NEW";
sum = testNew(REPS, bos);
break;
default:
throw new Error("Internal error");
}
now = System.currentTimeMillis() - now;
if (round == 0) {
System.out.println();
}
System.out.println("Test '"+msg+"' -> "+now+" msecs ("
+sum+" -> "+(total & 0xFF)+").");
total += sum;
if ((i % TEST_PER_GC) == 0) {
System.out.println("[GC]");
try { Thread.sleep(100L); } catch (InterruptedException ie) { }
System.gc();
try { Thread.sleep(200L); } catch (InterruptedException ie) { }
/* One more tweak: let's add load if things start
* running too fast or slow, to try to get sweet range
* of 50 to 250 millisseconds
*/
if (now < MIN_RUN_TIME) {
REPS += (REPS / 5); // 20% up
System.out.println("[NOTE: increasing reps, now: "+REPS+"]");
try { Thread.sleep(200L); } catch (InterruptedException ie) { }
} else if (now > MAX_RUN_TIME && i > 20) {
/* Let's reduce load slower than increase; also,
* due to initial warmup, let's not adjust until
* we've gone through a few cycles
*/
REPS -= (REPS / 10); // 10% down
System.out.println("[NOTE: decreasing reps, now: "+REPS+"]");
try { Thread.sleep(200L); } catch (InterruptedException ie) { }
}
}
}
}
/*
/////////////////////////////////////////////////////////
// Actual value type access, ones via Stax 1.0
/////////////////////////////////////////////////////////
*/
protected int testOld(int reps, ByteArrayOutputStream out)
throws Exception
{
int total = 0;
for (int i = 0; i < reps; ++i) {
out.reset();
JsonGenerator jg = _factory.createJsonGenerator(out, JsonEncoding.UTF8);
_mapper.writeValue(jg, _objectToMap);
jg.close();
total = out.size();
}
return total;
}
protected int testNew(int reps, ByteArrayOutputStream out)
throws Exception
{
int total = 0;
for (int i = 0; i < reps; ++i) {
out.reset();
JsonGenerator jg = _factory.createJsonGenerator(out, JsonEncoding.UTF8);
_mapper.writeValue(jg, _objectToMap);
jg.close();
total = out.size();
}
return total;
}
/*
/////////////////////////////////////////////////////////
// Helper methods
/////////////////////////////////////////////////////////
*/
static byte[] readData(File file) throws IOException
{
InputStream fin = new FileInputStream(file);
byte[] buf = new byte[4000];
ByteArrayOutputStream bos = new ByteArrayOutputStream(4000);
int count;
while ((count = fin.read(buf)) > 0) {
bos.write(buf, 0, count);
}
fin.close();
return bos.toByteArray();
}
public static void main(String[] args)
throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java ... ");
System.exit(1);
}
byte[] data = readData(new File(args[0]));
System.out.println(" -> "+data.length+" bytes read.");
new TestMapSpeed(data).test();
}
}
jackson-src-1.9.2/src/tools/VerifySmileDocs.java 0000644 0001750 0001750 00000012227 11655120726 022264 0 ustar jamespage jamespage import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.smile.SmileFactory;
import org.codehaus.jackson.smile.SmileGenerator;
/**
* Helper test class that will check to see that we can round-trip
* JSON-to-Smile-to-JSON, without loss of data.
*/
public class VerifySmileDocs
{
private final static JsonFactory jsonFactory = new JsonFactory();
private final static SmileFactory smileFactoryWithNoBackrefs = new SmileFactory();
{
smileFactoryWithNoBackrefs.configure(SmileGenerator.Feature.CHECK_SHARED_NAMES, false);
smileFactoryWithNoBackrefs.configure(SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES, false);
}
private final static SmileFactory jsonFactoryWithBackrefs = new SmileFactory();
{
jsonFactoryWithBackrefs.configure(SmileGenerator.Feature.CHECK_SHARED_NAMES, true);
jsonFactoryWithBackrefs.configure(SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES, true);
}
private VerifySmileDocs() {}
private void verifyAll(File inputDir) throws IOException
{
File[] files = inputDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
int i = name.lastIndexOf('.');
if (i > 0) {
String suffix = name.substring(i+1);
if ("json".equals(suffix) || "jsn".equals(suffix)) {
return true;
}
}
return false;
}
});
if (files.length == 0) {
throw new IOException("No files with suffix '.json' or '.jsn' found from under '" +
inputDir.getAbsolutePath()+"'");
}
System.out.printf("Found %d JSON files to test with...\n", files.length);
int failures = 0;
for (File file : files) {
System.out.printf(" verifying file '%s'...", file.getName());
if (!verify(file, false) || !verify(file, true)) {
++failures;
} else {
System.out.println(" OK");
}
}
System.out.printf("Done: %d files had issues\n", failures);
}
private boolean verify(File inputFile, boolean backrefs) throws IOException
{
JsonFactory smileFactory = backrefs ? jsonFactoryWithBackrefs : smileFactoryWithNoBackrefs;
byte[] smileDoc = convertToSmile(inputFile, smileFactory);
JsonParser jpWithDiff = compareJsonToSmile(inputFile, smileDoc, smileFactory);
if (jpWithDiff != null) {
System.err.printf(" events differ (expected %s), location (backrefs: %s): %s\n",
String.valueOf(jpWithDiff.getCurrentToken()),
backrefs,
inputFile.getName());
jpWithDiff.close();
return false;
}
return true;
}
private byte[] convertToSmile(File inputFile, JsonFactory outputFactory) throws IOException
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
JsonParser jp = jsonFactory.createJsonParser(inputFile);
JsonGenerator jgen = outputFactory.createJsonGenerator(bytes);
while (jp.nextToken() != null) {
jgen.copyCurrentEvent(jp);
}
jp.close();
jgen.close();
return bytes.toByteArray();
}
private JsonParser compareJsonToSmile(File inputFile, byte[] smileBytes,
JsonFactory smileFactory) throws IOException
{
JsonParser jsonParser = jsonFactory.createJsonParser(inputFile);
JsonParser smileParser = smileFactory.createJsonParser(smileBytes);
JsonToken t1;
while ((t1 = jsonParser.nextToken()) != null) {
JsonToken t2 = smileParser.nextToken();
// first: token types must match
if (t1 != t2) {
return jsonParser;
}
// and second, values as well
switch (t1) {
case VALUE_STRING:
case FIELD_NAME:
if (!jsonParser.getText().equals(smileParser.getText())) {
return jsonParser;
}
break;
case VALUE_NUMBER_INT:
if (jsonParser.getLongValue() != smileParser.getLongValue()) {
return jsonParser;
}
break;
case VALUE_NUMBER_FLOAT:
if (jsonParser.getDoubleValue() != smileParser.getDoubleValue()) {
return jsonParser;
}
break;
// others are fine:
// Boolean values are distinct tokens;
// Object/Array start/end likewise
}
}
if (t1 == null) {
if (smileParser.nextToken() != null) {
return jsonParser;
}
}
jsonParser.close();
smileParser.close();
return null;
}
public static void main(String[] args) throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java ... [input-dir]");
System.exit(1);
}
new VerifySmileDocs().verifyAll(new File(args[0]));
}
}
jackson-src-1.9.2/src/java/ 0000755 0001750 0001750 00000000000 11655120726 016127 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/ 0000755 0001750 0001750 00000000000 11655120726 016716 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/ 0000755 0001750 0001750 00000000000 11655120726 020511 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/ 0000755 0001750 0001750 00000000000 11672662540 022145 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonEncoding.java 0000644 0001750 0001750 00000002726 11655120726 025373 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* Enumeration that defines legal encodings that can be used
* for JSON content, based on list of allowed encodings from
* JSON specification .
*
* Note: if application want to explicitly disregard Encoding
* limitations (to read in JSON encoded using an encoding not
* listed as allowed), they can use {@link java.io.Reader} /
* {@link java.io.Writer} instances as input
*/
public enum JsonEncoding {
UTF8("UTF-8", false), // N/A for big-endian, really
UTF16_BE("UTF-16BE", true),
UTF16_LE("UTF-16LE", false),
UTF32_BE("UTF-32BE", true),
UTF32_LE("UTF-32LE", false)
;
protected final String _javaName;
protected final boolean _bigEndian;
JsonEncoding(String javaName, boolean bigEndian)
{
_javaName = javaName;
_bigEndian = bigEndian;
}
/**
* Method for accessing encoding name that JDK will support.
*
* @return Matching encoding name that JDK will support.
*/
public String getJavaName() { return _javaName; }
/**
* Whether encoding is big-endian (if encoding supports such
* notion). If no such distinction is made (as is the case for
* {@link #UTF8}), return value is undefined.
*
* @return True for big-endian encodings; false for little-endian
* (or if not applicable)
*/
public boolean isBigEndian() { return _bigEndian; }
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonFactory.java 0000644 0001750 0001750 00000077014 11655120726 025256 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson;
import java.io.*;
import java.lang.ref.SoftReference;
import java.net.URL;
import org.codehaus.jackson.format.InputAccessor;
import org.codehaus.jackson.format.MatchStrength;
import org.codehaus.jackson.io.*;
import org.codehaus.jackson.impl.ByteSourceBootstrapper;
import org.codehaus.jackson.impl.ReaderBasedParser;
import org.codehaus.jackson.impl.Utf8Generator;
import org.codehaus.jackson.impl.WriterBasedGenerator;
import org.codehaus.jackson.sym.BytesToNameCanonicalizer;
import org.codehaus.jackson.sym.CharsToNameCanonicalizer;
import org.codehaus.jackson.util.BufferRecycler;
import org.codehaus.jackson.util.VersionUtil;
/**
* The main factory class of Jackson package, used to configure and
* construct reader (aka parser, {@link JsonParser})
* and writer (aka generator, {@link JsonGenerator})
* instances.
*
* Factory instances are thread-safe and reusable after configuration
* (if any). Typically applications and services use only a single
* globally shared factory instance, unless they need differently
* configured factories. Factory reuse is important if efficiency matters;
* most recycling of expensive construct is done on per-factory basis.
*
* Creation of a factory instance is a light-weight operation,
* and since there is no need for pluggable alternative implementations
* (as there is no "standard" JSON processor API to implement),
* the default constructor is used for constructing factory
* instances.
*
* @author Tatu Saloranta
*/
public class JsonFactory implements Versioned
{
/**
* 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 parser features that are enabled
* by default.
*/
final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults();
/**
* Bitfield (set of flags) of all generator features that are enabled
* by default.
*/
final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults();
/*
/**********************************************************
/* Buffer, symbol table management
/**********************************************************
*/
/**
* This ThreadLocal
contains a {@link java.lang.ref.SoftRerefence}
* 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 CharsToNameCanonicalizer _rootCharSymbols = CharsToNameCanonicalizer.createRoot();
/**
* Alternative to the basic symbol table, some stream-based
* parsers use different name canonicalization method.
*
* TODO: should clean up this; looks messy having 2 alternatives
* with not very clear differences.
*/
protected BytesToNameCanonicalizer _rootByteSymbols = BytesToNameCanonicalizer.createRoot();
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Object that implements conversion functionality between
* Java objects and JSON content. For base JsonFactory implementation
* usually not set by default, but can be explicitly set.
* Sub-classes (like @link org.codehaus.jackson.map.MappingJsonFactory}
* usually provide an implementation.
*/
protected ObjectCodec _objectCodec;
/**
* Currently enabled 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.
*
* @since 1.8
*/
protected CharacterEscapes _characterEscapes;
/**
* Optional helper object that may decorate input sources, to do
* additional processing on input during parsing.
*
* @since 1.8
*/
protected InputDecorator _inputDecorator;
/**
* Optional helper object that may decorate output object, to do
* additional processing on output during content generation.
*
* @since 1.8
*/
protected OutputDecorator _outputDecorator;
/*
/**********************************************************
/* 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; }
/*
/**********************************************************
/* Format detection functionality (since 1.8)
/**********************************************************
*/
/**
* 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
*
* @since 1.8
*/
public String getFormatName()
{
/* Somewhat nasty check: since we can't make this abstract
* (due to backwards compatibility concerns), need to prevent
* format name "leakage"
*/
if (getClass() == JsonFactory.class) {
return FORMAT_NAME_JSON;
}
return null;
}
public MatchStrength hasFormat(InputAccessor acc) throws IOException
{
// since we can't keep this abstract, only implement for "vanilla" instance
if (getClass() == JsonFactory.class) {
return hasJSONFormat(acc);
}
return null;
}
protected MatchStrength hasJSONFormat(InputAccessor acc) throws IOException
{
return ByteSourceBootstrapper.hasJSONFormat(acc);
}
/*
/**********************************************************
/* Versioned
/**********************************************************
*/
@Override
public Version version() {
// VERSION is included under impl, so can't pass this class:
return VersionUtil.versionFor(Utf8Generator.class);
}
/*
/**********************************************************
/* Configuration, parser settings
/**********************************************************
*/
/**
* Method for enabling or disabling specified parser feature
* (check {@link JsonParser.Feature} for list of features)
*
* @since 1.2
*/
public final JsonFactory configure(JsonParser.Feature f, boolean state)
{
if (state) {
enable(f);
} else {
disable(f);
}
return this;
}
/**
* Method for enabling specified parser feature
* (check {@link JsonParser.Feature} for list of features)
*
* @since 1.2
*/
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)
*
* @since 1.2
*/
public JsonFactory disable(JsonParser.Feature f) {
_parserFeatures &= ~f.getMask();
return this;
}
/**
* Checked whether specified parser feature is enabled.
*
* @since 1.2
*/
public final boolean isEnabled(JsonParser.Feature f) {
return (_parserFeatures & f.getMask()) != 0;
}
// // // Older deprecated (as of 1.2) methods
/**
* @deprecated Use {@link #enable(JsonParser.Feature)} instead
*/
@SuppressWarnings("dep-ann")
public final void enableParserFeature(JsonParser.Feature f) {
enable(f);
}
/**
* @deprecated Use {@link #disable(JsonParser.Feature)} instead
*/
@SuppressWarnings("dep-ann")
public final void disableParserFeature(JsonParser.Feature f) {
disable(f);
}
/**
* @deprecated Use {@link #configure(JsonParser.Feature, boolean)} instead
*/
@SuppressWarnings("dep-ann")
public final void setParserFeature(JsonParser.Feature f, boolean state) {
configure(f, state);
}
/**
* @deprecated Use {@link #isEnabled(JsonParser.Feature)} instead
*/
@SuppressWarnings("dep-ann")
public final boolean isParserFeatureEnabled(JsonParser.Feature f) {
return (_parserFeatures & f.getMask()) != 0;
}
/**
* Method for getting currently configured input decorator (if any;
* there is no default decorator).
*
* @since 1.8
*/
public InputDecorator getInputDecorator() {
return _inputDecorator;
}
/**
* Method for overriding currently configured input decorator
*
* @since 1.8
*/
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)
*
* @since 1.2
*/
public final JsonFactory configure(JsonGenerator.Feature f, boolean state) {
if (state) {
enable(f);
} else {
disable(f);
}
return this;
}
/**
* Method for enabling specified generator features
* (check {@link JsonGenerator.Feature} for list of features)
*
* @since 1.2
*/
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)
*
* @since 1.2
*/
public JsonFactory disable(JsonGenerator.Feature f) {
_generatorFeatures &= ~f.getMask();
return this;
}
/**
* Check whether specified generator feature is enabled.
*
* @since 1.2
*/
public final boolean isEnabled(JsonGenerator.Feature f) {
return (_generatorFeatures & f.getMask()) != 0;
}
// // // Older deprecated (as of 1.2) methods
/**
* @deprecated Use {@link #enable(JsonGenerator.Feature)} instead
*/
@Deprecated
public final void enableGeneratorFeature(JsonGenerator.Feature f) {
enable(f);
}
/**
* @deprecated Use {@link #disable(JsonGenerator.Feature)} instead
*/
@Deprecated
public final void disableGeneratorFeature(JsonGenerator.Feature f) {
disable(f);
}
/**
* @deprecated Use {@link #configure(JsonGenerator.Feature, boolean)} instead
*/
@Deprecated
public final void setGeneratorFeature(JsonGenerator.Feature f, boolean state) {
configure(f, state);
}
/**
* @deprecated Use {@link #isEnabled(JsonGenerator.Feature)} instead
*/
@Deprecated
public final boolean isGeneratorFeatureEnabled(JsonGenerator.Feature f) {
return isEnabled(f);
}
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*
* @since 1.8
*/
public CharacterEscapes getCharacterEscapes() {
return _characterEscapes;
}
/**
* Method for defining custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*
* @since 1.8
*/
public JsonFactory setCharacterEscapes(CharacterEscapes esc) {
_characterEscapes = esc;
return this;
}
/**
* Method for getting currently configured output decorator (if any;
* there is no default decorator).
*
* @since 1.8
*/
public OutputDecorator getOutputDecorator() {
return _outputDecorator;
}
/**
* Method for overriding currently configured output decorator
*
* @since 1.8
*/
public JsonFactory setOutputDecorator(OutputDecorator d) {
_outputDecorator = d;
return this;
}
/*
/**********************************************************
/* Configuration, other
/**********************************************************
*/
/**
* Method for associating a {@link ObjectCodec} (typically
* a {@link org.codehaus.jackson.map.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; }
/*
/**********************************************************
/* Reader factories
/**********************************************************
*/
/**
* Method for constructing JSON parser instance to parse
* contents of specified file. Encoding is auto-detected
* from contents according to JSON specification recommended
* mechanism.
*
* Underlying input stream (needed for reading contents)
* will be owned (and managed, i.e. closed as need be) by
* the parser, since caller has no access to it.
*
* @param f File that contains JSON content to parse
*/
public JsonParser createJsonParser(File f)
throws IOException, JsonParseException
{
// true, since we create InputStream from File
IOContext ctxt = _createContext(f, true);
InputStream in = new FileInputStream(f);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
in = _inputDecorator.decorate(ctxt, in);
}
return _createJsonParser(in, ctxt);
}
/**
* Method for constructing JSON parser instance to parse
* contents of resource reference by given URL.
* Encoding is auto-detected
* from contents according to JSON specification recommended
* mechanism.
*
* Underlying input stream (needed for reading contents)
* will be owned (and managed, i.e. closed as need be) by
* the parser, since caller has no access to it.
*
* @param url URL pointing to resource that contains JSON content to parse
*/
public JsonParser createJsonParser(URL url)
throws IOException, JsonParseException
{
// true, since we create InputStream from URL
IOContext ctxt = _createContext(url, true);
InputStream in = _optimizedStreamFromURL(url);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
in = _inputDecorator.decorate(ctxt, in);
}
return _createJsonParser(in, ctxt);
}
/**
* Method for constructing JSON parser instance to parse
* the contents accessed via specified input stream.
*
* The input stream will not be owned by
* the parser, it will still be managed (i.e. closed if
* end-of-stream is reacher, or parser close method called)
* if (and only if) {@link org.codehaus.jackson.JsonParser.Feature#AUTO_CLOSE_SOURCE}
* is enabled.
*
* Note: no encoding argument is taken since it can always be
* auto-detected as suggested by Json RFC.
*
* @param in InputStream to use for reading JSON content to parse
*/
public JsonParser createJsonParser(InputStream in)
throws IOException, JsonParseException
{
IOContext ctxt = _createContext(in, false);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
in = _inputDecorator.decorate(ctxt, in);
}
return _createJsonParser(in, ctxt);
}
/**
* Method for constructing parser for parsing
* the contents accessed via specified Reader.
* The read stream will not be owned by
* the parser, it will still be managed (i.e. closed if
* end-of-stream is reacher, or parser close method called)
* if (and only if) {@link org.codehaus.jackson.JsonParser.Feature#AUTO_CLOSE_SOURCE}
* is enabled.
*
*
* @param r Reader to use for reading JSON content to parse
*/
public JsonParser createJsonParser(Reader r)
throws IOException, JsonParseException
{
// false -> we do NOT own Reader (did not create it)
IOContext ctxt = _createContext(r, false);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
r = _inputDecorator.decorate(ctxt, r);
}
return _createJsonParser(r, ctxt);
}
/**
* Method for constructing parser for parsing
* the contents of given byte array.
*/
public JsonParser createJsonParser(byte[] data)
throws IOException, JsonParseException
{
IOContext ctxt = _createContext(data, true);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length);
if (in != null) {
return _createJsonParser(in, ctxt);
}
}
return _createJsonParser(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
*/
public JsonParser createJsonParser(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 _createJsonParser(in, ctxt);
}
}
return _createJsonParser(data, offset, len, ctxt);
}
/**
* Method for constructing parser for parsing
* contens of given String.
*/
public JsonParser createJsonParser(String content)
throws IOException, JsonParseException
{
Reader r = new StringReader(content);
// true -> we own the Reader (and must close); not a big deal
IOContext ctxt = _createContext(r, true);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
r = _inputDecorator.decorate(ctxt, r);
}
return _createJsonParser(r, ctxt);
}
/*
/**********************************************************
/* Generator factories
/**********************************************************
*/
/**
* Method for constructing JSON generator for writing JSON content
* using specified output stream.
* Encoding to use must be specified, and needs to be one of available
* types (as per JSON specification).
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the output stream when
* {@link JsonGenerator#close} is called (unless auto-closing
* feature,
* {@link org.codehaus.jackson.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
*/
public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc)
throws IOException
{
// false -> we won't manage the stream unless explicitly directed to
IOContext ctxt = _createContext(out, false);
ctxt.setEncoding(enc);
if (enc == JsonEncoding.UTF8) {
// [JACKSON-512]: allow wrapping with _outputDecorator
if (_outputDecorator != null) {
out = _outputDecorator.decorate(ctxt, out);
}
return _createUTF8JsonGenerator(out, ctxt);
}
Writer w = _createWriter(out, enc, ctxt);
// [JACKSON-512]: allow wrapping with _outputDecorator
if (_outputDecorator != null) {
w = _outputDecorator.decorate(ctxt, w);
}
return _createJsonGenerator(w, ctxt);
}
/**
* 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 org.codehaus.jackson.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled).
* Using application needs to close it explicitly.
*
* @param out Writer to use for writing JSON content
*/
public JsonGenerator createJsonGenerator(Writer out)
throws IOException
{
IOContext ctxt = _createContext(out, false);
// [JACKSON-512]: allow wrapping with _outputDecorator
if (_outputDecorator != null) {
out = _outputDecorator.decorate(ctxt, out);
}
return _createJsonGenerator(out, 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 1.8
*/
public JsonGenerator createJsonGenerator(OutputStream out) throws IOException {
return createJsonGenerator(out, JsonEncoding.UTF8);
}
/**
* Method for constructing JSON generator for writing JSON content
* to specified file, overwriting contents it might have (or creating
* it if such file does not yet exist).
* Encoding to use must be specified, and needs to be one of available
* types (as per JSON specification).
*
* Underlying stream is owned by the generator constructed,
* i.e. generator will handle closing of file when
* {@link JsonGenerator#close} is called.
*
* @param f File to write contents to
* @param enc Character encoding to use
*/
public JsonGenerator createJsonGenerator(File f, JsonEncoding enc)
throws IOException
{
OutputStream out = new FileOutputStream(f);
// true -> yes, we have to manage the stream since we created it
IOContext ctxt = _createContext(out, true);
ctxt.setEncoding(enc);
if (enc == JsonEncoding.UTF8) {
// [JACKSON-512]: allow wrapping with _outputDecorator
if (_outputDecorator != null) {
out = _outputDecorator.decorate(ctxt, out);
}
return _createUTF8JsonGenerator(out, ctxt);
}
Writer w = _createWriter(out, enc, ctxt);
// [JACKSON-512]: allow wrapping with _outputDecorator
if (_outputDecorator != null) {
w = _outputDecorator.decorate(ctxt, w);
}
return _createJsonGenerator(w, ctxt);
}
/*
/**********************************************************
/* 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.
*/
protected JsonParser _createJsonParser(InputStream in, IOContext ctxt)
throws IOException, JsonParseException
{
return new ByteSourceBootstrapper(ctxt, in).constructParser(_parserFeatures,
_objectCodec, _rootByteSymbols, _rootCharSymbols);
}
/**
* 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.
*/
protected JsonParser _createJsonParser(Reader r, IOContext ctxt)
throws IOException, JsonParseException
{
return new ReaderBasedParser(ctxt, _parserFeatures, r, _objectCodec,
_rootCharSymbols.makeChild(isEnabled(JsonParser.Feature.CANONICALIZE_FIELD_NAMES),
isEnabled(JsonParser.Feature.INTERN_FIELD_NAMES)));
}
/**
* 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 _createJsonParser(byte[] data, int offset, int len, IOContext ctxt)
throws IOException, JsonParseException
{
return new ByteSourceBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures,
_objectCodec, _rootByteSymbols, _rootCharSymbols);
}
/*
/**********************************************************
/* 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 _createJsonGenerator(Writer out, IOContext ctxt)
throws IOException
{
WriterBasedGenerator gen = new WriterBasedGenerator(ctxt, _generatorFeatures, _objectCodec, out);
if (_characterEscapes != null) {
gen.setCharacterEscapes(_characterEscapes);
}
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 _createUTF8JsonGenerator(OutputStream out, IOContext ctxt)
throws IOException
{
Utf8Generator gen = new Utf8Generator(ctxt, _generatorFeatures, _objectCodec, out);
if (_characterEscapes != null) {
gen.setCharacterEscapes(_characterEscapes);
}
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, other
/**********************************************************
*/
/**
* Overridable factory method that actually instantiates desired
* context object.
*/
protected IOContext _createContext(Object srcRef, boolean resourceManaged)
{
return new IOContext(_getBufferRecycler(), srcRef, resourceManaged);
}
/**
* Method used by factory to create buffer recycler instances
* for parsers and generators.
*
* Note: only public to give access for ObjectMapper
*/
public BufferRecycler _getBufferRecycler()
{
SoftReference ref = _recyclerRef.get();
BufferRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new BufferRecycler();
_recyclerRef.set(new SoftReference(br));
}
return br;
}
/**
* Helper methods used for constructing an optimal stream for
* parsers to use, when input is to be read from an URL.
* This helps when reading file content via URL.
*/
protected InputStream _optimizedStreamFromURL(URL url)
throws IOException
{
if ("file".equals(url.getProtocol())) {
/* Can not do this if the path refers
* to a network drive on windows. This fixes the problem;
* might not be needed on all platforms (NFS?), but should not
* matter a lot: performance penalty of extra wrapping is more
* relevant when accessing local file system.
*/
String host = url.getHost();
if (host == null || host.length() == 0) {
return new FileInputStream(url.getPath());
}
}
return url.openStream();
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonParseException.java 0000644 0001750 0001750 00000001060 11655120726 026564 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* Exception type for parsing problems, used when non-well-formed content
* (content that does not conform to JSON syntax as per specification)
* is encountered.
*/
public class JsonParseException
extends JsonProcessingException
{
final static long serialVersionUID = 123; // Stupid eclipse...
public JsonParseException(String msg, JsonLocation loc)
{
super(msg, loc);
}
public JsonParseException(String msg, JsonLocation loc, Throwable root)
{
super(msg, loc, root);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonGenerationException.java 0000644 0001750 0001750 00000001246 11655120726 027613 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* 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
{
final static long serialVersionUID = 123; // Stupid eclipse...
public JsonGenerationException(Throwable rootCause)
{
super(rootCause);
}
public JsonGenerationException(String msg)
{
super(msg, (JsonLocation)null);
}
public JsonGenerationException(String msg, Throwable rootCause)
{
super(msg, (JsonLocation)null, rootCause);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/ 0000755 0001750 0001750 00000000000 11672662540 023106 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/JsonGeneratorBase.java 0000644 0001750 0001750 00000042527 11655120726 027332 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
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 org.codehaus.jackson.*;
import org.codehaus.jackson.util.DefaultPrettyPrinter;
import org.codehaus.jackson.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 JsonGeneratorBase
extends JsonGenerator
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
protected ObjectCodec _objectCodec;
/**
* Bit flag composed of bits that indicate which
* {@link org.codehaus.jackson.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 org.codehaus.jackson.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 JsonGeneratorBase(int features, ObjectCodec codec)
{
super();
_features = features;
_writeContext = JsonWriteContext.createRootContext();
_objectCodec = codec;
_cfgNumbersAsStrings = isEnabled(Feature.WRITE_NUMBERS_AS_STRINGS);
}
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
@Override
public JsonGenerator enable(Feature f) {
_features |= f.getMask();
if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
_cfgNumbersAsStrings = true;
} else if (f == Feature.ESCAPE_NON_ASCII) {
setHighestNonEscapedChar(127);
}
return this;
}
@Override
public JsonGenerator disable(Feature f) {
_features &= ~f.getMask();
if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
_cfgNumbersAsStrings = false;
} else if (f == Feature.ESCAPE_NON_ASCII) {
setHighestNonEscapedChar(0);
}
return this;
}
//public JsonGenerator configure(Feature f, boolean state) { }
@Override
public final boolean isEnabled(Feature f) {
return (_features & f.getMask()) != 0;
}
@Override
public JsonGenerator useDefaultPrettyPrinter() {
return setPrettyPrinter(new DefaultPrettyPrinter());
}
@Override
public JsonGenerator setCodec(ObjectCodec oc) {
_objectCodec = oc;
return this;
}
@Override
public final ObjectCodec getCodec() { return _objectCodec; }
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
/**
* Note: co-variant return type.
*/
@Override
public final JsonWriteContext getOutputContext() { return _writeContext; }
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
@Override
public void writeStartArray() throws IOException, JsonGenerationException
{
// Array is a value, need to verify it's allowed
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
_writeStartArray();
}
}
/**
* @deprecated since 1.7, should just override {@link #writeStartArray} instead
* of defining this method
*/
@Deprecated
protected void _writeStartArray() throws IOException, JsonGenerationException {
}
@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 {
_writeEndArray();
}
_writeContext = _writeContext.getParent();
}
/**
* @deprecated since 1.7, should just override {@link #writeEndArray} instead
* of defining this method
*/
@Deprecated
protected void _writeEndArray() throws IOException, JsonGenerationException {
}
@Override
public void writeStartObject() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
_writeStartObject();
}
}
/**
* @deprecated since 1.7, should just override {@link #writeStartObject} instead
* of defining this method
*/
@Deprecated
protected void _writeStartObject() throws IOException, JsonGenerationException {
}
@Override
public void writeEndObject() throws IOException, JsonGenerationException
{
if (!_writeContext.inObject()) {
_reportError("Current context not an object but "+_writeContext.getTypeDesc());
}
_writeContext = _writeContext.getParent();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
} else {
_writeEndObject();
}
}
/**
* @deprecated since 1.7, should just override {@link #writeEndObject} instead
* of defining this method
*/
@Deprecated
protected void _writeEndObject() throws IOException, JsonGenerationException {
}
/*
/**********************************************************
/* Public API, write methods, textual
/**********************************************************
*/
//public abstract void writeString(String text) throws IOException, JsonGenerationException;
//public abstract void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException;
//public abstract void writeRaw(String text) throws IOException, JsonGenerationException;
//public abstract void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException;
@Override
public void writeRawValue(String text)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write raw value");
writeRaw(text);
}
@Override
public void writeRawValue(String text, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write raw value");
writeRaw(text, offset, len);
}
@Override
public void writeRawValue(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write raw value");
writeRaw(text, offset, len);
}
//public abstract void writeBinary(byte[] data, int offset, int len, boolean includeLFs) throws IOException, JsonGenerationException;
/*
/**********************************************************
/* Public API, write methods, primitive
/**********************************************************
*/
// Not implemented at this level, added as placeholders
/*
public abstract void writeNumber(int i)
public abstract void writeNumber(long l)
public abstract void writeNumber(double d)
public abstract void writeNumber(float f)
public abstract void writeNumber(BigDecimal dec)
public abstract void writeBoolean(boolean state)
public abstract void writeNull()
*/
/*
/**********************************************************
/* Public API, write methods, POJOs, trees
/**********************************************************
*/
@Override
public void writeObject(Object value)
throws IOException, JsonProcessingException
{
if (value == null) {
// important: call method that does check value write:
writeNull();
} else {
/* 02-Mar-2009, tatu: we are NOT to call _verifyValueWrite here,
* because that will be done when codec actually serializes
* contained POJO. If we did call it it would advance state
* causing exception later on
*/
if (_objectCodec != null) {
_objectCodec.writeValue(this, value);
return;
}
_writeSimpleObject(value);
}
}
@Override
public void writeTree(JsonNode rootNode)
throws IOException, JsonProcessingException
{
// As with 'writeObject()', we are not check if write would work
if (rootNode == null) {
writeNull();
} else {
if (_objectCodec == null) {
throw new IllegalStateException("No ObjectCodec defined for the generator, can not serialize JsonNode-based trees");
}
_objectCodec.writeTree(this, rootNode);
}
}
/*
/**********************************************************
/* Public API, low-level output handling
/**********************************************************
*/
@Override
public abstract void flush() throws IOException;
@Override
public void close() throws IOException
{
_closed = true;
}
@Override
public boolean isClosed() { return _closed; }
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
@Override
public final void copyCurrentEvent(JsonParser jp)
throws IOException, JsonProcessingException
{
JsonToken t = jp.getCurrentToken();
// sanity check; what to do?
if (t == null) {
_reportError("No current event to copy");
}
switch(t) {
case START_OBJECT:
writeStartObject();
break;
case END_OBJECT:
writeEndObject();
break;
case START_ARRAY:
writeStartArray();
break;
case END_ARRAY:
writeEndArray();
break;
case FIELD_NAME:
writeFieldName(jp.getCurrentName());
break;
case VALUE_STRING:
if (jp.hasTextCharacters()) {
writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
} else {
writeString(jp.getText());
}
break;
case VALUE_NUMBER_INT:
switch (jp.getNumberType()) {
case INT:
writeNumber(jp.getIntValue());
break;
case BIG_INTEGER:
writeNumber(jp.getBigIntegerValue());
break;
default:
writeNumber(jp.getLongValue());
}
break;
case VALUE_NUMBER_FLOAT:
switch (jp.getNumberType()) {
case BIG_DECIMAL:
writeNumber(jp.getDecimalValue());
break;
case FLOAT:
writeNumber(jp.getFloatValue());
break;
default:
writeNumber(jp.getDoubleValue());
}
break;
case VALUE_TRUE:
writeBoolean(true);
break;
case VALUE_FALSE:
writeBoolean(false);
break;
case VALUE_NULL:
writeNull();
break;
case VALUE_EMBEDDED_OBJECT:
writeObject(jp.getEmbeddedObject());
break;
default:
_cantHappen();
}
}
@Override
public final void copyCurrentStructure(JsonParser jp)
throws IOException, JsonProcessingException
{
JsonToken t = jp.getCurrentToken();
// Let's handle field-name separately first
if (t == JsonToken.FIELD_NAME) {
writeFieldName(jp.getCurrentName());
t = jp.nextToken();
// fall-through to copy the associated value
}
switch (t) {
case START_ARRAY:
writeStartArray();
while (jp.nextToken() != JsonToken.END_ARRAY) {
copyCurrentStructure(jp);
}
writeEndArray();
break;
case START_OBJECT:
writeStartObject();
while (jp.nextToken() != JsonToken.END_OBJECT) {
copyCurrentStructure(jp);
}
writeEndObject();
break;
default: // others are simple:
copyCurrentEvent(jp);
}
}
/*
/**********************************************************
/* Package methods for this, sub-classes
/**********************************************************
*/
protected abstract void _releaseBuffers();
protected abstract void _verifyValueWrite(String typeMsg)
throws IOException, JsonGenerationException;
protected void _reportError(String msg)
throws JsonGenerationException
{
throw new JsonGenerationException(msg);
}
protected void _cantHappen()
{
throw new RuntimeException("Internal error: should never end up through this code path");
}
/**
* Helper method to try to call appropriate write method for given
* untyped Object. At this point, no structural conversions should be done,
* only simple basic types are to be coerced as necessary.
*
* @param value Non-null value to write
*/
protected void _writeSimpleObject(Object value)
throws IOException, JsonGenerationException
{
/* 31-Dec-2009, tatu: Actually, we could just handle some basic
* types even without codec. This can improve interoperability,
* and specifically help with TokenBuffer.
*/
if (value == null) {
writeNull();
return;
}
if (value instanceof String) {
writeString((String) value);
return;
}
if (value instanceof Number) {
Number n = (Number) value;
if (n instanceof Integer) {
writeNumber(n.intValue());
return;
} else if (n instanceof Long) {
writeNumber(n.longValue());
return;
} else if (n instanceof Double) {
writeNumber(n.doubleValue());
return;
} else if (n instanceof Float) {
writeNumber(n.floatValue());
return;
} else if (n instanceof Short) {
writeNumber(n.shortValue());
return;
} else if (n instanceof Byte) {
writeNumber(n.byteValue());
return;
} else if (n instanceof BigInteger) {
writeNumber((BigInteger) n);
return;
} else if (n instanceof BigDecimal) {
writeNumber((BigDecimal) n);
return;
// then Atomic types
} else if (n instanceof AtomicInteger) {
writeNumber(((AtomicInteger) n).get());
return;
} else if (n instanceof AtomicLong) {
writeNumber(((AtomicLong) n).get());
return;
}
} else if (value instanceof byte[]) {
writeBinary((byte[]) value);
return;
} else if (value instanceof Boolean) {
writeBoolean(((Boolean) value).booleanValue());
return;
} else if (value instanceof AtomicBoolean) {
writeBoolean(((AtomicBoolean) value).get());
return;
}
throw new IllegalStateException("No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed "
+value.getClass().getName()+")");
}
protected final void _throwInternal() {
throw new RuntimeException("Internal error: this code path should never get executed");
}
/**
* @since 1.7
*/
protected void _reportUnsupportedOperation() {
throw new UnsupportedOperationException("Operation not supported by generator of type "+getClass().getName());
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/Utf8Generator.java 0000644 0001750 0001750 00000167070 11655120726 026455 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.CharacterEscapes;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.io.NumberOutput;
import org.codehaus.jackson.io.SerializedString;
import org.codehaus.jackson.util.CharTypes;
public class Utf8Generator
extends JsonGeneratorBase
{
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_SPACE = (byte) ' ';
private final static byte BYTE_COMMA = (byte) ',';
private final static byte BYTE_COLON = (byte) ':';
private final static byte BYTE_QUOTE = (byte) '"';
protected final static int SURR1_FIRST = 0xD800;
protected final static int SURR1_LAST = 0xDBFF;
protected final static int SURR2_FIRST = 0xDC00;
protected final static int SURR2_LAST = 0xDFFF;
// intermediate copies only made up to certain length...
private final static int MAX_BYTES_TO_BUFFER = 512;
final static byte[] HEX_CHARS = CharTypes.copyHexBytes();
private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' };
private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' };
/**
* 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;
/**
* Underlying output stream used for writing JSON content.
*/
final protected OutputStream _outputStream;
/*
/**********************************************************
/* 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.
*
* @since 1.8
*/
protected CharacterEscapes _characterEscapes;
/*
/**********************************************************
/* Output buffering
/**********************************************************
*/
/**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_outputStream}.
*/
protected byte[] _outputBuffer;
/**
* Pointer to the position right beyond the last character to output
* (end marker; may be past the buffer)
*/
protected int _outputTail = 0;
/**
* End marker of the output buffer; one past the last valid position
* within the buffer.
*/
protected final int _outputEnd;
/**
* Maximum number of char
s that we know will always fit
* in the output buffer after escaping
*/
protected final int _outputMaxContiguous;
/**
* Intermediate buffer in which characters of a String are copied
* before being encoded.
*/
protected char[] _charBuffer;
/**
* Length of _charBuffer
*/
protected final int _charBufferLength;
/**
* 6 character temporary buffer allocated if needed, for constructing
* escape sequences
*/
protected byte[] _entityBuffer;
/**
* Flag that indicates whether the output buffer is recycable (and
* needs to be returned to recycler once we are done) or not.
*/
protected boolean _bufferRecyclable;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public Utf8Generator(IOContext ctxt, int features, ObjectCodec codec,
OutputStream out)
{
super(features, codec);
_ioContext = ctxt;
_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 Utf8Generator(IOContext ctxt, int features, ObjectCodec codec,
OutputStream out, byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
{
super(features, codec);
_ioContext = ctxt;
_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;
if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
setHighestNonEscapedChar(127);
}
}
/*
/**********************************************************
/* Overridden configuration methods
/**********************************************************
*/
@Override
public JsonGenerator setHighestNonEscapedChar(int charCode) {
_maximumNonEscapedChar = (charCode < 0) ? 0 : charCode;
return this;
}
@Override
public int getHighestEscapedChar() {
return _maximumNonEscapedChar;
}
@Override
public JsonGenerator setCharacterEscapes(CharacterEscapes esc)
{
_characterEscapes = esc;
if (esc == null) { // revert to standard escapes
_outputEscapes = sOutputEscapes;
} else {
_outputEscapes = esc.getEscapeCodesForAscii();
}
return this;
}
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*
* @since 1.8
*/
@Override
public CharacterEscapes getCharacterEscapes() {
return _characterEscapes;
}
@Override
public Object getOutputTarget() {
return _outputStream;
}
/*
/**********************************************************
/* Overridden methods
/**********************************************************
*/
/* Most overrides in this section are just to make methods final,
* to allow better inlining...
*/
@Override
public final void writeStringField(String fieldName, String value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeString(value);
}
@Override
public final void writeFieldName(String name) throws IOException, JsonGenerationException
{
int status = _writeContext.writeFieldName(name);
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
return;
}
if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_COMMA;
}
_writeFieldName(name);
}
@Override
public final void writeFieldName(SerializedString name)
throws IOException, JsonGenerationException
{
// Object is a value, need to verify it's allowed
int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
return;
}
if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_COMMA;
}
_writeFieldName(name);
}
@Override
public final void writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
// Object is a value, need to verify it's allowed
int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
return;
}
if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_COMMA;
}
_writeFieldName(name);
}
/*
/**********************************************************
/* Output method implementations, structural
/**********************************************************
*/
@Override
public final void writeStartArray() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_LBRACKET;
}
}
@Override
public final void writeEndArray() throws IOException, JsonGenerationException
{
if (!_writeContext.inArray()) {
_reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_RBRACKET;
}
_writeContext = _writeContext.getParent();
}
@Override
public final void writeStartObject() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_LCURLY;
}
}
@Override
public final void writeEndObject() throws IOException, JsonGenerationException
{
if (!_writeContext.inObject()) {
_reportError("Current context not an object but "+_writeContext.getTypeDesc());
}
_writeContext = _writeContext.getParent();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_RCURLY;
}
}
protected final void _writeFieldName(String name)
throws IOException, JsonGenerationException
{
/* To support [JACKSON-46], we'll do this:
* (Question: should quoting of spaces (etc) still be enabled?)
*/
if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
_writeStringSegments(name);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
// The beef:
final int len = name.length();
if (len <= _charBufferLength) { // yes, fits right in
name.getChars(0, len, _charBuffer, 0);
// But as one segment, or multiple?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(_charBuffer, 0, len);
} else {
_writeStringSegments(_charBuffer, 0, len);
}
} else {
_writeStringSegments(name);
}
// and closing quotes; need room for one more char:
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
protected final void _writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
byte[] raw = name.asQuotedUTF8();
if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
_writeBytes(raw);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
// Can do it all in buffer?
final int len = raw.length;
if ((_outputTail + len + 1) < _outputEnd) { // yup
System.arraycopy(raw, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
_outputBuffer[_outputTail++] = BYTE_QUOTE;
} else {
_writeBytes(raw);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
}
/**
* Specialized version of _writeFieldName
, off-lined
* to keep the "fast path" as simple (and hopefully fast) as possible.
*/
protected final void _writePPFieldName(String name, boolean commaBefore)
throws IOException, JsonGenerationException
{
if (commaBefore) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
final int len = name.length();
if (len <= _charBufferLength) { // yes, fits right in
name.getChars(0, len, _charBuffer, 0);
// But as one segment, or multiple?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(_charBuffer, 0, len);
} else {
_writeStringSegments(_charBuffer, 0, len);
}
} else {
_writeStringSegments(name);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
} else { // non-standard, omit quotes
_writeStringSegments(name);
}
}
protected final void _writePPFieldName(SerializableString name, boolean commaBefore)
throws IOException, JsonGenerationException
{
if (commaBefore) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
boolean addQuotes = isEnabled(Feature.QUOTE_FIELD_NAMES); // standard
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
_writeBytes(name.asQuotedUTF8());
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
}
/*
/**********************************************************
/* Output method implementations, textual
/**********************************************************
*/
@Override
public void writeString(String text)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (text == null) {
_writeNull();
return;
}
// First: can we make a local copy of chars that make up text?
final int len = text.length();
if (len > _charBufferLength) { // nope: off-line handling
_writeLongString(text);
return;
}
// yes: good.
text.getChars(0, len, _charBuffer, 0);
// Output: if we can't guarantee it fits in output buffer, off-line as well:
if (len > _outputMaxContiguous) {
_writeLongString(_charBuffer, 0, len);
return;
}
if ((_outputTail + len + 2) > _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
_writeStringSegment(_charBuffer, 0, len); // we checked space already above
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
private final void _writeLongString(String text)
throws IOException, JsonGenerationException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
_writeStringSegments(text);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
private final void _writeLongString(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
_writeStringSegments(_charBuffer, 0, len);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
@Override
public void writeString(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
// One or multiple segments?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(text, offset, len);
} else {
_writeStringSegments(text, offset, len);
}
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
@Override
public final void writeString(SerializableString text)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
_writeBytes(text.asQuotedUTF8());
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
_writeBytes(text, offset, length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
@Override
public void writeUTF8String(byte[] text, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
// One or multiple segments?
if (len <= _outputMaxContiguous) {
_writeUTF8Segment(text, offset, len);
} else {
_writeUTF8Segments(text, offset, len);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
/*
/**********************************************************
/* Output method implementations, unprocessed ("raw")
/**********************************************************
*/
@Override
public void writeRaw(String text)
throws IOException, JsonGenerationException
{
int start = 0;
int len = text.length();
while (len > 0) {
char[] buf = _charBuffer;
final int blen = buf.length;
final int len2 = (len < blen) ? len : blen;
text.getChars(start, start+len2, buf, 0);
writeRaw(buf, 0, len2);
start += len2;
len -= len2;
}
}
@Override
public void writeRaw(String text, int offset, int len)
throws IOException, JsonGenerationException
{
while (len > 0) {
char[] buf = _charBuffer;
final int blen = buf.length;
final int len2 = (len < blen) ? len : blen;
text.getChars(offset, offset+len2, buf, 0);
writeRaw(buf, 0, len2);
offset += len2;
len -= len2;
}
}
// @TODO: rewrite for speed...
@Override
public final void writeRaw(char[] cbuf, int offset, int len)
throws IOException, JsonGenerationException
{
// First: if we have 3 x charCount spaces, we know it'll fit just fine
{
int len3 = len+len+len;
if ((_outputTail + len3) > _outputEnd) {
// maybe we could flush?
if (_outputEnd < len3) { // wouldn't be enough...
_writeSegmentedRaw(cbuf, offset, len);
return;
}
// yes, flushing brings enough space
_flushBuffer();
}
}
len += offset; // now marks the end
// Note: here we know there is enough room, hence no output boundary checks
main_loop:
while (offset < len) {
inner_loop:
while (true) {
int ch = (int) cbuf[offset];
if (ch > 0x7F) {
break inner_loop;
}
_outputBuffer[_outputTail++] = (byte) ch;
if (++offset >= len) {
break main_loop;
}
}
char ch = cbuf[offset++];
if (ch < 0x800) { // 2-byte?
_outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
_outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
_outputRawMultiByteChar(ch, cbuf, offset, len);
}
}
}
@Override
public void writeRaw(char ch)
throws IOException, JsonGenerationException
{
if ((_outputTail + 3) >= _outputEnd) {
_flushBuffer();
}
final byte[] bbuf = _outputBuffer;
if (ch <= 0x7F) {
bbuf[_outputTail++] = (byte) ch;
} else if (ch < 0x800) { // 2-byte?
bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
_outputRawMultiByteChar(ch, null, 0, 0);
}
}
/**
* Helper method called when it is possible that output of raw section
* to output may cross buffer boundary
*/
private final void _writeSegmentedRaw(char[] cbuf, int offset, int len)
throws IOException, JsonGenerationException
{
final int end = _outputEnd;
final byte[] bbuf = _outputBuffer;
main_loop:
while (offset < len) {
inner_loop:
while (true) {
int ch = (int) cbuf[offset];
if (ch >= 0x80) {
break inner_loop;
}
// !!! TODO: fast(er) writes (roll input, output checks in one)
if (_outputTail >= end) {
_flushBuffer();
}
bbuf[_outputTail++] = (byte) ch;
if (++offset >= len) {
break main_loop;
}
}
if ((_outputTail + 3) >= _outputEnd) {
_flushBuffer();
}
char ch = cbuf[offset++];
if (ch < 0x800) { // 2-byte?
bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
_outputRawMultiByteChar(ch, cbuf, offset, len);
}
}
}
/*
/**********************************************************
/* Output method implementations, base64-encoded binary
/**********************************************************
*/
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write binary value");
// Starting quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
_writeBinary(b64variant, data, offset, offset+len);
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
/*
/**********************************************************
/* Output method implementations, primitive
/**********************************************************
*/
@Override
public void writeNumber(int i)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
// up to 10 digits and possible minus sign
if ((_outputTail + 11) >= _outputEnd) {
_flushBuffer();
}
if (_cfgNumbersAsStrings) {
_writeQuotedInt(i);
return;
}
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
}
private 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, JsonGenerationException
{
_verifyValueWrite("write number");
if (_cfgNumbersAsStrings) {
_writeQuotedLong(l);
return;
}
if ((_outputTail + 21) >= _outputEnd) {
// up to 20 digits, minus sign
_flushBuffer();
}
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
}
private 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, JsonGenerationException
{
_verifyValueWrite("write number");
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(value);
} else {
writeRaw(value.toString());
}
}
@Override
public void writeNumber(double d)
throws IOException, JsonGenerationException
{
if (_cfgNumbersAsStrings ||
// [JACKSON-139]
(((Double.isNaN(d) || Double.isInfinite(d))
&& isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
writeString(String.valueOf(d));
return;
}
// What is the max length for doubles? 40 chars?
_verifyValueWrite("write number");
writeRaw(String.valueOf(d));
}
@Override
public void writeNumber(float f)
throws IOException, JsonGenerationException
{
if (_cfgNumbersAsStrings ||
// [JACKSON-139]
(((Float.isNaN(f) || Float.isInfinite(f))
&& isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
writeString(String.valueOf(f));
return;
}
// What is the max length for floats?
_verifyValueWrite("write number");
writeRaw(String.valueOf(f));
}
@Override
public void writeNumber(BigDecimal value)
throws IOException, JsonGenerationException
{
// Don't really know max length for big decimal, no point checking
_verifyValueWrite("write number");
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(value);
} else {
writeRaw(value.toString());
}
}
@Override
public void writeNumber(String encodedValue)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
if (_cfgNumbersAsStrings) {
_writeQuotedRaw(encodedValue);
} else {
writeRaw(encodedValue);
}
}
private final void _writeQuotedRaw(Object value) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
writeRaw(value.toString());
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_QUOTE;
}
@Override
public void writeBoolean(boolean state)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write boolean value");
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
int len = keyword.length;
System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
}
@Override
public void writeNull()
throws IOException, JsonGenerationException
{
_verifyValueWrite("write null value");
_writeNull();
}
/*
/**********************************************************
/* Implementations for other methods
/**********************************************************
*/
@Override
protected final void _verifyValueWrite(String typeMsg)
throws IOException, JsonGenerationException
{
int status = _writeContext.writeValue();
if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
_reportError("Can not "+typeMsg+", expecting field name");
}
if (_cfgPrettyPrinter == null) {
byte b;
switch (status) {
case JsonWriteContext.STATUS_OK_AFTER_COMMA:
b = BYTE_COMMA;
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
b = BYTE_COLON;
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE:
b = BYTE_SPACE;
break;
case JsonWriteContext.STATUS_OK_AS_IS:
default:
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail] = b;
++_outputTail;
return;
}
// Otherwise, pretty printer knows what to do...
_verifyPrettyValueWrite(typeMsg, status);
}
protected final void _verifyPrettyValueWrite(String typeMsg, int status)
throws IOException, JsonGenerationException
{
// If we have a pretty printer, it knows what to do:
switch (status) {
case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
_cfgPrettyPrinter.writeArrayValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
_cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE:
_cfgPrettyPrinter.writeRootValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AS_IS:
// First entry, but of which context?
if (_writeContext.inArray()) {
_cfgPrettyPrinter.beforeArrayValues(this);
} else if (_writeContext.inObject()) {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
break;
default:
_cantHappen();
break;
}
}
/*
/**********************************************************
/* Low-level output handling
/**********************************************************
*/
@Override
public final void flush()
throws IOException
{
_flushBuffer();
if (_outputStream != null) {
if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
_outputStream.flush();
}
}
}
@Override
public void close()
throws IOException
{
super.close();
/* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
* scopes.
*/
// First: let's see that we still have buffers...
if (_outputBuffer != null
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
while (true) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt.inArray()) {
writeEndArray();
} else if (ctxt.inObject()) {
writeEndObject();
} else {
break;
}
}
}
_flushBuffer();
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside: when using UTF8Writer, underlying buffer(s)
* may not be properly recycled if we don't close the writer.
*/
if (_outputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
_outputStream.close();
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
// If we can't close it, we should at least flush
_outputStream.flush();
}
}
// Internal buffer(s) generator has can now be released as well
_releaseBuffers();
}
@Override
protected void _releaseBuffers()
{
byte[] buf = _outputBuffer;
if (buf != null && _bufferRecyclable) {
_outputBuffer = null;
_ioContext.releaseWriteEncodingBuffer(buf);
}
char[] cbuf = _charBuffer;
if (cbuf != null) {
_charBuffer = null;
_ioContext.releaseConcatBuffer(cbuf);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing, raw bytes
/**********************************************************
*/
private final void _writeBytes(byte[] bytes) throws IOException
{
final int len = bytes.length;
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
// still not enough?
if (len > MAX_BYTES_TO_BUFFER) {
_outputStream.write(bytes, 0, len);
return;
}
}
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
}
private final void _writeBytes(byte[] bytes, int offset, int len) throws IOException
{
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
// still not enough?
if (len > MAX_BYTES_TO_BUFFER) {
_outputStream.write(bytes, offset, len);
return;
}
}
System.arraycopy(bytes, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
}
/*
/**********************************************************
/* Internal methods, mid-level writing, String segments
/**********************************************************
*/
/**
* Method called when String to write is long enough not to fit
* completely in temporary copy buffer. If so, we will actually
* copy it in small enough chunks so it can be directly fed
* to single-segment writes (instead of maximum slices that
* would fit in copy buffer)
*/
private final void _writeStringSegments(String text)
throws IOException, JsonGenerationException
{
int left = text.length();
int offset = 0;
final char[] cbuf = _charBuffer;
while (left > 0) {
int len = Math.min(_outputMaxContiguous, left);
text.getChars(offset, offset+len, cbuf, 0);
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(cbuf, 0, len);
offset += len;
left -= len;
}
}
/**
* Method called when character sequence to write is long enough that
* its maximum encoded and escaped form is not guaranteed to fit in
* the output buffer. If so, we will need to choose smaller output
* chunks to write at a time.
*/
private final void _writeStringSegments(char[] cbuf, int offset, int totalLen)
throws IOException, JsonGenerationException
{
do {
int len = Math.min(_outputMaxContiguous, totalLen);
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(cbuf, offset, len);
offset += len;
totalLen -= len;
} while (totalLen > 0);
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segments
/**********************************************************
*/
/**
* This method called when the string content is already in
* a char buffer, and its maximum total encoded and escaped length
* can not exceed size of the output buffer.
* Caller must ensure that there is enough space in output buffer,
* assuming case of all non-escaped ASCII characters, as well as
* potentially enough space for other cases (but not necessarily flushed)
*/
private final void _writeStringSegment(char[] cbuf, int offset, int len)
throws IOException, JsonGenerationException
{
// note: caller MUST ensure (via flushing) there's room for ASCII only
// Fast+tight loop for ASCII-only, no-escaping-needed output
len += offset; // becomes end marker, then
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
while (offset < len) {
int ch = cbuf[offset];
// note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
if (ch > 0x7F || escCodes[ch] != 0) {
break;
}
outputBuffer[outputPtr++] = (byte) ch;
++offset;
}
_outputTail = outputPtr;
if (offset < len) {
// [JACKSON-106]
if (_characterEscapes != null) {
_writeCustomStringSegment2(cbuf, offset, len);
// [JACKSON-102]
} else if (_maximumNonEscapedChar == 0) {
_writeStringSegment2(cbuf, offset, len);
} else {
_writeStringSegmentASCII2(cbuf, offset, len);
}
}
}
/**
* Secondary method called when content contains characters to escape,
* and/or multi-byte UTF-8 characters.
*/
private final void _writeStringSegment2(final char[] cbuf, int offset, final int end)
throws IOException, JsonGenerationException
{
// Ok: caller guarantees buffer can have room; but that may require flushing:
if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
while (offset < end) {
int ch = cbuf[offset++];
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with additional escaping (ASCII or such)
/* (since 1.8; see [JACKSON-102])
/**********************************************************
*/
/**
* Same as _writeStringSegment2(char[], ...) _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
final int maxUnescaped = _maximumNonEscapedChar;
while (offset < end) {
int ch = cbuf[offset++];
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
outputPtr = _writeGenericEscape(ch, outputPtr);
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with fully custom escaping (and possibly escaping of non-ASCII
/**********************************************************
*/
/**
* Same as _writeStringSegmentASCII2(char[], ...) _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
// may or may not have this limit
final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
final CharacterEscapes customEscapes = _characterEscapes; // non-null
while (offset < end) {
int ch = cbuf[offset++];
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
SerializableString esc = customEscapes.getEscapeSequence(ch);
if (esc == null) {
throw new JsonGenerationException("Invalid custom escape definitions; custom escape not found for character code 0x"
+Integer.toHexString(ch)+", although was supposed to have one");
}
outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
outputPtr = _writeGenericEscape(ch, outputPtr);
continue;
}
SerializableString esc = customEscapes.getEscapeSequence(ch);
if (esc != null) {
outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
private int _writeCustomEscape(byte[] outputBuffer, int outputPtr, SerializableString esc, int remainingChars)
throws IOException, JsonGenerationException
{
byte[] raw = esc.asUnquotedUTF8(); // must be escaped at this point, shouldn't double-quote
int len = raw.length;
if (len > 6) { // may violate constraints we have, do offline
return _handleLongCustomEscape(outputBuffer, outputPtr, _outputEnd, raw, remainingChars);
}
// otherwise will fit without issues, so:
System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
return (outputPtr + len);
}
private int _handleLongCustomEscape(byte[] outputBuffer, int outputPtr, int outputEnd, byte[] raw,
int remainingChars)
throws IOException, JsonGenerationException
{
int len = raw.length;
if ((outputPtr + len) > outputEnd) {
_outputTail = outputPtr;
_flushBuffer();
outputPtr = _outputTail;
if (len > outputBuffer.length) { // very unlikely, but possible...
_outputStream.write(raw, 0, len);
return outputPtr;
}
System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
outputPtr += len;
}
// but is the invariant still obeyed? If not, flush once more
if ((outputPtr + 6 * remainingChars) > outputEnd) {
_flushBuffer();
return _outputTail;
}
return outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, "raw UTF-8" segments
/**********************************************************
*/
/**
* Method called when UTF-8 encoded (but NOT yet escaped!) content is not guaranteed
* to fit in the output buffer after escaping; as such, we just need to
* chunk writes.
*/
private 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 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);
}
}
/*
/**********************************************************
/* 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) { // 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
bbuf[outputPtr++] = BYTE_BACKSLASH;
bbuf[outputPtr++] = BYTE_u;
bbuf[outputPtr++] = HEX_CHARS[(ch >> 12) & 0xF];
bbuf[outputPtr++] = HEX_CHARS[(ch >> 8) & 0xF];
bbuf[outputPtr++] = HEX_CHARS[(ch >> 4) & 0xF];
bbuf[outputPtr++] = HEX_CHARS[ch & 0xF];
} else {
bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12));
bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
}
return outputPtr;
}
protected final int _decodeSurrogate(int surr1, int surr2) throws IOException
{
// First is known to be valid, but how about the other?
if (surr2 < SURR2_FIRST || surr2 > SURR2_LAST) {
String msg = "Incomplete surrogate pair: first char 0x"+Integer.toHexString(surr1)+", second 0x"+Integer.toHexString(surr2);
_reportError(msg);
}
int c = 0x10000 + ((surr1 - SURR1_FIRST) << 10) + (surr2 - SURR2_FIRST);
return c;
}
private 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);
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/StreamBasedParserBase.java 0000644 0001750 0001750 00000013634 11655120726 030116 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import org.codehaus.jackson.io.IOContext;
/**
* This is a simple low-level input reader base class, used by
* JSON parser. It is used when underlying input source is
* a byte stream such as {@link InputStream}.
* The reason for sub-classing (over composition)
* is due to need for direct access to low-level byte buffers
* and positions.
*
* @author Tatu Saloranta
*
* @deprecated Since 1.9, sub-classes should just embed code from here
*/
@Deprecated
public abstract class StreamBasedParserBase
extends JsonParserBase
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Input stream that can be used for reading more content, if one
* in use. May be null, if input comes just as a full buffer,
* or if the stream has been closed.
*/
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
/**********************************************************
*/
protected StreamBasedParserBase(IOContext ctxt, int features,
InputStream in, byte[] inputBuffer, int start, int end,
boolean bufferRecyclable)
{
super(ctxt, features);
_inputStream = in;
_inputBuffer = inputBuffer;
_inputPtr = start;
_inputEnd = end;
_bufferRecyclable = bufferRecyclable;
}
/*
/**********************************************************
/* Overrides
/**********************************************************
*/
@Override
public int releaseBuffered(OutputStream out) throws IOException
{
int count = _inputEnd - _inputPtr;
if (count < 1) {
return 0;
}
// let's just advance ptr to end
int origPtr = _inputPtr;
out.write(_inputBuffer, origPtr, count);
return count;
}
@Override
public Object getInputSource() {
return _inputStream;
}
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
@Override
protected final boolean loadMore()
throws IOException
{
_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;
if (_inputStream != null) {
int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes");
}
}
return false;
}
/**
* Helper method that will try to load at least specified number bytes in
* input buffer, possible moving existing data around if necessary
*
* @since 1.6
*/
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) {
_currInputProcessed += _inputPtr;
_currInputRowStart -= _inputPtr;
System.arraycopy(_inputBuffer, _inputPtr, _inputBuffer, 0, amount);
_inputEnd = amount;
} else {
_inputEnd = 0;
}
_inputPtr = 0;
while (_inputEnd < minAvailable) {
int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
if (count < 1) {
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+amount+" bytes");
}
return false;
}
_inputEnd += count;
}
return true;
}
@Override
protected void _closeInput() throws IOException
{
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying InputStream, unless we "own" it, or auto-closing
* feature is enabled.
*/
if (_inputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_inputStream.close();
}
_inputStream = null;
}
}
@Override
protected void _releaseBuffers() throws IOException
{
super._releaseBuffers();
if (_bufferRecyclable) {
byte[] buf = _inputBuffer;
if (buf != null) {
_inputBuffer = null;
_ioContext.releaseReadIOBuffer(buf);
}
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/JsonParserBase.java 0000644 0001750 0001750 00000110044 11655120726 026626 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.io.NumberInput;
import org.codehaus.jackson.util.ByteArrayBuilder;
import org.codehaus.jackson.util.TextBuffer;
import org.codehaus.jackson.util.VersionUtil;
/**
* Intermediate base class used by all Jackson {@link JsonParser}
* implementations. Contains most common things that are independent
* of actual underlying input source
*
* @author Tatu Saloranta
*/
public abstract class JsonParserBase
extends JsonParserMinimalBase
{
/*
/**********************************************************
/* Generic I/O state
/**********************************************************
*/
/**
* I/O context for this reader. It handles buffer allocation
* for the reader.
*/
final protected IOContext _ioContext;
/**
* Flag that indicates whether parser is closed or not. Gets
* set when parser is either closed by explicit call
* ({@link #close}) or when end-of-input is reached.
*/
protected boolean _closed;
/*
/**********************************************************
/* Current input data
/**********************************************************
*/
// Note: type of actual buffer depends on sub-class, can't include
/**
* Pointer to next available character in buffer
*/
protected int _inputPtr = 0;
/**
* Index of character after last available one in the buffer.
*/
protected int _inputEnd = 0;
/*
/**********************************************************
/* Current input location information
/**********************************************************
*/
/**
* Number of characters/bytes that were contained in previous blocks
* (blocks that were already processed prior to the current buffer).
*/
protected long _currInputProcessed = 0L;
/**
* Current row location of current point in input buffer, starting
* from 1, if available.
*/
protected int _currInputRow = 1;
/**
* Current index of the first character of the current row in input
* buffer. Needed to calculate column position, if necessary; benefit
* of not having column itself is that this only has to be updated
* once per line.
*/
protected int _currInputRowStart = 0;
/*
/**********************************************************
/* Information about starting location of event
/* Reader is pointing to; updated on-demand
/**********************************************************
*/
// // // Location info at point when current token was started
/**
* Total number of bytes/characters read before start of current token.
* For big (gigabyte-sized) sizes are possible, needs to be long,
* unlike pointers and sizes related to in-memory buffers.
*/
protected long _tokenInputTotal = 0;
/**
* Input row on which current token starts, 1-based
*/
protected int _tokenInputRow = 1;
/**
* Column on input row that current token starts; 0-based (although
* in the end it'll be converted to 1-based)
*/
protected int _tokenInputCol = 0;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Information about parser context, context in which
* the next token is to be parsed (root, array, object).
*/
protected JsonReadContext _parsingContext;
/**
* Secondary token related to the next token after current one;
* used if its type is known. This may be value token that
* follows FIELD_NAME, for example.
*/
protected JsonToken _nextToken;
/*
/**********************************************************
/* Buffer(s) for local name(s) and text content
/**********************************************************
*/
/**
* Buffer that contains contents of String values, including
* field names if necessary (name split across boundary,
* contains escape sequence, or access needed to char array)
*/
protected final TextBuffer _textBuffer;
/**
* Temporary buffer that is needed if field name is accessed
* using {@link #getTextCharacters} method (instead of String
* returning alternatives)
*/
protected char[] _nameCopyBuffer = null;
/**
* Flag set to indicate whether the field name is available
* from the name copy buffer or not (in addition to its String
* representation being available via read context)
*/
protected boolean _nameCopied = false;
/**
* ByteArrayBuilder is needed if 'getBinaryValue' is called. If so,
* we better reuse it for remainder of content.
*/
protected ByteArrayBuilder _byteArrayBuilder = null;
/**
* We will hold on to decoded binary data, for duration of
* current event, so that multiple calls to
* {@link #getBinaryValue} will not need to decode data more
* than once.
*/
protected byte[] _binaryValue;
/*
/**********************************************************
/* Constants and fields of former 'JsonNumericParserBase'
/**********************************************************
*/
final protected static int NR_UNKNOWN = 0;
// First, integer types
final protected static int NR_INT = 0x0001;
final protected static int NR_LONG = 0x0002;
final protected static int NR_BIGINT = 0x0004;
// And then floating point types
final protected static int NR_DOUBLE = 0x008;
final protected static int NR_BIGDECIMAL = 0x0010;
// Also, we need some numeric constants
final static BigDecimal BD_MIN_LONG = new BigDecimal(Long.MIN_VALUE);
final static BigDecimal BD_MAX_LONG = new BigDecimal(Long.MAX_VALUE);
final static BigDecimal BD_MIN_INT = new BigDecimal(Long.MIN_VALUE);
final static BigDecimal BD_MAX_INT = new BigDecimal(Long.MAX_VALUE);
final static long MIN_INT_L = (long) Integer.MIN_VALUE;
final static long MAX_INT_L = (long) Integer.MAX_VALUE;
// These are not very accurate, but have to do... (for bounds checks)
final static double MIN_LONG_D = (double) Long.MIN_VALUE;
final static double MAX_LONG_D = (double) Long.MAX_VALUE;
final static double MIN_INT_D = (double) Integer.MIN_VALUE;
final static double MAX_INT_D = (double) Integer.MAX_VALUE;
// Digits, numeric
final protected static int INT_0 = '0';
final protected static int INT_1 = '1';
final protected static int INT_2 = '2';
final protected static int INT_3 = '3';
final protected static int INT_4 = '4';
final protected static int INT_5 = '5';
final protected static int INT_6 = '6';
final protected static int INT_7 = '7';
final protected static int INT_8 = '8';
final protected static int INT_9 = '9';
final protected static int INT_MINUS = '-';
final protected static int INT_PLUS = '+';
final protected static int INT_DECIMAL_POINT = '.';
final protected static int INT_e = 'e';
final protected static int INT_E = 'E';
final protected static char CHAR_NULL = '\0';
// Numeric value holders: multiple fields used for
// for efficiency
/**
* Bitfield that indicates which numeric representations
* have been calculated for the current type
*/
protected int _numTypesValid = NR_UNKNOWN;
// First primitives
protected int _numberInt;
protected long _numberLong;
protected double _numberDouble;
// And then object types
protected BigInteger _numberBigInt;
protected BigDecimal _numberBigDecimal;
// And then other information about value itself
/**
* Flag that indicates whether numeric value has a negative
* value. That is, whether its textual representation starts
* with minus character.
*/
protected boolean _numberNegative;
/**
* Length of integer part of the number, in characters
*/
protected int _intLength;
/**
* Length of the fractional part (not including decimal
* point or exponent), in characters.
* Not used for pure integer values.
*/
protected int _fractLength;
/**
* Length of the exponent part of the number, if any, not
* including 'e' marker or sign, just digits.
* Not used for pure integer values.
*/
protected int _expLength;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected JsonParserBase(IOContext ctxt, int features)
{
super();
_features = features;
_ioContext = ctxt;
_textBuffer = ctxt.constructTextBuffer();
_parsingContext = JsonReadContext.createRootContext();
}
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/*
/**********************************************************
/* JsonParser impl
/**********************************************************
*/
/**
* Method that can be called to get the name associated with
* the current event.
*/
@Override
public String getCurrentName()
throws IOException, JsonParseException
{
// [JACKSON-395]: start markers require information from parent
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
JsonReadContext parent = _parsingContext.getParent();
return parent.getCurrentName();
}
return _parsingContext.getCurrentName();
}
@Override
public void close() throws IOException
{
if (!_closed) {
_closed = true;
try {
_closeInput();
} finally {
// as per [JACKSON-324], do in finally block
// Also, internal buffer(s) can now be released as well
_releaseBuffers();
}
}
}
@Override
public boolean isClosed() { return _closed; }
@Override
public JsonReadContext getParsingContext()
{
return _parsingContext;
}
/**
* Method that return the starting location of the current
* token; that is, position of the first character from input
* that starts the current token.
*/
@Override
public JsonLocation getTokenLocation()
{
return new JsonLocation(_ioContext.getSourceReference(),
getTokenCharacterOffset(),
getTokenLineNr(),
getTokenColumnNr());
}
/**
* Method that returns location of the last processed character;
* usually for error reporting purposes
*/
@Override
public JsonLocation getCurrentLocation()
{
int col = _inputPtr - _currInputRowStart + 1; // 1-based
return new JsonLocation(_ioContext.getSourceReference(),
_currInputProcessed + _inputPtr - 1,
_currInputRow, col);
}
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
@Override
public boolean hasTextCharacters()
{
if (_currToken == JsonToken.VALUE_STRING) {
return true; // usually true
}
if (_currToken == JsonToken.FIELD_NAME) {
return _nameCopied;
}
return false;
}
/*
/**********************************************************
/* Public low-level accessors
/**********************************************************
*/
public final long getTokenCharacterOffset() { return _tokenInputTotal; }
public final int getTokenLineNr() { return _tokenInputRow; }
public final int getTokenColumnNr() {
// note: value of -1 means "not available"; otherwise convert from 0-based to 1-based
int col = _tokenInputCol;
return (col < 0) ? col : (col + 1);
}
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
protected final void loadMoreGuaranteed()
throws IOException
{
if (!loadMore()) {
_reportInvalidEOF();
}
}
/*
/**********************************************************
/* Abstract methods needed from sub-classes
/**********************************************************
*/
protected abstract boolean loadMore() throws IOException;
protected abstract void _finishString() throws IOException, JsonParseException;
protected abstract void _closeInput() throws IOException;
protected abstract byte[] _decodeBase64(Base64Variant b64variant) throws IOException, JsonParseException;
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
protected void _releaseBuffers() throws IOException
{
_textBuffer.releaseBuffers();
char[] buf = _nameCopyBuffer;
if (buf != null) {
_nameCopyBuffer = null;
_ioContext.releaseNameCopyBuffer(buf);
}
}
/**
* Method called when an EOF is encountered between tokens.
* If so, it may be a legitimate EOF, but only iff there
* is no open non-root context.
*/
@Override
protected void _handleEOF() throws JsonParseException
{
if (!_parsingContext.inRoot()) {
_reportInvalidEOF(": expected close marker for "+_parsingContext.getTypeDesc()+" (from "+_parsingContext.getStartLocation(_ioContext.getSourceReference())+")");
}
}
/*
/**********************************************************
/* Internal/package methods: Error reporting
/**********************************************************
*/
protected void _reportMismatchedEndMarker(int actCh, char expCh)
throws JsonParseException
{
String startDesc = ""+_parsingContext.getStartLocation(_ioContext.getSourceReference());
_reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+_parsingContext.getTypeDesc()+" starting at "+startDesc+")");
}
/*
/**********************************************************
/* Internal/package methods: shared/reusable builders
/**********************************************************
*/
public ByteArrayBuilder _getByteArrayBuilder()
{
if (_byteArrayBuilder == null) {
_byteArrayBuilder = new ByteArrayBuilder();
} else {
_byteArrayBuilder.reset();
}
return _byteArrayBuilder;
}
/*
/**********************************************************
/* Methods from former JsonNumericParserBase
/**********************************************************
*/
// // // Life-cycle of number-parsing
protected final JsonToken reset(boolean negative, int intLen, int fractLen, int expLen)
{
if (fractLen < 1 && expLen < 1) { // integer
return resetInt(negative, intLen);
}
return resetFloat(negative, intLen, fractLen, expLen);
}
protected final JsonToken resetInt(boolean negative, int intLen)
{
_numberNegative = negative;
_intLength = intLen;
_fractLength = 0;
_expLength = 0;
_numTypesValid = NR_UNKNOWN; // to force parsing
return JsonToken.VALUE_NUMBER_INT;
}
protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen, int expLen)
{
_numberNegative = negative;
_intLength = intLen;
_fractLength = fractLen;
_expLength = expLen;
_numTypesValid = NR_UNKNOWN; // to force parsing
return JsonToken.VALUE_NUMBER_FLOAT;
}
protected final JsonToken resetAsNaN(String valueStr, double value)
{
_textBuffer.resetWithString(valueStr);
_numberDouble = value;
_numTypesValid = NR_DOUBLE;
return JsonToken.VALUE_NUMBER_FLOAT;
}
/*
/**********************************************************
/* Numeric accessors of public API
/**********************************************************
*/
@Override
public Number getNumberValue() throws IOException, JsonParseException
{
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_UNKNOWN); // will also check event type
}
// Separate types for int types
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
if ((_numTypesValid & NR_INT) != 0) {
return Integer.valueOf(_numberInt);
}
if ((_numTypesValid & NR_LONG) != 0) {
return Long.valueOf(_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 Double.valueOf(_numberDouble);
}
@Override
public NumberType getNumberType() throws IOException, JsonParseException
{
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_UNKNOWN); // will also check event type
}
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
if ((_numTypesValid & NR_INT) != 0) {
return NumberType.INT;
}
if ((_numTypesValid & NR_LONG) != 0) {
return NumberType.LONG;
}
return NumberType.BIG_INTEGER;
}
/* And then floating point types. Here optimal type
* needs to be big decimal, to avoid losing any data?
* However... using BD is slow, so let's allow returning
* double as type if no explicit call has been made to access
* data as BD?
*/
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
return NumberType.BIG_DECIMAL;
}
return NumberType.DOUBLE;
}
@Override
public int getIntValue() throws IOException, JsonParseException
{
if ((_numTypesValid & NR_INT) == 0) {
if (_numTypesValid == NR_UNKNOWN) { // not parsed at all
_parseNumericValue(NR_INT); // will also check event type
}
if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively?
convertNumberToInt(); // let's make it so, if possible
}
}
return _numberInt;
}
@Override
public long getLongValue() throws IOException, JsonParseException
{
if ((_numTypesValid & NR_LONG) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_LONG);
}
if ((_numTypesValid & NR_LONG) == 0) {
convertNumberToLong();
}
}
return _numberLong;
}
@Override
public BigInteger getBigIntegerValue() throws IOException, JsonParseException
{
if ((_numTypesValid & NR_BIGINT) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_BIGINT);
}
if ((_numTypesValid & NR_BIGINT) == 0) {
convertNumberToBigInteger();
}
}
return _numberBigInt;
}
@Override
public float getFloatValue() throws IOException, JsonParseException
{
double value = getDoubleValue();
/* 22-Jan-2009, tatu: Bounds/range checks would be tricky
* here, so let's not bother even trying...
*/
/*
if (value < -Float.MAX_VALUE || value > MAX_FLOAT_D) {
_reportError("Numeric value ("+getText()+") out of range of Java float");
}
*/
return (float) value;
}
@Override
public double getDoubleValue() throws IOException, JsonParseException
{
if ((_numTypesValid & NR_DOUBLE) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_DOUBLE);
}
if ((_numTypesValid & NR_DOUBLE) == 0) {
convertNumberToDouble();
}
}
return _numberDouble;
}
@Override
public BigDecimal getDecimalValue() throws IOException, JsonParseException
{
if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_BIGDECIMAL);
}
if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
convertNumberToBigDecimal();
}
}
return _numberBigDecimal;
}
/*
/**********************************************************
/* Conversion from textual to numeric representation
/**********************************************************
*/
/**
* Method that will parse actual numeric value out of a syntactically
* valid number value. Type it will parse into depends on whether
* it is a floating point number, as well as its magnitude: smallest
* legal type (of ones available) is used for efficiency.
*
* @param expType Numeric type that we will immediately need, if any;
* mostly necessary to optimize handling of floating point numbers
*/
protected void _parseNumericValue(int expType)
throws IOException, JsonParseException
{
// Int or float?
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
char[] buf = _textBuffer.getTextBuffer();
int offset = _textBuffer.getTextOffset();
int len = _intLength;
if (_numberNegative) {
++offset;
}
if (len <= 9) { // definitely fits in int
int i = NumberInput.parseInt(buf, offset, len);
_numberInt = _numberNegative ? -i : i;
_numTypesValid = NR_INT;
return;
}
if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls
long l = NumberInput.parseLong(buf, offset, len);
if (_numberNegative) {
l = -l;
}
// [JACKSON-230] Could still fit in int, need to check
if (len == 10) {
if (_numberNegative) {
if (l >= MIN_INT_L) {
_numberInt = (int) l;
_numTypesValid = NR_INT;
return;
}
} else {
if (l <= MAX_INT_L) {
_numberInt = (int) l;
_numTypesValid = NR_INT;
return;
}
}
}
_numberLong = l;
_numTypesValid = NR_LONG;
return;
}
_parseSlowIntValue(expType, buf, offset, len);
return;
}
if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
_parseSlowFloatValue(expType);
return;
}
_reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
}
private final void _parseSlowFloatValue(int expType)
throws IOException, JsonParseException
{
/* Nope: floating point. Here we need to be careful to get
* optimal parsing strategy: choice is between accurate but
* slow (BigDecimal) and lossy but fast (Double). For now
* let's only use BD when explicitly requested -- it can
* still be constructed correctly at any point since we do
* retain textual representation
*/
try {
if (expType == NR_BIGDECIMAL) {
_numberBigDecimal = _textBuffer.contentsAsDecimal();
_numTypesValid = NR_BIGDECIMAL;
} else {
// Otherwise double has to do
_numberDouble = _textBuffer.contentsAsDouble();
_numTypesValid = NR_DOUBLE;
}
} catch (NumberFormatException nex) {
// Can this ever occur? Due to overflow, maybe?
_wrapError("Malformed numeric value '"+_textBuffer.contentsAsString()+"'", nex);
}
}
private final void _parseSlowIntValue(int expType, char[] buf, int offset, int len)
throws IOException, JsonParseException
{
String numStr = _textBuffer.contentsAsString();
try {
// [JACKSON-230] Some long cases still...
if (NumberInput.inLongRange(buf, offset, len, _numberNegative)) {
// Probably faster to construct a String, call parse, than to use BigInteger
_numberLong = Long.parseLong(numStr);
_numTypesValid = NR_LONG;
} else {
// nope, need the heavy guns... (rare case)
_numberBigInt = new BigInteger(numStr);
_numTypesValid = NR_BIGINT;
}
} catch (NumberFormatException nex) {
// Can this ever occur? Due to overflow, maybe?
_wrapError("Malformed numeric value '"+numStr+"'", nex);
}
}
/*
/**********************************************************
/* Numeric conversions
/**********************************************************
*/
protected void convertNumberToInt()
throws IOException, JsonParseException
{
// First, converting from long ought to be easy
if ((_numTypesValid & NR_LONG) != 0) {
// Let's verify it's lossless conversion by simple roundtrip
int result = (int) _numberLong;
if (((long) result) != _numberLong) {
_reportError("Numeric value ("+getText()+") out of range of int");
}
_numberInt = result;
} else if ((_numTypesValid & NR_BIGINT) != 0) {
// !!! Should check for range...
_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(); // should never get here
}
_numTypesValid |= NR_INT;
}
protected void convertNumberToLong()
throws IOException, JsonParseException
{
if ((_numTypesValid & NR_INT) != 0) {
_numberLong = (long) _numberInt;
} else if ((_numTypesValid & NR_BIGINT) != 0) {
// !!! Should check for range...
_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(); // should never get here
}
_numTypesValid |= NR_LONG;
}
protected void convertNumberToBigInteger()
throws IOException, JsonParseException
{
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
// here it'll just get truncated, no exceptions thrown
_numberBigInt = _numberBigDecimal.toBigInteger();
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberBigInt = BigInteger.valueOf(_numberLong);
} else if ((_numTypesValid & NR_INT) != 0) {
_numberBigInt = BigInteger.valueOf(_numberInt);
} else if ((_numTypesValid & NR_DOUBLE) != 0) {
_numberBigInt = BigDecimal.valueOf(_numberDouble).toBigInteger();
} else {
_throwInternal(); // should never get here
}
_numTypesValid |= NR_BIGINT;
}
protected void convertNumberToDouble()
throws IOException, JsonParseException
{
/* 05-Aug-2008, tatus: Important note: this MUST start with
* more accurate representations, since we don't know which
* value is the original one (others get generated when
* requested)
*/
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
_numberDouble = _numberBigDecimal.doubleValue();
} else if ((_numTypesValid & NR_BIGINT) != 0) {
_numberDouble = _numberBigInt.doubleValue();
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberDouble = (double) _numberLong;
} else if ((_numTypesValid & NR_INT) != 0) {
_numberDouble = (double) _numberInt;
} else {
_throwInternal(); // should never get here
}
_numTypesValid |= NR_DOUBLE;
}
protected void convertNumberToBigDecimal()
throws IOException, JsonParseException
{
/* 05-Aug-2008, tatus: Important note: this MUST start with
* more accurate representations, since we don't know which
* value is the original one (others get generated when
* requested)
*/
if ((_numTypesValid & NR_DOUBLE) != 0) {
/* Let's actually parse from String representation,
* to avoid rounding errors that non-decimal floating operations
* would incur
*/
_numberBigDecimal = new BigDecimal(getText());
} else if ((_numTypesValid & NR_BIGINT) != 0) {
_numberBigDecimal = new BigDecimal(_numberBigInt);
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberBigDecimal = BigDecimal.valueOf(_numberLong);
} else if ((_numTypesValid & NR_INT) != 0) {
_numberBigDecimal = BigDecimal.valueOf((long) _numberInt);
} else {
_throwInternal(); // should never get here
}
_numTypesValid |= NR_BIGDECIMAL;
}
/*
/**********************************************************
/* Number handling exceptions
/**********************************************************
*/
protected void reportUnexpectedNumberChar(int ch, String comment)
throws JsonParseException
{
String msg = "Unexpected character ("+_getCharDesc(ch)+") in numeric value";
if (comment != null) {
msg += ": "+comment;
}
_reportError(msg);
}
protected void reportInvalidNumber(String msg)
throws JsonParseException
{
_reportError("Invalid numeric value: "+msg);
}
protected void reportOverflowInt()
throws IOException, JsonParseException
{
_reportError("Numeric value ("+getText()+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
}
protected void reportOverflowLong()
throws IOException, JsonParseException
{
_reportError("Numeric value ("+getText()+") out of range of long ("+Long.MIN_VALUE+" - "+Long.MAX_VALUE+")");
}
/*
/**********************************************************
/* Base64 handling support
/**********************************************************
*/
/**
* Method that sub-classes must implement to support escaped sequences
* in base64-encoded sections.
* Sub-classes that do not need base64 support can leave this as is
*/
protected char _decodeEscaped()
throws IOException, JsonParseException {
throw new UnsupportedOperationException();
}
protected final int _decodeBase64Escape(Base64Variant b64variant, int ch, int index)
throws IOException, JsonParseException
{
// 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
if (ch != '\\') {
throw reportInvalidBase64Char(b64variant, ch, index);
}
int unescaped = _decodeEscaped();
// if white space, skip if first triplet; otherwise errors
if (unescaped <= INT_SPACE) {
if (index == 0) { // whitespace only allowed to be skipped between triplets
return -1;
}
}
// otherwise try to find actual triplet value
int bits = b64variant.decodeBase64Char(unescaped);
if (bits < 0) {
throw reportInvalidBase64Char(b64variant, unescaped, index);
}
return bits;
}
protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index)
throws IOException, JsonParseException
{
// 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
if (ch != '\\') {
throw reportInvalidBase64Char(b64variant, ch, index);
}
char unescaped = _decodeEscaped();
// if white space, skip if first triplet; otherwise errors
if (unescaped <= INT_SPACE) {
if (index == 0) { // whitespace only allowed to be skipped between triplets
return -1;
}
}
// otherwise try to find actual triplet value
int bits = b64variant.decodeBase64Char(unescaped);
if (bits < 0) {
throw reportInvalidBase64Char(b64variant, unescaped, index);
}
return bits;
}
protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex)
throws IllegalArgumentException
{
return reportInvalidBase64Char(b64variant, ch, bindex, null);
}
/**
* @param bindex Relative index within base64 character unit; between 0
* and 3 (as unit has exactly 4 characters)
*/
protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex, String msg)
throws IllegalArgumentException
{
String base;
if (ch <= INT_SPACE) {
base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
} else if (b64variant.usesPaddingChar(ch)) {
base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
} else {
base = "Illegal character '"+((char)ch)+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
}
if (msg != null) {
base = base + ": " + msg;
}
return new IllegalArgumentException(base);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/ReaderBasedParserBase.java 0000644 0001750 0001750 00000015054 11655120726 030063 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
/**
* This is a simple low-level input reader base class, used by
* JSON parser.
* The reason for sub-classing (over composition)
* is due to need for direct access to character buffers
* and positions.
*
* @author Tatu Saloranta
*
* @deprecated Since 1.9 sub-classes should just include code
* from here as is.
*/
@Deprecated
public abstract class ReaderBasedParserBase
extends JsonParserBase
{
/*
/**********************************************************
/* 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 input data
/**********************************************************
*/
/**
* Current buffer from which data is read; generally data is read into
* buffer from input source.
*/
protected char[] _inputBuffer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected ReaderBasedParserBase(IOContext ctxt, int features, Reader r)
{
super(ctxt, features);
_reader = r;
_inputBuffer = ctxt.allocTokenBuffer();
}
/*
/**********************************************************
/* Overrides
/**********************************************************
*/
@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;
}
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
@Override
protected final boolean loadMore() throws IOException
{
_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;
if (_reader != null) {
int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd);
}
}
return false;
}
protected char getNextChar(String eofMsg)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(eofMsg);
}
}
return _inputBuffer[_inputPtr++];
}
@Override
protected void _closeInput() throws IOException
{
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside is that when using our optimized
* Reader (granted, we only do that for UTF-32...) this
* means that buffer recycling won't work correctly.
*/
if (_reader != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_reader.close();
}
_reader = null;
}
}
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers()
throws IOException
{
super._releaseBuffers();
char[] buf = _inputBuffer;
if (buf != null) {
_inputBuffer = null;
_ioContext.releaseTokenBuffer(buf);
}
}
/*
/**********************************************************
/* Helper methods for subclasses
/**********************************************************
*/
/**
* Helper method for checking whether input matches expected token
*
* @since 1.8
*/
protected final boolean _matchToken(String matchStr, int i)
throws IOException, JsonParseException
{
final int len = matchStr.length();
do {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOFInValue();
}
}
if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
_reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
}
++_inputPtr;
} while (++i < len);
// but let's also ensure we either get EOF, or non-alphanum char...
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
return true;
}
}
char c = _inputBuffer[_inputPtr];
// if Java letter, it's a problem tho
if (Character.isJavaIdentifierPart(c)) {
++_inputPtr;
_reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
}
return true;
}
protected void _reportInvalidToken(String matchedPart, String msg)
throws IOException, JsonParseException
{
StringBuilder sb = new StringBuilder(matchedPart);
/* Let's just try to find what appears to be the token, using
* regular Java identifier character rules. It's just a heuristic,
* nothing fancy here.
*/
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
break;
}
}
char c = _inputBuffer[_inputPtr];
if (!Character.isJavaIdentifierPart(c)) {
break;
}
++_inputPtr;
sb.append(c);
}
_reportError("Unrecognized token '"+sb.toString()+"': was expecting ");
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/package-info.java 0000644 0001750 0001750 00000000303 11655120726 026265 0 ustar jamespage jamespage /**
* Parser and generator implementation classes that Jackson
* defines and uses.
* Application code should not (need to) use contents of this package.
*/
package org.codehaus.jackson.impl;
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/Utf8StreamParser.java 0000644 0001750 0001750 00000307512 11655120726 027134 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.sym.*;
import org.codehaus.jackson.util.*;
/**
* This is a concrete implementation of {@link JsonParser}, which is
* based on a {@link java.io.InputStream} as the input source.
*/
public final class Utf8StreamParser
extends JsonParserBase
{
final static byte BYTE_LF = (byte) '\n';
private final static int[] sInputCodesUtf8 = CharTypes.getInputCodeUtf8();
/**
* Latin1 encoding is not supported, but we do use 8-bit subset for
* pre-processing task, to simplify first pass, keep it fast.
*/
private final static int[] sInputCodesLatin1 = CharTypes.getInputCodeLatin1();
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Codec used for data binding when (if) requested; typically full
* ObjectMapper
, but that abstract is not part of core
* package.
*/
protected ObjectCodec _objectCodec;
/**
* Symbol table that contains field names encountered so far
*/
final protected BytesToNameCanonicalizer _symbols;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Temporary buffer used for name parsing.
*/
protected int[] _quadBuffer = new int[16];
/**
* Flag that indicates that the current token has not yet
* been fully processed, and needs to be finished for
* some access (or skipped to obtain the next token)
*/
protected boolean _tokenIncomplete = false;
/**
* Temporary storage for partially parsed name bytes.
*/
private int _quad1;
/*
/**********************************************************
/* Input buffering (from former 'StreamBasedParserBase')
/**********************************************************
*/
protected InputStream _inputStream;
/*
/**********************************************************
/* Current input data
/**********************************************************
*/
/**
* Current buffer from which data is read; generally data is read into
* buffer from input source, but in some cases pre-loaded buffer
* is handed to the parser.
*/
protected byte[] _inputBuffer;
/**
* Flag that indicates whether the input buffer is recycable (and
* needs to be returned to recycler once we are done) or not.
*
* If it is not, it also means that parser can NOT modify underlying
* buffer.
*/
protected boolean _bufferRecyclable;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public Utf8StreamParser(IOContext ctxt, int features, InputStream in,
ObjectCodec codec, BytesToNameCanonicalizer sym,
byte[] inputBuffer, int start, int end,
boolean bufferRecyclable)
{
super(ctxt, features);
_inputStream = in;
_objectCodec = codec;
_symbols = sym;
_inputBuffer = inputBuffer;
_inputPtr = start;
_inputEnd = end;
_bufferRecyclable = bufferRecyclable;
// 12-Mar-2010, tatus: Sanity check, related to [JACKSON-259]:
if (!JsonParser.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(features)) {
// should never construct non-canonical UTF-8/byte parser (instead, use Reader)
_throwInternal();
}
}
@Override
public ObjectCodec getCodec() {
return _objectCodec;
}
@Override
public void setCodec(ObjectCodec c) {
_objectCodec = c;
}
/*
/**********************************************************
/* Former StreamBasedParserBase methods
/**********************************************************
*/
@Override
public int releaseBuffered(OutputStream out) throws IOException
{
int count = _inputEnd - _inputPtr;
if (count < 1) {
return 0;
}
// let's just advance ptr to end
int origPtr = _inputPtr;
out.write(_inputBuffer, origPtr, count);
return count;
}
@Override
public Object getInputSource() {
return _inputStream;
}
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
@Override
protected final boolean loadMore()
throws IOException
{
_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;
if (_inputStream != null) {
int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes");
}
}
return false;
}
/**
* Helper method that will try to load at least specified number bytes in
* input buffer, possible moving existing data around if necessary
*
* @since 1.6
*/
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) {
_currInputProcessed += _inputPtr;
_currInputRowStart -= _inputPtr;
System.arraycopy(_inputBuffer, _inputPtr, _inputBuffer, 0, amount);
_inputEnd = amount;
} else {
_inputEnd = 0;
}
_inputPtr = 0;
while (_inputEnd < minAvailable) {
int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
if (count < 1) {
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+amount+" bytes");
}
return false;
}
_inputEnd += count;
}
return true;
}
@Override
protected void _closeInput() throws IOException
{
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying InputStream, unless we "own" it, or auto-closing
* feature is enabled.
*/
if (_inputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_inputStream.close();
}
_inputStream = null;
}
}
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers() throws IOException
{
super._releaseBuffers();
if (_bufferRecyclable) {
byte[] buf = _inputBuffer;
if (buf != null) {
_inputBuffer = null;
_ioContext.releaseReadIOBuffer(buf);
}
}
}
/*
/**********************************************************
/* Public API, data access
/**********************************************************
*/
@Override
public String getText()
throws IOException, JsonParseException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
return _getText2(t);
}
protected final String _getText2(JsonToken t)
{
if (t == null) {
return null;
}
switch (t) {
case FIELD_NAME:
return _parsingContext.getCurrentName();
case VALUE_STRING:
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.contentsAsString();
}
return t.asString();
}
@Override
public char[] getTextCharacters()
throws IOException, JsonParseException
{
if (_currToken != null) { // null only before/after document
switch (_currToken) {
case FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
case VALUE_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.getTextBuffer();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public int getTextLength()
throws IOException, JsonParseException
{
if (_currToken != null) { // null only before/after document
switch (_currToken) {
case FIELD_NAME:
return _parsingContext.getCurrentName().length();
case VALUE_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.size();
default:
return _currToken.asCharArray().length;
}
}
return 0;
}
@Override
public int getTextOffset() throws IOException, JsonParseException
{
// Most have offset of 0, only some may have other values:
if (_currToken != null) {
switch (_currToken) {
case FIELD_NAME:
return 0;
case VALUE_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.getTextOffset();
}
}
return 0;
}
@Override
public byte[] getBinaryValue(Base64Variant b64variant)
throws IOException, JsonParseException
{
if (_currToken != JsonToken.VALUE_STRING &&
(_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
}
/* To ensure that we won't see inconsistent data, better clear up
* state...
*/
if (_tokenIncomplete) {
try {
_binaryValue = _decodeBase64(b64variant);
} catch (IllegalArgumentException iae) {
throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
}
/* let's clear incomplete only now; allows for accessing other
* textual content in error cases
*/
_tokenIncomplete = false;
}
return _binaryValue;
}
/*
/**********************************************************
/* Public API, traversal, basic
/**********************************************************
*/
/**
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
@Override
public JsonToken nextToken()
throws IOException, JsonParseException
{
_numTypesValid = NR_UNKNOWN;
/* First: field names are special -- we will always tokenize
* (part of) value along with field name to simplify
* state handling. If so, can and need to use secondary token:
*/
if (_currToken == JsonToken.FIELD_NAME) {
return _nextAfterName();
}
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
/* 19-Feb-2009, tatu: Should actually close/release things
* like input source, symbol table and recyclable buffers now.
*/
close();
return (_currToken = null);
}
/* First, need to ensure we know the starting location of token
* after skipping leading white space
*/
_tokenInputTotal = _currInputProcessed + _inputPtr - 1;
_tokenInputRow = _currInputRow;
_tokenInputCol = _inputPtr - _currInputRowStart - 1;
// finally: clear any data retained so far
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET) {
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(i, '}');
}
_parsingContext = _parsingContext.getParent();
return (_currToken = JsonToken.END_ARRAY);
}
if (i == INT_RCURLY) {
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker(i, ']');
}
_parsingContext = _parsingContext.getParent();
return (_currToken = JsonToken.END_OBJECT);
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
}
i = _skipWS();
}
/* And should we now have a name? Always true for
* Object contexts, since the intermediate 'expect-value'
* state is never retained.
*/
if (!_parsingContext.inObject()) {
return _nextTokenNotInObject(i);
}
// So first parse the field name itself:
Name n = _parseFieldName(i);
_parsingContext.setCurrentName(n.getName());
_currToken = JsonToken.FIELD_NAME;
i = _skipWS();
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
i = _skipWS();
// Ok: we must have a value... what is it? Strings are very common, check first:
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return _currToken;
}
JsonToken t;
switch (i) {
case INT_LBRACKET:
t = JsonToken.START_ARRAY;
break;
case INT_LCURLY:
t = JsonToken.START_OBJECT;
break;
case INT_RBRACKET:
case INT_RCURLY:
// Error: neither is valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(i, "expected a value");
case INT_t:
_matchToken("true", 1);
t = JsonToken.VALUE_TRUE;
break;
case INT_f:
_matchToken("false", 1);
t = JsonToken.VALUE_FALSE;
break;
case INT_n:
_matchToken("null", 1);
t = JsonToken.VALUE_NULL;
break;
case INT_MINUS:
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicate by a more specific error message.
*/
case INT_0:
case INT_1:
case INT_2:
case INT_3:
case INT_4:
case INT_5:
case INT_6:
case INT_7:
case INT_8:
case INT_9:
t = parseNumberText(i);
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
return _currToken;
}
private final JsonToken _nextTokenNotInObject(int i)
throws IOException, JsonParseException
{
if (i == INT_QUOTE) {
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_STRING);
}
switch (i) {
case INT_LBRACKET:
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_ARRAY);
case INT_LCURLY:
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_OBJECT);
case INT_RBRACKET:
case INT_RCURLY:
// Error: neither is valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(i, "expected a value");
case INT_t:
_matchToken("true", 1);
return (_currToken = JsonToken.VALUE_TRUE);
case INT_f:
_matchToken("false", 1);
return (_currToken = JsonToken.VALUE_FALSE);
case INT_n:
_matchToken("null", 1);
return (_currToken = JsonToken.VALUE_NULL);
case INT_MINUS:
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicate by a more specific error message.
*/
case INT_0:
case INT_1:
case INT_2:
case INT_3:
case INT_4:
case INT_5:
case INT_6:
case INT_7:
case INT_8:
case INT_9:
return (_currToken = parseNumberText(i));
}
return (_currToken = _handleUnexpectedValue(i));
}
private final JsonToken _nextAfterName()
{
_nameCopied = false; // need to invalidate if it was copied
JsonToken t = _nextToken;
_nextToken = null;
// Also: may need to start new context?
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return (_currToken = t);
}
@Override
public void close() throws IOException
{
super.close();
// Merge found symbols, if any:
_symbols.release();
}
/*
/**********************************************************
/* Public API, traversal, nextXxxValue/nextFieldName
/**********************************************************
*/
@Override
public boolean nextFieldName(SerializableString str)
throws IOException, JsonParseException
{
// // // Note: most of code below is copied from nextToken()
_numTypesValid = NR_UNKNOWN;
if (_currToken == JsonToken.FIELD_NAME) { // can't have name right after name
_nextAfterName();
return false;
}
if (_tokenIncomplete) {
_skipString();
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
close();
_currToken = null;
return false;
}
_tokenInputTotal = _currInputProcessed + _inputPtr - 1;
_tokenInputRow = _currInputRow;
_tokenInputCol = _inputPtr - _currInputRowStart - 1;
// finally: clear any data retained so far
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET) {
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(i, '}');
}
_parsingContext = _parsingContext.getParent();
_currToken = JsonToken.END_ARRAY;
return false;
}
if (i == INT_RCURLY) {
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker(i, ']');
}
_parsingContext = _parsingContext.getParent();
_currToken = JsonToken.END_OBJECT;
return false;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
}
i = _skipWS();
}
if (!_parsingContext.inObject()) {
_nextTokenNotInObject(i);
return false;
}
// // // This part differs, name parsing
if (i == INT_QUOTE) {
// when doing literal match, must consider escaping:
byte[] nameBytes = str.asQuotedUTF8();
final int len = nameBytes.length;
if ((_inputPtr + len) < _inputEnd) { // maybe...
// first check length match by
final int end = _inputPtr+len;
if (_inputBuffer[end] == INT_QUOTE) {
int offset = 0;
final int ptr = _inputPtr;
while (true) {
if (offset == len) { // yes, match!
_inputPtr = end+1; // skip current value first
// First part is simple; setting of name
_parsingContext.setCurrentName(str.getValue());
_currToken = JsonToken.FIELD_NAME;
// But then we also must handle following value etc
_isNextTokenNameYes();
return true;
}
if (nameBytes[offset] != _inputBuffer[ptr+offset]) {
break;
}
++offset;
}
}
}
}
_isNextTokenNameNo(i);
return false;
}
private final void _isNextTokenNameYes()
throws IOException, JsonParseException
{
// very first thing: common case, colon, value, no white space
int i;
if (_inputPtr < _inputEnd && _inputBuffer[_inputPtr] == INT_COLON) { // fast case first
++_inputPtr;
i = _inputBuffer[_inputPtr++];
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return;
}
if (i == INT_LCURLY) {
_nextToken = JsonToken.START_OBJECT;
return;
}
if (i == INT_LBRACKET) {
_nextToken = JsonToken.START_ARRAY;
return;
}
i &= 0xFF;
if (i <= INT_SPACE || i == INT_SLASH) {
--_inputPtr;
i = _skipWS();
}
} else {
i = _skipColon();
}
switch (i) {
case INT_QUOTE:
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return;
case INT_LBRACKET:
_nextToken = JsonToken.START_ARRAY;
return;
case INT_LCURLY:
_nextToken = JsonToken.START_OBJECT;
return;
case INT_RBRACKET:
case INT_RCURLY:
_reportUnexpectedChar(i, "expected a value");
case INT_t:
_matchToken("true", 1);
_nextToken = JsonToken.VALUE_TRUE;
return;
case INT_f:
_matchToken("false", 1);
_nextToken = JsonToken.VALUE_FALSE;
return;
case INT_n:
_matchToken("null", 1);
_nextToken = JsonToken.VALUE_NULL;
return;
case INT_MINUS:
case INT_0:
case INT_1:
case INT_2:
case INT_3:
case INT_4:
case INT_5:
case INT_6:
case INT_7:
case INT_8:
case INT_9:
_nextToken = parseNumberText(i);
return;
}
_nextToken = _handleUnexpectedValue(i);
}
private final void _isNextTokenNameNo(int i)
throws IOException, JsonParseException
{
// // // and this is back to standard nextToken()
Name n = _parseFieldName(i);
_parsingContext.setCurrentName(n.getName());
_currToken = JsonToken.FIELD_NAME;
i = _skipWS();
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
i = _skipWS();
// Ok: we must have a value... what is it? Strings are very common, check first:
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return;
}
JsonToken t;
switch (i) {
case INT_LBRACKET:
t = JsonToken.START_ARRAY;
break;
case INT_LCURLY:
t = JsonToken.START_OBJECT;
break;
case INT_RBRACKET:
case INT_RCURLY:
_reportUnexpectedChar(i, "expected a value");
case INT_t:
_matchToken("true", 1);
t = JsonToken.VALUE_TRUE;
break;
case INT_f:
_matchToken("false", 1);
t = JsonToken.VALUE_FALSE;
break;
case INT_n:
_matchToken("null", 1);
t = JsonToken.VALUE_NULL;
break;
case INT_MINUS:
case INT_0:
case INT_1:
case INT_2:
case INT_3:
case INT_4:
case INT_5:
case INT_6:
case INT_7:
case INT_8:
case INT_9:
t = parseNumberText(i);
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
}
@Override
public String nextTextValue()
throws IOException, JsonParseException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString();
}
return _textBuffer.contentsAsString();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
@Override
public int nextIntValue(int defaultValue)
throws IOException, JsonParseException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getIntValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
@Override
public long nextLongValue(long defaultValue)
throws IOException, JsonParseException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getLongValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
@Override
public Boolean nextBooleanValue()
throws IOException, JsonParseException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
switch (nextToken()) {
case VALUE_TRUE:
return Boolean.TRUE;
case VALUE_FALSE:
return Boolean.FALSE;
}
return null;
}
/*
/**********************************************************
/* Internal methods, number parsing
/* (note: in 1.6 and prior, part of "Utf8NumericParser"
/**********************************************************
*/
/**
* Initial parsing method for number values. It needs to be able
* to parse enough input to be able to determine whether the
* value is to be considered a simple integer value, or a more
* generic decimal value: latter of which needs to be expressed
* as a floating point number. The basic rule is that if the number
* has no fractional or exponential part, it is an integer; otherwise
* a floating point number.
*
* Because much of input has to be processed in any case, no partial
* parsing is done: all input text will be stored for further
* processing. However, actual numeric value conversion will be
* deferred, since it is usually the most complicated and costliest
* part of processing.
*/
protected final JsonToken parseNumberText(int c)
throws IOException, JsonParseException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
boolean negative = (c == INT_MINUS);
// Need to prepend sign?
if (negative) {
outBuf[outPtr++] = '-';
// Must have something after sign too
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
// Note: must be followed by a digit
if (c < INT_0 || c > INT_9) {
return _handleInvalidNumberStart(c, true);
}
}
// One special case: if first char is 0, must not be followed by a digit
if (c == INT_0) {
c = _verifyNoLeadingZeroes();
}
// Ok: we can first just add digit we saw first:
outBuf[outPtr++] = (char) c;
int intLen = 1;
// And then figure out how far we can read without further checks:
int end = _inputPtr + outBuf.length;
if (end > _inputEnd) {
end = _inputEnd;
}
// With this, we have a nice and tight loop:
while (true) {
if (_inputPtr >= end) {
// Long enough to be split across boundary, so:
return _parserNumber2(outBuf, outPtr, negative, intLen);
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c < INT_0 || c > INT_9) {
break;
}
++intLen;
outBuf[outPtr++] = (char) c;
}
if (c == '.' || c == 'e' || c == 'E') {
return _parseFloatText(outBuf, outPtr, c, negative, intLen);
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return resetInt(negative, intLen);
}
/**
* Method called to handle parsing when input is split across buffer boundary
* (or output is longer than segment used to store it)
*/
private final JsonToken _parserNumber2(char[] outBuf, int outPtr, boolean negative,
int intPartLength)
throws IOException, JsonParseException
{
// Ok, parse the rest
while (true) {
if (_inputPtr >= _inputEnd && !loadMore()) {
_textBuffer.setCurrentLength(outPtr);
return resetInt(negative, intPartLength);
}
int c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c > INT_9 || c < INT_0) {
if (c == '.' || c == 'e' || c == 'E') {
return _parseFloatText(outBuf, outPtr, c, negative, intPartLength);
}
break;
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
++intPartLength;
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return resetInt(negative, intPartLength);
}
/**
* Method called when we have seen one zero, and want to ensure
* it is not followed by another
*/
private final int _verifyNoLeadingZeroes()
throws IOException, JsonParseException
{
// Ok to have plain "0"
if (_inputPtr >= _inputEnd && !loadMore()) {
return INT_0;
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
// if not followed by a number (probably '.'); return zero as is, to be included
if (ch < INT_0 || ch > INT_9) {
return INT_0;
}
// [JACKSON-358]: we may want to allow them, after all...
if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) {
reportInvalidNumber("Leading zeroes not allowed");
}
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
++_inputPtr; // Leading zero to be skipped
if (ch == INT_0) {
while (_inputPtr < _inputEnd || loadMore()) {
ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch < INT_0 || ch > INT_9) { // followed by non-number; retain one zero
return INT_0;
}
++_inputPtr; // skip previous zeroes
if (ch != INT_0) { // followed by other number; return
break;
}
}
}
return ch;
}
private final JsonToken _parseFloatText(char[] outBuf, int outPtr, int c,
boolean negative, int integerPartLength)
throws IOException, JsonParseException
{
int fractLen = 0;
boolean eof = false;
// And then see if we get other parts
if (c == '.') { // yes, fraction
outBuf[outPtr++] = (char) c;
fract_loop:
while (true) {
if (_inputPtr >= _inputEnd && !loadMore()) {
eof = true;
break fract_loop;
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c < INT_0 || c > INT_9) {
break fract_loop;
}
++fractLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (c == 'e' || c == 'E') { // exponent?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
// Not optional, can require that we get one more char
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
// Sign indicator?
if (c == '-' || c == '+') {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
// Likewise, non optional:
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
}
exp_loop:
while (c <= INT_9 && c >= INT_0) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
if (_inputPtr >= _inputEnd && !loadMore()) {
eof = true;
break exp_loop;
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
}
}
// Ok; unless we hit end-of-input, need to push last char read back
if (!eof) {
--_inputPtr;
}
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return resetFloat(negative, integerPartLength, fractLen, expLen);
}
/*
/**********************************************************
/* Internal methods, secondary parsing
/**********************************************************
*/
protected final Name _parseFieldName(int i)
throws IOException, JsonParseException
{
if (i != INT_QUOTE) {
return _handleUnusualFieldName(i);
}
// First: can we optimize out bounds checks?
if ((_inputPtr + 9) > _inputEnd) { // Need 8 chars, plus one trailing (quote)
return slowParseFieldName();
}
// If so, can also unroll loops nicely
/* 25-Nov-2008, tatu: This may seem weird, but here we do
* NOT want to worry about UTF-8 decoding. Rather, we'll
* assume that part is ok (if not it will get caught
* later on), and just handle quotes and backslashes here.
*/
final byte[] input = _inputBuffer;
final int[] codes = sInputCodesLatin1;
int q = input[_inputPtr++] & 0xFF;
if (codes[q] == 0) {
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
_quad1 = q;
return parseMediumFieldName(i, codes);
}
if (i == INT_QUOTE) { // one byte/char case or broken
return findName(q, 4);
}
return parseFieldName(q, i, 4);
}
if (i == INT_QUOTE) { // one byte/char case or broken
return findName(q, 3);
}
return parseFieldName(q, i, 3);
}
if (i == INT_QUOTE) { // one byte/char case or broken
return findName(q, 2);
}
return parseFieldName(q, i, 2);
}
if (i == INT_QUOTE) { // one byte/char case or broken
return findName(q, 1);
}
return parseFieldName(q, i, 1);
}
if (q == INT_QUOTE) { // special case, ""
return BytesToNameCanonicalizer.getEmptyName();
}
return parseFieldName(0, q, 0); // quoting or invalid char
}
protected final Name parseMediumFieldName(int q2, final int[] codes)
throws IOException, JsonParseException
{
// Ok, got 5 name bytes so far
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 5 bytes
return findName(_quad1, q2, 1);
}
return parseFieldName(_quad1, q2, i, 1); // quoting or invalid char
}
q2 = (q2 << 8) | i;
i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 6 bytes
return findName(_quad1, q2, 2);
}
return parseFieldName(_quad1, q2, i, 2);
}
q2 = (q2 << 8) | i;
i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 7 bytes
return findName(_quad1, q2, 3);
}
return parseFieldName(_quad1, q2, i, 3);
}
q2 = (q2 << 8) | i;
i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 8 bytes
return findName(_quad1, q2, 4);
}
return parseFieldName(_quad1, q2, i, 4);
}
_quadBuffer[0] = _quad1;
_quadBuffer[1] = q2;
return parseLongFieldName(i);
}
protected Name parseLongFieldName(int q)
throws IOException, JsonParseException
{
// As explained above, will ignore UTF-8 encoding at this point
final int[] codes = sInputCodesLatin1;
int qlen = 2;
while (true) {
/* Let's offline if we hit buffer boundary (otherwise would
* need to [try to] align input, which is bit complicated
* and may not always be possible)
*/
if ((_inputEnd - _inputPtr) < 4) {
return parseEscapedFieldName(_quadBuffer, qlen, 0, q, 0);
}
// Otherwise can skip boundary checks for 4 bytes in loop
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 1);
}
return parseEscapedFieldName(_quadBuffer, qlen, q, i, 1);
}
q = (q << 8) | i;
i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 2);
}
return parseEscapedFieldName(_quadBuffer, qlen, q, i, 2);
}
q = (q << 8) | i;
i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 3);
}
return parseEscapedFieldName(_quadBuffer, qlen, q, i, 3);
}
q = (q << 8) | i;
i = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 4);
}
return parseEscapedFieldName(_quadBuffer, qlen, q, i, 4);
}
// Nope, no end in sight. Need to grow quad array etc
if (qlen >= _quadBuffer.length) {
_quadBuffer = growArrayBy(_quadBuffer, qlen);
}
_quadBuffer[qlen++] = q;
q = i;
}
}
/**
* Method called when not even first 8 bytes are guaranteed
* to come consequtively. Happens rarely, so this is offlined;
* plus we'll also do full checks for escaping etc.
*/
protected Name slowParseFieldName()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing '\"' for name");
}
}
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i == INT_QUOTE) { // special case, ""
return BytesToNameCanonicalizer.getEmptyName();
}
return parseEscapedFieldName(_quadBuffer, 0, 0, i, 0);
}
private final Name parseFieldName(int q1, int ch, int lastQuadBytes)
throws IOException, JsonParseException
{
return parseEscapedFieldName(_quadBuffer, 0, q1, ch, lastQuadBytes);
}
private final Name parseFieldName(int q1, int q2, int ch, int lastQuadBytes)
throws IOException, JsonParseException
{
_quadBuffer[0] = q1;
return parseEscapedFieldName(_quadBuffer, 1, q2, ch, lastQuadBytes);
}
/**
* Slower parsing method which is generally branched to when
* an escape sequence is detected (or alternatively for long
* names, or ones crossing input buffer boundary). In any case,
* needs to be able to handle more exceptional cases, gets
* slower, and hance is offlined to a separate method.
*/
protected Name parseEscapedFieldName(int[] quads, int qlen, int currQuad, int ch,
int currQuadBytes)
throws IOException, JsonParseException
{
/* 25-Nov-2008, tatu: This may seem weird, but here we do
* NOT want to worry about UTF-8 decoding. Rather, we'll
* assume that part is ok (if not it will get caught
* later on), and just handle quotes and backslashes here.
*/
final int[] codes = sInputCodesLatin1;
while (true) {
if (codes[ch] != 0) {
if (ch == INT_QUOTE) { // we are done
break;
}
// Unquoted white space?
if (ch != INT_BACKSLASH) {
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeEscaped();
}
/* Oh crap. May need to UTF-8 (re-)encode it, if it's
* beyond 7-bit ascii. Gets pretty messy.
* If this happens often, may want to use different name
* canonicalization to avoid these hits.
*/
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in field name");
}
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
}
Name name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/**
* Method called when we see non-white space character other
* than double quote, when expecting a field name.
* In standard mode will just throw an expection; but
* in non-standard modes may be able to parse name.
*/
protected final Name _handleUnusualFieldName(int ch)
throws IOException, JsonParseException
{
// [JACKSON-173]: allow single quotes
if (ch == INT_APOSTROPHE && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
return _parseApostropheFieldName();
}
// [JACKSON-69]: allow unquoted names if feature enabled:
if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) {
_reportUnexpectedChar(ch, "was expecting double-quote to start field name");
}
/* Also: note that although we use a different table here,
* it does NOT handle UTF-8 decoding. It'll just pass those
* high-bit codes as acceptable for later decoding.
*/
final int[] codes = CharTypes.getInputCodeUtf8JsNames();
// Also: must start with a valid character...
if (codes[ch] != 0) {
_reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
}
/* Ok, now; instead of ultra-optimizing parsing here (as with
* regular JSON names), let's just use the generic "slow"
* variant. Can measure its impact later on if need be
*/
int[] quads = _quadBuffer;
int qlen = 0;
int currQuad = 0;
int currQuadBytes = 0;
while (true) {
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in field name");
}
}
ch = _inputBuffer[_inputPtr] & 0xFF;
if (codes[ch] != 0) {
break;
}
++_inputPtr;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
}
Name name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/* Parsing to support [JACKSON-173]. Plenty of duplicated code;
* main reason being to try to avoid slowing down fast path
* for valid JSON -- more alternatives, more code, generally
* bit slower execution.
*/
protected final Name _parseApostropheFieldName()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing '\'' for name");
}
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch == INT_APOSTROPHE) { // special case, ''
return BytesToNameCanonicalizer.getEmptyName();
}
int[] quads = _quadBuffer;
int qlen = 0;
int currQuad = 0;
int currQuadBytes = 0;
// Copied from parseEscapedFieldName, with minor mods:
final int[] codes = sInputCodesLatin1;
while (true) {
if (ch == INT_APOSTROPHE) {
break;
}
// additional check to skip handling of double-quotes
if (ch != INT_QUOTE && codes[ch] != 0) {
if (ch != INT_BACKSLASH) {
// Unquoted white space?
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeEscaped();
}
/* Oh crap. May need to UTF-8 (re-)encode it, if it's
* beyond 7-bit ascii. Gets pretty messy.
* If this happens often, may want to use different name
* canonicalization to avoid these hits.
*/
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in field name");
}
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
}
Name name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/*
/**********************************************************
/* Internal methods, symbol (name) handling
/**********************************************************
*/
private final Name findName(int q1, int lastQuadBytes)
throws JsonParseException
{
// Usually we'll find it from the canonical symbol table already
Name name = _symbols.findName(q1);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
return addName(_quadBuffer, 1, lastQuadBytes);
}
private final Name findName(int q1, int q2, int lastQuadBytes)
throws JsonParseException
{
// Usually we'll find it from the canonical symbol table already
Name name = _symbols.findName(q1, q2);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
_quadBuffer[1] = q2;
return addName(_quadBuffer, 2, lastQuadBytes);
}
private final Name findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes)
throws JsonParseException
{
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = lastQuad;
Name name = _symbols.findName(quads, qlen);
if (name == null) {
return addName(quads, qlen, lastQuadBytes);
}
return name;
}
/**
* This is the main workhorse method used when we take a symbol
* table miss. It needs to demultiplex individual bytes, decode
* multi-byte chars (if any), and then construct Name instance
* and add it to the symbol table.
*/
private final Name addName(int[] quads, int qlen, int lastQuadBytes)
throws JsonParseException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
* (as well as error reporting for unescaped control chars)
*/
// 4 bytes per quad, except last one maybe less
int byteLen = (qlen << 2) - 4 + lastQuadBytes;
/* And last one is not correctly aligned (leading zero bytes instead
* need to shift a bit, instead of trailing). Only need to shift it
* for UTF-8 decoding; need revert for storage (since key will not
* be aligned, to optimize lookup speed)
*/
int lastQuad;
if (lastQuadBytes < 4) {
lastQuad = quads[qlen-1];
// 8/16/24 bit left shift
quads[qlen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
} else {
lastQuad = 0;
}
// Need some working space, TextBuffer works well:
char[] cbuf = _textBuffer.emptyAndGetCurrentSegment();
int cix = 0;
for (int ix = 0; ix < byteLen; ) {
int ch = quads[ix >> 2]; // current quad, need to shift+mask
int byteIx = (ix & 3);
ch = (ch >> ((3 - byteIx) << 3)) & 0xFF;
++ix;
if (ch > 127) { // multi-byte
int needed;
if ((ch & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
ch &= 0x1F;
needed = 1;
} else if ((ch & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
ch &= 0x0F;
needed = 2;
} else if ((ch & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all...
ch &= 0x07;
needed = 3;
} else { // 5- and 6-byte chars not valid xml chars
_reportInvalidInitial(ch);
needed = ch = 1; // never really gets this far
}
if ((ix + needed) > byteLen) {
_reportInvalidEOF(" in field name");
}
// Ok, always need at least one more:
int ch2 = quads[ix >> 2]; // current quad, need to shift+mask
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 1) {
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates on output)
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2 & 0xFF);
}
ch = (ch << 6) | (ch2 & 0x3F);
}
}
if (needed > 2) { // surrogate pair? once again, let's output one here, one later on
ch -= 0x10000; // to normalize it starting with 0x0
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) (0xD800 + (ch >> 10));
ch = 0xDC00 | (ch & 0x03FF);
}
}
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) ch;
}
// Ok. Now we have the character array, and can construct the String
String baseName = new String(cbuf, 0, cix);
// And finally, un-align if necessary
if (lastQuadBytes < 4) {
quads[qlen-1] = lastQuad;
}
return _symbols.addName(baseName, quads, qlen);
}
/*
/**********************************************************
/* Internal methods, String value parsing
/**********************************************************
*/
@Override
protected void _finishString()
throws IOException, JsonParseException
{
// First, single tight loop for ASCII content, not split across input buffer boundary:
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
loadMoreGuaranteed();
ptr = _inputPtr;
}
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = sInputCodesUtf8;
final int max = Math.min(_inputEnd, (ptr + outBuf.length));
final byte[] inputBuffer = _inputBuffer;
while (ptr < max) {
int c = (int) inputBuffer[ptr] & 0xFF;
if (codes[c] != 0) {
if (c == INT_QUOTE) {
_inputPtr = ptr+1;
_textBuffer.setCurrentLength(outPtr);
return;
}
break;
}
++ptr;
outBuf[outPtr++] = (char) c;
}
_inputPtr = ptr;
_finishString2(outBuf, outPtr);
}
private final void _finishString2(char[] outBuf, int outPtr)
throws IOException, JsonParseException
{
int c;
// Here we do want to do full decoding, hence:
final int[] codes = sInputCodesUtf8;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
// Then the tight ASCII non-funny-char loop:
ascii_loop:
while (true) {
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
loadMoreGuaranteed();
ptr = _inputPtr;
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr)));
while (ptr < max) {
c = (int) inputBuffer[ptr++] & 0xFF;
if (codes[c] != 0) {
_inputPtr = ptr;
break ascii_loop;
}
outBuf[outPtr++] = (char) c;
}
_inputPtr = ptr;
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
c = _decodeEscaped();
break;
case 2: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 3: // 3-byte UTF
if ((_inputEnd - _inputPtr) >= 2) {
c = _decodeUtf8_3fast(c);
} else {
c = _decodeUtf8_3(c);
}
break;
case 4: // 4-byte UTF
c = _decodeUtf8_4(c);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
}
/**
* Method called to skim through rest of unparsed String value,
* if it is not needed. This can be done bit faster if contents
* need not be stored for future access.
*/
protected void _skipString()
throws IOException, JsonParseException
{
_tokenIncomplete = false;
// Need to be fully UTF-8 aware here:
final int[] codes = sInputCodesUtf8;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
int c;
ascii_loop:
while (true) {
int ptr = _inputPtr;
int max = _inputEnd;
if (ptr >= max) {
loadMoreGuaranteed();
ptr = _inputPtr;
max = _inputEnd;
}
while (ptr < max) {
c = (int) inputBuffer[ptr++] & 0xFF;
if (codes[c] != 0) {
_inputPtr = ptr;
break ascii_loop;
}
}
_inputPtr = ptr;
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
_decodeEscaped();
break;
case 2: // 2-byte UTF
_skipUtf8_2(c);
break;
case 3: // 3-byte UTF
_skipUtf8_3(c);
break;
case 4: // 4-byte UTF
_skipUtf8_4(c);
break;
default:
if (c < INT_SPACE) {
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
}
}
/**
* Method for handling cases where first non-space character
* of an expected value token is not legal for standard JSON content.
*
* @since 1.3
*/
protected JsonToken _handleUnexpectedValue(int c)
throws IOException, JsonParseException
{
// Most likely an error, unless we are to allow single-quote-strings
switch (c) {
case '\'':
if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
return _handleApostropheValue();
}
break;
case 'N':
_matchToken("NaN", 1);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return resetAsNaN("NaN", Double.NaN);
}
_reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case '+': // note: '-' is taken as number
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOFInValue();
}
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false);
}
_reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')");
return null;
}
protected JsonToken _handleApostropheValue()
throws IOException, JsonParseException
{
int c = 0;
// Otherwise almost verbatim copy of _finishString()
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
// Here we do want to do full decoding, hence:
final int[] codes = sInputCodesUtf8;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
// Then the tight ascii non-funny-char loop:
ascii_loop:
while (true) {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
int max = _inputEnd;
{
int max2 = _inputPtr + (outBuf.length - outPtr);
if (max2 < max) {
max = max2;
}
}
while (_inputPtr < max) {
c = (int) inputBuffer[_inputPtr++] & 0xFF;
if (c == INT_APOSTROPHE || codes[c] != 0) {
break ascii_loop;
}
outBuf[outPtr++] = (char) c;
}
}
// Ok: end marker, escape or multi-byte?
if (c == INT_APOSTROPHE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
if (c != INT_QUOTE) { // marked as special, isn't here
c = _decodeEscaped();
}
break;
case 2: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 3: // 3-byte UTF
if ((_inputEnd - _inputPtr) >= 2) {
c = _decodeUtf8_3fast(c);
} else {
c = _decodeUtf8_3(c);
}
break;
case 4: // 4-byte UTF
c = _decodeUtf8_4(c);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
_throwUnquotedSpace(c, "string value");
}
// Is this good enough error message?
_reportInvalidChar(c);
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
return JsonToken.VALUE_STRING;
}
/**
* Method called if expected numeric value (due to leading sign) does not
* look like a number
*/
protected JsonToken _handleInvalidNumberStart(int ch, boolean negative)
throws IOException, JsonParseException
{
if (ch == 'I') {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOFInValue();
}
}
ch = _inputBuffer[_inputPtr++];
if (ch == 'N') {
String match = negative ? "-INF" :"+INF";
_matchToken(match, 3);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
} else if (ch == 'n') {
String match = negative ? "-Infinity" :"+Infinity";
_matchToken(match, 3);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
}
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
return null;
}
protected final void _matchToken(String matchStr, int i)
throws IOException, JsonParseException
{
final int len = matchStr.length();
do {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in a value");
}
}
if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
_reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
}
++_inputPtr;
} while (++i < len);
// but let's also ensure we either get EOF, or non-alphanum char...
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
return;
}
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch < '0' || ch == ']' || ch == '}') { // expected/allowed chars
return;
}
// but actually only alphanums are problematic
char c = (char) _decodeCharForError(ch);
if (Character.isJavaIdentifierPart(c)) {
++_inputPtr;
_reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
}
}
protected void _reportInvalidToken(String matchedPart, String msg)
throws IOException, JsonParseException
{
StringBuilder sb = new StringBuilder(matchedPart);
/* Let's just try to find what appears to be the token, using
* regular Java identifier character rules. It's just a heuristic,
* nothing fancy here (nor fast).
*/
while (true) {
if (_inputPtr >= _inputEnd && !loadMore()) {
break;
}
int i = (int) _inputBuffer[_inputPtr++];
char c = (char) _decodeCharForError(i);
if (!Character.isJavaIdentifierPart(c)) {
break;
}
++_inputPtr;
sb.append(c);
}
_reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
}
/*
/**********************************************************
/* Internal methods, ws skipping, escape/unescape
/**********************************************************
*/
private final int _skipWS()
throws IOException, JsonParseException
{
while (_inputPtr < _inputEnd || loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i != INT_SLASH) {
return i;
}
_skipComment();
} else if (i != INT_SPACE) {
if (i == INT_LF) {
_skipLF();
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
}
private final int _skipWSOrEnd()
throws IOException, JsonParseException
{
while ((_inputPtr < _inputEnd) || loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i != INT_SLASH) {
return i;
}
_skipComment();
} else if (i != INT_SPACE) {
if (i == INT_LF) {
_skipLF();
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
// We ran out of input...
_handleEOF();
return -1;
}
/**
* Helper method for matching and skipping a colon character,
* optionally surrounded by white space
*
* @since 1.9
*/
private final int _skipColon()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
// first fast case: we just got a colon without white space:
int i = _inputBuffer[_inputPtr++];
if (i == INT_COLON) {
if (_inputPtr < _inputEnd) {
i = _inputBuffer[_inputPtr] & 0xFF;
if (i > INT_SPACE && i != INT_SLASH) {
++_inputPtr;
return i;
}
}
} else {
// need to skip potential leading space
i &= 0xFF;
space_loop:
while (true) {
switch (i) {
case INT_SPACE:
case INT_TAB:
case INT_CR:
_skipCR();
break;
case INT_LF:
_skipLF();
break;
case INT_SLASH:
_skipComment();
break;
default:
if (i < INT_SPACE) {
_throwInvalidSpace(i);
}
break space_loop;
}
}
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
i = _inputBuffer[_inputPtr++] & 0xFF;
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
}
// either way, found colon, skip through trailing WS
while (_inputPtr < _inputEnd || loadMore()) {
i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i != INT_SLASH) {
return i;
}
_skipComment();
} else if (i != INT_SPACE) {
if (i == INT_LF) {
_skipLF();
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
}
private final void _skipComment()
throws IOException, JsonParseException
{
if (!isEnabled(Feature.ALLOW_COMMENTS)) {
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// First: check which comment (if either) it is:
if (_inputPtr >= _inputEnd && !loadMore()) {
_reportInvalidEOF(" in a comment");
}
int c = _inputBuffer[_inputPtr++] & 0xFF;
if (c == INT_SLASH) {
_skipCppComment();
} else if (c == INT_ASTERISK) {
_skipCComment();
} else {
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
}
}
private final void _skipCComment()
throws IOException, JsonParseException
{
// Need to be UTF-8 aware here to decode content (for skipping)
final int[] codes = CharTypes.getInputCodeComment();
// Ok: need the matching '*/'
while ((_inputPtr < _inputEnd) || loadMore()) {
int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
switch (code) {
case INT_ASTERISK:
if (_inputBuffer[_inputPtr] == INT_SLASH) {
++_inputPtr;
return;
}
break;
case INT_LF:
_skipLF();
break;
case INT_CR:
_skipCR();
break;
default: // e.g. -1
// Is this good enough error message?
_reportInvalidChar(i);
}
}
}
_reportInvalidEOF(" in a comment");
}
private final void _skipCppComment()
throws IOException, JsonParseException
{
// Ok: need to find EOF or linefeed
final int[] codes = CharTypes.getInputCodeComment();
while ((_inputPtr < _inputEnd) || loadMore()) {
int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
switch (code) {
case INT_LF:
_skipLF();
return;
case INT_CR:
_skipCR();
return;
case INT_ASTERISK: // nop for these comments
break;
default: // e.g. -1
// Is this good enough error message?
_reportInvalidChar(i);
}
}
}
}
@Override
protected final char _decodeEscaped()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in character escape sequence");
}
}
int c = (int) _inputBuffer[_inputPtr++];
switch ((int) c) {
// First, ones that are mapped
case INT_b:
return '\b';
case INT_t:
return '\t';
case INT_n:
return '\n';
case INT_f:
return '\f';
case INT_r:
return '\r';
// And these are to be returned as they are
case INT_QUOTE:
case INT_SLASH:
case INT_BACKSLASH:
return (char) c;
case INT_u: // and finally hex-escaped
break;
default:
return _handleUnrecognizedCharacterEscape((char) _decodeCharForError(c));
}
// Ok, a hex escape. Need 4 characters
int value = 0;
for (int i = 0; i < 4; ++i) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in character escape sequence");
}
}
int ch = (int) _inputBuffer[_inputPtr++];
int digit = CharTypes.charToHex(ch);
if (digit < 0) {
_reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
}
value = (value << 4) | digit;
}
return (char) value;
}
protected int _decodeCharForError(int firstByte)
throws IOException, JsonParseException
{
int c = (int) firstByte;
if (c < 0) { // if >= 0, is ascii and fine as is
int needed;
// Ok; if we end here, we got multi-byte combination
if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
c &= 0x1F;
needed = 1;
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
c &= 0x0F;
needed = 2;
} else if ((c & 0xF8) == 0xF0) {
// 4 bytes; double-char with surrogates and all...
c &= 0x07;
needed = 3;
} else {
_reportInvalidInitial(c & 0xFF);
needed = 1; // never gets here
}
int d = nextByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
if (needed > 1) { // needed == 1 means 2 bytes total
d = nextByte(); // 3rd byte
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates)
d = nextByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
}
}
}
return c;
}
/*
/**********************************************************
/* Internal methods,UTF8 decoding
/**********************************************************
*/
private final int _decodeUtf8_2(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
return ((c & 0x1F) << 6) | (d & 0x3F);
}
private final int _decodeUtf8_3(int c1)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
int c = (c1 << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
return c;
}
private final int _decodeUtf8_3fast(int c1)
throws IOException, JsonParseException
{
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
int c = (c1 << 6) | (d & 0x3F);
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
return c;
}
/**
* @return Character value minus 0x10000; this so that caller
* can readily expand it to actual surrogates
*/
private final int _decodeUtf8_4(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = ((c & 0x07) << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
/* note: won't change it to negative here, since caller
* already knows it'll need a surrogate
*/
return ((c << 6) | (d & 0x3F)) - 0x10000;
}
private final void _skipUtf8_2(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF, _inputPtr);
}
}
/* Alas, can't heavily optimize skipping, since we still have to
* do validity checks...
*/
private final void _skipUtf8_3(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
//c &= 0x0F;
c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF, _inputPtr);
}
}
private final void _skipUtf8_4(int c)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
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;
}
protected final void _skipLF() throws IOException
{
++_currInputRow;
_currInputRowStart = _inputPtr;
}
private int nextByte()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
return _inputBuffer[_inputPtr++] & 0xFF;
}
/*
/**********************************************************
/* Internal methods, error reporting
/**********************************************************
*/
protected void _reportInvalidChar(int c)
throws JsonParseException
{
// Either invalid WS or illegal UTF-8 start char
if (c < INT_SPACE) {
_throwInvalidSpace(c);
}
_reportInvalidInitial(c);
}
protected void _reportInvalidInitial(int mask)
throws JsonParseException
{
_reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask));
}
protected void _reportInvalidOther(int mask)
throws JsonParseException
{
_reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask));
}
protected void _reportInvalidOther(int mask, int ptr)
throws JsonParseException
{
_inputPtr = ptr;
_reportInvalidOther(mask);
}
public static int[] growArrayBy(int[] arr, int more)
{
if (arr == null) {
return new int[more];
}
int[] old = arr;
int len = arr.length;
arr = new int[len + more];
System.arraycopy(old, 0, arr, 0, len);
return arr;
}
/*
/**********************************************************
/* Binary access
/**********************************************************
*/
@Override
protected byte[] _decodeBase64(Base64Variant b64variant)
throws IOException, JsonParseException
{
ByteArrayBuilder builder = _getByteArrayBuilder();
//main_loop:
while (true) {
// first, we'll skip preceding white space, if any
int ch;
do {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = (int) _inputBuffer[_inputPtr++] & 0xFF;
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) { // reached the end, fair and square?
if (ch == INT_QUOTE) {
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"' && !b64variant.usesPadding()) {
decodedData >>= 4;
builder.append(decodedData);
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
if (!b64variant.usesPaddingChar(ch)) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
builder.append(decodedData);
continue;
}
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"' && !b64variant.usesPadding()) {
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
/* With padding we only get 2 bytes; but we have
* to shift it a bit so it is identical to triplet
* case with partial output.
* 3 chars gives 3x6 == 18 bits, of which 2 are
* dummies, need to discard:
*/
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
continue;
}
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
builder.appendThreeBytes(decodedData);
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/DefaultPrettyPrinter.java 0000644 0001750 0001750 00000000502 11655120726 030102 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
/**
* Deprecated version of the default pretty printer.
*
* @deprecated Moved to {@link org.codehaus.jackson.util.DefaultPrettyPrinter}; will be removed in Jackson 2.0
*/
@Deprecated
public class DefaultPrettyPrinter
extends org.codehaus.jackson.util.DefaultPrettyPrinter
{
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/Indenter.java 0000644 0001750 0001750 00000001172 11655120726 025516 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.IOException;
import org.codehaus.jackson.*;
/**
* 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
{
public void writeIndentation(JsonGenerator jg, int level)
throws IOException, JsonGenerationException;
/**
* @return True if indenter is considered inline (does not add linefeeds),
* false otherwise
*/
public boolean isInline();
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/JsonNumericParserBase.java 0000644 0001750 0001750 00000001120 11655120726 030143 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
/**
* Another intermediate base class used by all Jackson {@link JsonParser}
* implementations. Contains shared functionality for dealing with
* number parsing aspects, independent of input source decoding.
*
* @deprecated Since 1.9.0: functionality demoted down to JsonParserBase
*/
@Deprecated
public abstract class JsonNumericParserBase
extends JsonParserBase
{
protected JsonNumericParserBase(IOContext ctxt, int features) {
super(ctxt, features);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/JsonParserMinimalBase.java 0000644 0001750 0001750 00000030210 11655120726 030131 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.IOException;
import org.codehaus.jackson.*;
import org.codehaus.jackson.JsonParser.Feature;
import org.codehaus.jackson.io.NumberInput;
/**
* 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.
*
* @since 1.6
*
* @author Tatu Saloranta
*/
public abstract class JsonParserMinimalBase
extends JsonParser
{
// Control chars:
protected final static int INT_TAB = '\t';
protected final static int INT_LF = '\n';
protected final static int INT_CR = '\r';
protected final static int INT_SPACE = 0x0020;
// Markup
protected final static int INT_LBRACKET = '[';
protected final static int INT_RBRACKET = ']';
protected final static int INT_LCURLY = '{';
protected final static int INT_RCURLY = '}';
protected final static int INT_QUOTE = '"';
protected final static int INT_BACKSLASH = '\\';
protected final static int INT_SLASH = '/';
protected final static int INT_COLON = ':';
protected final static int INT_COMMA = ',';
protected final static int INT_ASTERISK = '*';
protected final static int INT_APOSTROPHE = '\'';
// Letters we need
protected final static int INT_b = 'b';
protected final static int INT_f = 'f';
protected final static int INT_n = 'n';
protected final static int INT_r = 'r';
protected final static int INT_t = 't';
protected final static int INT_u = 'u';
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected JsonParserMinimalBase() { }
protected JsonParserMinimalBase(int features) {
super(features);
}
/*
/**********************************************************
/* Configuration overrides if any
/**********************************************************
*/
// from base class:
//public void enableFeature(Feature f)
//public void disableFeature(Feature f)
//public void setFeature(Feature f, boolean state)
//public boolean isFeatureEnabled(Feature f)
/*
/**********************************************************
/* JsonParser impl
/**********************************************************
*/
@Override
public abstract JsonToken nextToken() throws IOException, JsonParseException;
//public final JsonToken nextValue()
@Override
public JsonParser skipChildren() throws IOException, JsonParseException
{
if (_currToken != JsonToken.START_OBJECT
&& _currToken != JsonToken.START_ARRAY) {
return this;
}
int open = 1;
/* Since proper matching of start/end markers is handled
* by nextToken(), we'll just count nesting levels here
*/
while (true) {
JsonToken t = nextToken();
if (t == null) {
_handleEOF();
/* given constraints, above should never return;
* however, FindBugs doesn't know about it and
* complains... so let's add dummy break here
*/
return this;
}
switch (t) {
case START_OBJECT:
case START_ARRAY:
++open;
break;
case END_OBJECT:
case END_ARRAY:
if (--open == 0) {
return this;
}
break;
}
}
}
/**
* Method sub-classes need to implement
*/
protected abstract void _handleEOF() throws JsonParseException;
//public JsonToken getCurrentToken()
//public boolean hasCurrentToken()
@Override
public abstract String getCurrentName() throws IOException, JsonParseException;
@Override
public abstract void close() throws IOException;
@Override
public abstract boolean isClosed();
@Override
public abstract JsonStreamContext getParsingContext();
// public abstract JsonLocation getTokenLocation();
// public abstract JsonLocation getCurrentLocation();
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
@Override
public abstract String getText() throws IOException, JsonParseException;
@Override
public abstract char[] getTextCharacters() throws IOException, JsonParseException;
@Override
public abstract boolean hasTextCharacters();
@Override
public abstract int getTextLength() throws IOException, JsonParseException;
@Override
public abstract int getTextOffset() throws IOException, JsonParseException;
/*
/**********************************************************
/* Public API, access to token information, binary
/**********************************************************
*/
@Override
public abstract byte[] getBinaryValue(Base64Variant b64variant)
throws IOException, JsonParseException;
/*
/**********************************************************
/* Public API, access with conversion/coercion
/**********************************************************
*/
@Override
public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException
{
if (_currToken != null) {
switch (_currToken) {
case VALUE_NUMBER_INT:
return getIntValue() != 0;
case VALUE_TRUE:
return true;
case VALUE_FALSE:
case VALUE_NULL:
return false;
case VALUE_EMBEDDED_OBJECT:
{
Object value = this.getEmbeddedObject();
if (value instanceof Boolean) {
return ((Boolean) value).booleanValue();
}
}
case VALUE_STRING:
String str = getText().trim();
if ("true".equals(str)) {
return true;
}
break;
}
}
return defaultValue;
}
@Override
public int getValueAsInt(int defaultValue) throws IOException, JsonParseException
{
if (_currToken != null) {
switch (_currToken) {
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return getIntValue();
case VALUE_TRUE:
return 1;
case VALUE_FALSE:
case VALUE_NULL:
return 0;
case VALUE_STRING:
return NumberInput.parseAsInt(getText(), defaultValue);
case VALUE_EMBEDDED_OBJECT:
{
Object value = this.getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).intValue();
}
}
}
}
return defaultValue;
}
@Override
public long getValueAsLong(long defaultValue) throws IOException, JsonParseException
{
if (_currToken != null) {
switch (_currToken) {
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return getLongValue();
case VALUE_TRUE:
return 1;
case VALUE_FALSE:
case VALUE_NULL:
return 0;
case VALUE_STRING:
return NumberInput.parseAsLong(getText(), defaultValue);
case VALUE_EMBEDDED_OBJECT:
{
Object value = this.getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).longValue();
}
}
}
}
return defaultValue;
}
@Override
public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException
{
if (_currToken != null) {
switch (_currToken) {
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return getDoubleValue();
case VALUE_TRUE:
return 1;
case VALUE_FALSE:
case VALUE_NULL:
return 0;
case VALUE_STRING:
return NumberInput.parseAsDouble(getText(), defaultValue);
case VALUE_EMBEDDED_OBJECT:
{
Object value = this.getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
}
}
}
return defaultValue;
}
/*
/**********************************************************
/* Error reporting
/**********************************************************
*/
protected void _reportUnexpectedChar(int ch, String comment)
throws JsonParseException
{
String msg = "Unexpected character ("+_getCharDesc(ch)+")";
if (comment != null) {
msg += ": "+comment;
}
_reportError(msg);
}
protected void _reportInvalidEOF()
throws JsonParseException
{
_reportInvalidEOF(" in "+_currToken);
}
protected void _reportInvalidEOF(String msg)
throws JsonParseException
{
_reportError("Unexpected end-of-input"+msg);
}
protected void _reportInvalidEOFInValue() throws JsonParseException
{
_reportInvalidEOF(" in a value");
}
protected void _throwInvalidSpace(int i)
throws JsonParseException
{
char c = (char) i;
String msg = "Illegal character ("+_getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens";
_reportError(msg);
}
/**
* Method called to report a problem with unquoted control character.
* Note: starting with version 1.4, it is possible to suppress
* exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}.
*/
protected void _throwUnquotedSpace(int i, String ctxtDesc)
throws JsonParseException
{
// JACKSON-208; possible to allow unquoted control chars:
if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i >= INT_SPACE) {
char c = (char) i;
String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc;
_reportError(msg);
}
}
protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException
{
// as per [JACKSON-300]
if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) {
return ch;
}
// and [JACKSON-548]
if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
return ch;
}
_reportError("Unrecognized character escape "+_getCharDesc(ch));
return ch;
}
/*
/**********************************************************
/* Error reporting, generic
/**********************************************************
*/
protected final static String _getCharDesc(int ch)
{
char c = (char) ch;
if (Character.isISOControl(c)) {
return "(CTRL-CHAR, code "+ch+")";
}
if (ch > 255) {
return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")";
}
return "'"+c+"' (code "+ch+")";
}
protected final void _reportError(String msg)
throws JsonParseException
{
throw _constructError(msg);
}
protected final void _wrapError(String msg, Throwable t)
throws JsonParseException
{
throw _constructError(msg, t);
}
protected final void _throwInternal()
{
throw new RuntimeException("Internal error: this code path should never get executed");
}
protected final JsonParseException _constructError(String msg, Throwable t)
{
return new JsonParseException(msg, getCurrentLocation(), t);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/ByteSourceBootstrapper.java 0000644 0001750 0001750 00000042043 11655120726 030441 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.format.InputAccessor;
import org.codehaus.jackson.format.MatchStrength;
import org.codehaus.jackson.io.*;
import org.codehaus.jackson.sym.BytesToNameCanonicalizer;
import org.codehaus.jackson.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 ByteSourceBootstrapper
{
final static byte UTF8_BOM_1 = (byte) 0xEF;
final static byte UTF8_BOM_2 = (byte) 0xBB;
final static byte UTF8_BOM_3 = (byte) 0xBF;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
protected final IOContext _context;
protected final InputStream _in;
/*
/**********************************************************
/* Input buffering
/**********************************************************
*/
protected final byte[] _inputBuffer;
private int _inputPtr;
private int _inputEnd;
/**
* Flag that indicates whether buffer above is to be recycled
* after being used or not.
*/
private final boolean _bufferRecyclable;
/*
/**********************************************************
/* Input location
/**********************************************************
*/
/**
* Current number of input units (bytes or chars) that were processed in
* previous blocks,
* before contents of current input buffer.
*
* Note: includes possible BOMs, if those were part of the input.
*/
protected int _inputProcessed;
/*
/**********************************************************
/* Data gathered
/**********************************************************
*/
protected boolean _bigEndian = true;
protected int _bytesPerChar = 0; // 0 means "dunno yet"
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public ByteSourceBootstrapper(IOContext ctxt, InputStream in)
{
_context = ctxt;
_in = in;
_inputBuffer = ctxt.allocReadIOBuffer();
_inputEnd = _inputPtr = 0;
_inputProcessed = 0;
_bufferRecyclable = true;
}
public ByteSourceBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen)
{
_context = ctxt;
_in = null;
_inputBuffer = inputBuffer;
_inputPtr = inputStart;
_inputEnd = (inputStart + inputLen);
// Need to offset this for correct location info
_inputProcessed = -inputStart;
_bufferRecyclable = false;
}
/*
/**********************************************************
/* Encoding detection during bootstrapping
/**********************************************************
*/
/**
* Method that should be called after constructing an instace.
* It will figure out encoding that content uses, to allow
* for instantiating a proper scanner object.
*/
public JsonEncoding detectEncoding()
throws IOException, JsonParseException
{
boolean foundEncoding = false;
// First things first: BOM handling
/* Note: we can require 4 bytes to be read, since no
* combination of BOM + valid JSON content can have
* shorter length (shortest valid JSON content is single
* digit char, but BOMs are chosen such that combination
* is always at least 4 chars long)
*/
if (ensureLoaded(4)) {
int quad = (_inputBuffer[_inputPtr] << 24)
| ((_inputBuffer[_inputPtr+1] & 0xFF) << 16)
| ((_inputBuffer[_inputPtr+2] & 0xFF) << 8)
| (_inputBuffer[_inputPtr+3] & 0xFF);
if (handleBOM(quad)) {
foundEncoding = true;
} else {
/* If no BOM, need to auto-detect based on first char;
* this works since it must be 7-bit ascii (wrt. unicode
* compatible encodings, only ones JSON can be transferred
* over)
*/
// UTF-32?
if (checkUTF32(quad)) {
foundEncoding = true;
} else if (checkUTF16(quad >>> 16)) {
foundEncoding = true;
}
}
} else if (ensureLoaded(2)) {
int i16 = ((_inputBuffer[_inputPtr] & 0xFF) << 8)
| (_inputBuffer[_inputPtr+1] & 0xFF);
if (checkUTF16(i16)) {
foundEncoding = true;
}
}
JsonEncoding enc;
/* Not found yet? As per specs, this means it must be UTF-8. */
if (!foundEncoding) {
enc = JsonEncoding.UTF8;
} else {
switch (_bytesPerChar) {
case 1:
enc = JsonEncoding.UTF8;
break;
case 2:
enc = _bigEndian ? JsonEncoding.UTF16_BE : JsonEncoding.UTF16_LE;
break;
case 4:
enc = _bigEndian ? JsonEncoding.UTF32_BE : JsonEncoding.UTF32_LE;
break;
default:
throw new RuntimeException("Internal error"); // should never get here
}
}
_context.setEncoding(enc);
return enc;
}
/*
/**********************************************************
/* Constructing a Reader
/**********************************************************
*/
public Reader constructReader()
throws IOException
{
JsonEncoding enc = _context.getEncoding();
switch (enc) {
case UTF32_BE:
case UTF32_LE:
return new UTF32Reader(_context, _in, _inputBuffer, _inputPtr, _inputEnd,
_context.getEncoding().isBigEndian());
case UTF16_BE:
case UTF16_LE:
case UTF8: // only in non-common case where we don't want to do direct mapping
{
// First: do we have a Stream? If not, need to create one:
InputStream in = _in;
if (in == null) {
in = new ByteArrayInputStream(_inputBuffer, _inputPtr, _inputEnd);
} else {
/* Also, if we have any read but unused input (usually true),
* need to merge that input in:
*/
if (_inputPtr < _inputEnd) {
in = new MergedStream(_context, in, _inputBuffer, _inputPtr, _inputEnd);
}
}
return new InputStreamReader(in, enc.getJavaName());
}
}
throw new RuntimeException("Internal error"); // should never get here
}
public JsonParser constructParser(int features, ObjectCodec codec, BytesToNameCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols)
throws IOException, JsonParseException
{
JsonEncoding enc = detectEncoding();
// As per [JACKSON-259], may want to fully disable canonicalization:
boolean canonicalize = JsonParser.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(features);
boolean intern = JsonParser.Feature.INTERN_FIELD_NAMES.enabledIn(features);
if (enc == JsonEncoding.UTF8) {
/* and without canonicalization, byte-based approach is not performance; just use std UTF-8 reader
* (which is ok for larger input; not so hot for smaller; but this is not a common case)
*/
if (canonicalize) {
BytesToNameCanonicalizer can = rootByteSymbols.makeChild(canonicalize, intern);
return new Utf8StreamParser(_context, features, _in, codec, can, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable);
}
}
return new ReaderBasedParser(_context, features, constructReader(), codec, rootCharSymbols.makeChild(canonicalize, intern));
}
/*
/**********************************************************
/* Encoding detection for data format auto-detection
/**********************************************************
*/
/**
* Current implementation is not as thorough as other functionality
* ({@link org.codehaus.jackson.impl.ByteSourceBootstrapper});
* supports UTF-8, for example. But it should work, for now, and can
* be improved as necessary.
*
* @since 1.8
*/
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 final 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 final static int skipSpace(InputAccessor acc) throws IOException
{
if (!acc.hasMoreBytes()) {
return -1;
}
return skipSpace(acc, acc.nextByte());
}
private final static int skipSpace(InputAccessor acc, byte b) throws IOException
{
while (true) {
int ch = (int) b & 0xFF;
if (!(ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')) {
return ch;
}
if (!acc.hasMoreBytes()) {
return -1;
}
b = acc.nextByte();
ch = (int) b & 0xFF;
}
}
/*
/**********************************************************
/* Internal methods, parsing
/**********************************************************
*/
/**
* @return True if a BOM was succesfully found, and encoding
* thereby recognized.
*/
private boolean handleBOM(int quad)
throws IOException
{
/* Handling of (usually) optional BOM (required for
* multi-byte formats); first 32-bit charsets:
*/
switch (quad) {
case 0x0000FEFF:
_bigEndian = true;
_inputPtr += 4;
_bytesPerChar = 4;
return true;
case 0xFFFE0000: // UCS-4, LE?
_inputPtr += 4;
_bytesPerChar = 4;
_bigEndian = false;
return true;
case 0x0000FFFE: // UCS-4, in-order...
reportWeirdUCS4("2143"); // throws exception
case 0xFEFF0000: // UCS-4, in-order...
reportWeirdUCS4("3412"); // throws exception
}
// Ok, if not, how about 16-bit encoding BOMs?
int msw = quad >>> 16;
if (msw == 0xFEFF) { // UTF-16, BE
_inputPtr += 2;
_bytesPerChar = 2;
_bigEndian = true;
return true;
}
if (msw == 0xFFFE) { // UTF-16, LE
_inputPtr += 2;
_bytesPerChar = 2;
_bigEndian = false;
return true;
}
// And if not, then UTF-8 BOM?
if ((quad >>> 8) == 0xEFBBBF) { // UTF-8
_inputPtr += 3;
_bytesPerChar = 1;
_bigEndian = true; // doesn't really matter
return true;
}
return false;
}
private boolean checkUTF32(int quad)
throws IOException
{
/* Handling of (usually) optional BOM (required for
* multi-byte formats); first 32-bit charsets:
*/
if ((quad >> 8) == 0) { // 0x000000?? -> UTF32-BE
_bigEndian = true;
} else if ((quad & 0x00FFFFFF) == 0) { // 0x??000000 -> UTF32-LE
_bigEndian = false;
} else if ((quad & ~0x00FF0000) == 0) { // 0x00??0000 -> UTF32-in-order
reportWeirdUCS4("3412");
} else if ((quad & ~0x0000FF00) == 0) { // 0x0000??00 -> UTF32-in-order
reportWeirdUCS4("2143");
} else {
// Can not be valid UTF-32 encoded JSON...
return false;
}
// Not BOM (just regular content), nothing to skip past:
//_inputPtr += 4;
_bytesPerChar = 4;
return true;
}
private boolean checkUTF16(int i16)
{
if ((i16 & 0xFF00) == 0) { // UTF-16BE
_bigEndian = true;
} else if ((i16 & 0x00FF) == 0) { // UTF-16LE
_bigEndian = false;
} else { // nope, not UTF-16
return false;
}
// Not BOM (just regular content), nothing to skip past:
//_inputPtr += 2;
_bytesPerChar = 2;
return true;
}
/*
/**********************************************************
/* Internal methods, problem reporting
/**********************************************************
*/
private void reportWeirdUCS4(String type)
throws IOException
{
throw new CharConversionException("Unsupported UCS-4 endianness ("+type+") detected");
}
/*
/**********************************************************
/* Internal methods, raw input access
/**********************************************************
*/
protected boolean ensureLoaded(int minimum)
throws IOException
{
/* Let's assume here buffer has enough room -- this will always
* be true for the limited used this method gets
*/
int gotten = (_inputEnd - _inputPtr);
while (gotten < minimum) {
int count;
if (_in == null) { // block source
count = -1;
} else {
count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
}
if (count < 1) {
return false;
}
_inputEnd += count;
gotten += count;
}
return true;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/JsonWriteContext.java 0000644 0001750 0001750 00000011413 11655120726 027236 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import org.codehaus.jackson.*;
/**
* Extension of {@link JsonStreamContext}, which implements
* core methods needed, and also exposes
* more complete API to generator implementation classes.
*/
public class JsonWriteContext
extends JsonStreamContext
{
// // // Return values for writeValue()
public final static int STATUS_OK_AS_IS = 0;
public final static int STATUS_OK_AFTER_COMMA = 1;
public final static int STATUS_OK_AFTER_COLON = 2;
public final static int STATUS_OK_AFTER_SPACE = 3; // in root context
public final static int STATUS_EXPECT_VALUE = 4;
public final static int STATUS_EXPECT_NAME = 5;
protected final JsonWriteContext _parent;
/**
* Name of the field of which value is to be parsed; only
* used for OBJECT contexts
*/
protected String _currentName;
/*
/**********************************************************
/* Simple instance reuse slots; speed up things
/* a bit (10-15%) for docs with lots of small
/* arrays/objects
/**********************************************************
*/
protected JsonWriteContext _child = null;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected JsonWriteContext(int type, JsonWriteContext parent)
{
super();
_type = type;
_parent = parent;
_index = -1;
}
// // // Factory methods
public static JsonWriteContext createRootContext()
{
return new JsonWriteContext(TYPE_ROOT, null);
}
private final JsonWriteContext reset(int type) {
_type = type;
_index = -1;
_currentName = null;
return this;
}
public final JsonWriteContext createChildArrayContext()
{
JsonWriteContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonWriteContext(TYPE_ARRAY, this);
return ctxt;
}
return ctxt.reset(TYPE_ARRAY);
}
public final JsonWriteContext createChildObjectContext()
{
JsonWriteContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonWriteContext(TYPE_OBJECT, this);
return ctxt;
}
return ctxt.reset(TYPE_OBJECT);
}
// // // Shared API
@Override
public final JsonWriteContext getParent() { return _parent; }
@Override
public final String getCurrentName() { return _currentName; }
// // // API sub-classes are to implement
/**
* Method that writer is to call before it writes a field name.
*
* @return Index of the field entry (0-based)
*/
public final int writeFieldName(String name)
{
if (_type == TYPE_OBJECT) {
if (_currentName != null) { // just wrote a name...
return STATUS_EXPECT_VALUE;
}
_currentName = name;
return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
}
return STATUS_EXPECT_VALUE;
}
public final int writeValue()
{
// Most likely, object:
if (_type == TYPE_OBJECT) {
if (_currentName == null) {
return STATUS_EXPECT_NAME;
}
_currentName = null;
++_index;
return STATUS_OK_AFTER_COLON;
}
// Ok, array?
if (_type == TYPE_ARRAY) {
int ix = _index;
++_index;
return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
}
// Nope, root context
// No commas within root context, but need space
++_index;
return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE;
}
// // // Internally used abstract methods
protected final void appendDesc(StringBuilder sb)
{
if (_type == TYPE_OBJECT) {
sb.append('{');
if (_currentName != null) {
sb.append('"');
// !!! TODO: Name chars should be escaped?
sb.append(_currentName);
sb.append('"');
} else {
sb.append('?');
}
sb.append('}');
} else if (_type == TYPE_ARRAY) {
sb.append('[');
sb.append(getCurrentIndex());
sb.append(']');
} else {
// nah, ROOT:
sb.append("/");
}
}
// // // Overridden standard methods
/**
* Overridden to provide developer writeable "JsonPath" representation
* of the context.
*/
@Override
public final String toString()
{
StringBuilder sb = new StringBuilder(64);
appendDesc(sb);
return sb.toString();
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/WriterBasedGenerator.java 0000644 0001750 0001750 00000167131 11655120726 030040 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.*;
import org.codehaus.jackson.util.CharTypes;
/**
* {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer}
* which handles character encoding.
*/
public final class WriterBasedGenerator
extends JsonGeneratorBase
{
final protected static int SHORT_WRITE = 32;
final protected static char[] HEX_CHARS = CharTypes.copyHexChars();
/**
* 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
/**********************************************************
*/
final protected IOContext _ioContext;
final protected Writer _writer;
/*
/**********************************************************
/* 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.
*
* @since 1.8
*/
protected CharacterEscapes _characterEscapes;
/**
* When custom escapes are used, this member variable can be used to
* store escape to use
*
* @since 1.8
*/
protected SerializableString _currentEscape;
/*
/**********************************************************
/* Output buffering
/**********************************************************
*/
/**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_writer}.
*/
protected char[] _outputBuffer;
/**
* Pointer to the first buffered character to output
*/
protected int _outputHead = 0;
/**
* Pointer to the position right beyond the last character to output
* (end marker; may point to position right beyond the end of the buffer)
*/
protected int _outputTail = 0;
/**
* End marker of the output buffer; one past the last valid position
* within the buffer.
*/
protected int _outputEnd;
/**
* Short (14 char) temporary buffer allocated if needed, for constructing
* escape sequences
*/
protected char[] _entityBuffer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public WriterBasedGenerator(IOContext ctxt, int features, ObjectCodec codec,
Writer w)
{
super(features, codec);
_ioContext = ctxt;
_writer = w;
_outputBuffer = ctxt.allocConcatBuffer();
_outputEnd = _outputBuffer.length;
if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
setHighestNonEscapedChar(127);
}
}
/*
/**********************************************************
/* Overridden configuration methods
/**********************************************************
*/
@Override
public JsonGenerator setHighestNonEscapedChar(int charCode) {
_maximumNonEscapedChar = (charCode < 0) ? 0 : charCode;
return this;
}
@Override
public int getHighestEscapedChar() {
return _maximumNonEscapedChar;
}
@Override
public JsonGenerator setCharacterEscapes(CharacterEscapes esc)
{
_characterEscapes = esc;
if (esc == null) { // revert to standard escapes
_outputEscapes = sOutputEscapes;
} else {
_outputEscapes = esc.getEscapeCodesForAscii();
}
return this;
}
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*
* @since 1.8
*/
@Override
public CharacterEscapes getCharacterEscapes() {
return _characterEscapes;
}
@Override
public Object getOutputTarget() {
return _writer;
}
/*
/**********************************************************
/* Overridden methods
/**********************************************************
*/
/* Most overrides in this section are just to make methods final,
* to allow better inlining...
*/
@Override
public final void writeFieldName(String name) throws IOException, JsonGenerationException
{
int status = _writeContext.writeFieldName(name);
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
}
@Override
public final void writeStringField(String fieldName, String value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeString(value);
}
@Override
public final void writeFieldName(SerializedString name)
throws IOException, JsonGenerationException
{
// Object is a value, need to verify it's allowed
int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
}
@Override
public final void writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
// Object is a value, need to verify it's allowed
int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
}
/*
/**********************************************************
/* Output method implementations, structural
/**********************************************************
*/
@Override
public final void writeStartArray() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '[';
}
}
@Override
public final void writeEndArray() throws IOException, JsonGenerationException
{
if (!_writeContext.inArray()) {
_reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = ']';
}
_writeContext = _writeContext.getParent();
}
@Override
public final void writeStartObject() throws IOException, JsonGenerationException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '{';
}
}
@Override
public final void writeEndObject() throws IOException, JsonGenerationException
{
if (!_writeContext.inObject()) {
_reportError("Current context not an object but "+_writeContext.getTypeDesc());
}
_writeContext = _writeContext.getParent();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '}';
}
}
protected void _writeFieldName(String name, boolean commaBefore)
throws IOException, JsonGenerationException
{
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, commaBefore);
return;
}
// for fast+std case, need to output up to 2 chars, comma, dquote
if ((_outputTail + 1) >= _outputEnd) {
_flushBuffer();
}
if (commaBefore) {
_outputBuffer[_outputTail++] = ',';
}
/* To support [JACKSON-46], we'll do this:
* (Question: should quoting of spaces (etc) still be enabled?)
*/
if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
_writeString(name);
return;
}
// we know there's room for at least one more char
_outputBuffer[_outputTail++] = '"';
// The beef:
_writeString(name);
// and closing quotes; need room for one more char:
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
}
public void _writeFieldName(SerializableString name, boolean commaBefore)
throws IOException, JsonGenerationException
{
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, commaBefore);
return;
}
// for fast+std case, need to output up to 2 chars, comma, dquote
if ((_outputTail + 1) >= _outputEnd) {
_flushBuffer();
}
if (commaBefore) {
_outputBuffer[_outputTail++] = ',';
}
/* To support [JACKSON-46], we'll do this:
* (Question: should quoting of spaces (etc) still be enabled?)
*/
final char[] quoted = name.asQuotedChars();
if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
writeRaw(quoted, 0, quoted.length);
return;
}
// we know there's room for at least one more char
_outputBuffer[_outputTail++] = '"';
// The beef:
final int qlen = quoted.length;
if ((_outputTail + qlen + 1) >= _outputEnd) {
writeRaw(quoted, 0, qlen);
// and closing quotes; need room for one more char:
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
} else {
System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen);
_outputTail += qlen;
_outputBuffer[_outputTail++] = '"';
}
}
/**
* Specialized version of _writeFieldName
, off-lined
* to keep the "fast path" as simple (and hopefully fast) as possible.
*/
protected final void _writePPFieldName(String name, boolean commaBefore)
throws IOException, JsonGenerationException
{
if (commaBefore) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
_writeString(name);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
} else { // non-standard, omit quotes
_writeString(name);
}
}
protected final void _writePPFieldName(SerializableString name, boolean commaBefore)
throws IOException, JsonGenerationException
{
if (commaBefore) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
final char[] quoted = name.asQuotedChars();
if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
writeRaw(quoted, 0, quoted.length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
} else { // non-standard, omit quotes
writeRaw(quoted, 0, quoted.length);
}
}
/*
/**********************************************************
/* Output method implementations, textual
/**********************************************************
*/
@Override
public void writeString(String text)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (text == null) {
_writeNull();
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
_writeString(text);
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
}
@Override
public void writeString(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
_writeString(text, offset, len);
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
}
@Override
public final void writeString(SerializableString sstr)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write text value");
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
// Note: copied from writeRaw:
char[] text = sstr.asQuotedChars();
final int len = text.length;
// Only worth buffering if it's a short write?
if (len < SHORT_WRITE) {
int room = _outputEnd - _outputTail;
if (len > room) {
_flushBuffer();
}
System.arraycopy(text, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
} else {
// Otherwise, better just pass through:
_flushBuffer();
_writer.write(text, 0, len);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
// could add support for buffering if we really want it...
_reportUnsupportedOperation();
}
@Override
public void writeUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
// could add support for buffering if we really want it...
_reportUnsupportedOperation();
}
/*
/**********************************************************
/* Output method implementations, unprocessed ("raw")
/**********************************************************
*/
@Override
public void writeRaw(String text)
throws IOException, JsonGenerationException
{
// Nothing to check, can just output as is
int len = text.length();
int room = _outputEnd - _outputTail;
if (room == 0) {
_flushBuffer();
room = _outputEnd - _outputTail;
}
// But would it nicely fit in? If yes, it's easy
if (room >= len) {
text.getChars(0, len, _outputBuffer, _outputTail);
_outputTail += len;
} else {
writeRawLong(text);
}
}
@Override
public void writeRaw(String text, int start, int len)
throws IOException, JsonGenerationException
{
// Nothing to check, can just output as is
int room = _outputEnd - _outputTail;
if (room < len) {
_flushBuffer();
room = _outputEnd - _outputTail;
}
// But would it nicely fit in? If yes, it's easy
if (room >= len) {
text.getChars(start, start+len, _outputBuffer, _outputTail);
_outputTail += len;
} else {
writeRawLong(text.substring(start, start+len));
}
}
@Override
public void writeRaw(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
// Only worth buffering if it's a short write?
if (len < SHORT_WRITE) {
int room = _outputEnd - _outputTail;
if (len > room) {
_flushBuffer();
}
System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
return;
}
// Otherwise, better just pass through:
_flushBuffer();
_writer.write(text, offset, len);
}
@Override
public void writeRaw(char c)
throws IOException, JsonGenerationException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = c;
}
private void writeRawLong(String text)
throws IOException, JsonGenerationException
{
int room = _outputEnd - _outputTail;
// If not, need to do it by looping
text.getChars(0, room, _outputBuffer, _outputTail);
_outputTail += room;
_flushBuffer();
int offset = room;
int len = text.length() - room;
while (len > _outputEnd) {
int amount = _outputEnd;
text.getChars(offset, offset+amount, _outputBuffer, 0);
_outputHead = 0;
_outputTail = amount;
_flushBuffer();
offset += amount;
len -= amount;
}
// And last piece (at most length of buffer)
text.getChars(offset, offset+len, _outputBuffer, 0);
_outputHead = 0;
_outputTail = len;
}
/*
/**********************************************************
/* Output method implementations, base64-encoded binary
/**********************************************************
*/
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write binary value");
// Starting quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
_writeBinary(b64variant, data, offset, offset+len);
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
}
/*
/**********************************************************
/* Output method implementations, primitive
/**********************************************************
*/
@Override
public void writeNumber(int i)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
// up to 10 digits and possible minus sign
if ((_outputTail + 11) >= _outputEnd) {
_flushBuffer();
}
if (_cfgNumbersAsStrings) {
_writeQuotedInt(i);
return;
}
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
}
private final void _writeQuotedInt(int i) throws IOException {
if ((_outputTail + 13) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = '"';
}
@Override
public void writeNumber(long l)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
if (_cfgNumbersAsStrings) {
_writeQuotedLong(l);
return;
}
if ((_outputTail + 21) >= _outputEnd) {
// up to 20 digits, minus sign
_flushBuffer();
}
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
}
private final void _writeQuotedLong(long l) throws IOException {
if ((_outputTail + 23) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = '"';
}
// !!! 05-Aug-2008, tatus: Any ways to optimize these?
@Override
public void writeNumber(BigInteger value)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(value);
} else {
writeRaw(value.toString());
}
}
@Override
public void writeNumber(double d)
throws IOException, JsonGenerationException
{
if (_cfgNumbersAsStrings ||
// [JACKSON-139]
(((Double.isNaN(d) || Double.isInfinite(d))
&& isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
writeString(String.valueOf(d));
return;
}
// What is the max length for doubles? 40 chars?
_verifyValueWrite("write number");
writeRaw(String.valueOf(d));
}
@Override
public void writeNumber(float f)
throws IOException, JsonGenerationException
{
if (_cfgNumbersAsStrings ||
// [JACKSON-139]
(((Float.isNaN(f) || Float.isInfinite(f))
&& isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
writeString(String.valueOf(f));
return;
}
// What is the max length for floats?
_verifyValueWrite("write number");
writeRaw(String.valueOf(f));
}
@Override
public void writeNumber(BigDecimal value)
throws IOException, JsonGenerationException
{
// Don't really know max length for big decimal, no point checking
_verifyValueWrite("write number");
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(value);
} else {
writeRaw(value.toString());
}
}
@Override
public void writeNumber(String encodedValue)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write number");
if (_cfgNumbersAsStrings) {
_writeQuotedRaw(encodedValue);
} else {
writeRaw(encodedValue);
}
}
private final void _writeQuotedRaw(Object value) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
writeRaw(value.toString());
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '"';
}
@Override
public void writeBoolean(boolean state)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write boolean value");
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
int ptr = _outputTail;
char[] buf = _outputBuffer;
if (state) {
buf[ptr] = 't';
buf[++ptr] = 'r';
buf[++ptr] = 'u';
buf[++ptr] = 'e';
} else {
buf[ptr] = 'f';
buf[++ptr] = 'a';
buf[++ptr] = 'l';
buf[++ptr] = 's';
buf[++ptr] = 'e';
}
_outputTail = ptr+1;
}
@Override
public void writeNull()
throws IOException, JsonGenerationException
{
_verifyValueWrite("write null value");
_writeNull();
}
/*
/**********************************************************
/* Implementations for other methods
/**********************************************************
*/
@Override
protected final void _verifyValueWrite(String typeMsg)
throws IOException, JsonGenerationException
{
int status = _writeContext.writeValue();
if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
_reportError("Can not "+typeMsg+", expecting field name");
}
if (_cfgPrettyPrinter == null) {
char c;
switch (status) {
case JsonWriteContext.STATUS_OK_AFTER_COMMA:
c = ',';
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
c = ':';
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE:
c = ' ';
break;
case JsonWriteContext.STATUS_OK_AS_IS:
default:
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail] = c;
++_outputTail;
return;
}
// Otherwise, pretty printer knows what to do...
_verifyPrettyValueWrite(typeMsg, status);
}
protected final void _verifyPrettyValueWrite(String typeMsg, int status)
throws IOException, JsonGenerationException
{
// If we have a pretty printer, it knows what to do:
switch (status) {
case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
_cfgPrettyPrinter.writeArrayValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
_cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE:
_cfgPrettyPrinter.writeRootValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AS_IS:
// First entry, but of which context?
if (_writeContext.inArray()) {
_cfgPrettyPrinter.beforeArrayValues(this);
} else if (_writeContext.inObject()) {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
break;
default:
_cantHappen();
break;
}
}
/*
/**********************************************************
/* Low-level output handling
/**********************************************************
*/
@Override
public final void flush()
throws IOException
{
_flushBuffer();
if (_writer != null) {
if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
_writer.flush();
}
}
}
@Override
public void close()
throws IOException
{
super.close();
/* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
* scopes.
*/
// First: let's see that we still have buffers...
if (_outputBuffer != null
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
while (true) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt.inArray()) {
writeEndArray();
} else if (ctxt.inObject()) {
writeEndObject();
} else {
break;
}
}
}
_flushBuffer();
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside: when using UTF8Writer, underlying buffer(s)
* may not be properly recycled if we don't close the writer.
*/
if (_writer != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
_writer.close();
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
// If we can't close it, we should at least flush
_writer.flush();
}
}
// Internal buffer(s) generator has can now be released as well
_releaseBuffers();
}
@Override
protected void _releaseBuffers()
{
char[] buf = _outputBuffer;
if (buf != null) {
_outputBuffer = null;
_ioContext.releaseConcatBuffer(buf);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing; text, default
/**********************************************************
*/
private void _writeString(String text)
throws IOException, JsonGenerationException
{
/* One check first: if String won't fit in the buffer, let's
* segment writes. No point in extending buffer to huge sizes
* (like if someone wants to include multi-megabyte base64
* encoded stuff or such)
*/
final int len = text.length();
if (len > _outputEnd) { // Let's reserve space for entity at begin/end
_writeLongString(text);
return;
}
// Ok: we know String will fit in buffer ok
// But do we need to flush first?
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
}
text.getChars(0, len, _outputBuffer, _outputTail);
if (_characterEscapes != null) {
_writeStringCustom(len);
} else if (_maximumNonEscapedChar != 0) {
_writeStringASCII(len, _maximumNonEscapedChar);
} else {
_writeString2(len);
}
}
private void _writeString2(final int len)
throws IOException, JsonGenerationException
{
// And then we'll need to verify need for escaping etc:
int end = _outputTail + len;
final int[] escCodes = _outputEscapes;
final int escLen = escCodes.length;
output_loop:
while (_outputTail < end) {
// Fast loop for chars not needing escaping
escape_loop:
while (true) {
char c = _outputBuffer[_outputTail];
if (c < escLen && escCodes[c] != 0) {
break escape_loop;
}
if (++_outputTail >= end) {
break output_loop;
}
}
// Ok, bumped into something that needs escaping.
/* First things first: need to flush the buffer.
* Inlined, as we don't want to lose tail pointer
*/
int flushLen = (_outputTail - _outputHead);
if (flushLen > 0) {
_writer.write(_outputBuffer, _outputHead, flushLen);
}
/* In any case, tail will be the new start, so hopefully
* we have room now.
*/
char c = _outputBuffer[_outputTail++];
_prependOrWriteCharacterEscape(c, escCodes[c]);
}
}
/**
* Method called to write "long strings", strings whose length exceeds
* output buffer length.
*/
private void _writeLongString(String text)
throws IOException, JsonGenerationException
{
// First things first: let's flush the buffer to get some more room
_flushBuffer();
// Then we can write
final int textLen = text.length();
int offset = 0;
do {
int max = _outputEnd;
int segmentLen = ((offset + max) > textLen)
? (textLen - offset) : max;
text.getChars(offset, offset+segmentLen, _outputBuffer, 0);
if (_characterEscapes != null) {
_writeSegmentCustom(segmentLen);
} else if (_maximumNonEscapedChar != 0) {
_writeSegmentASCII(segmentLen, _maximumNonEscapedChar);
} else {
_writeSegment(segmentLen);
}
offset += segmentLen;
} while (offset < textLen);
}
/**
* Method called to output textual context which has been copied
* to the output buffer prior to call. If any escaping is needed,
* it will also be handled by the method.
*
* Note: when called, textual content to write is within output
* buffer, right after buffered content (if any). That's why only
* length of that text is passed, as buffer and offset are implied.
*/
private final void _writeSegment(int end)
throws IOException, JsonGenerationException
{
final int[] escCodes = _outputEscapes;
final int escLen = escCodes.length;
int ptr = 0;
int start = ptr;
output_loop:
while (ptr < end) {
// Fast loop for chars not needing escaping
char c;
while (true) {
c = _outputBuffer[ptr];
if (c < escLen && escCodes[c] != 0) {
break;
}
if (++ptr >= end) {
break;
}
}
// Ok, bumped into something that needs escaping.
/* First things first: need to flush the buffer.
* Inlined, as we don't want to lose tail pointer
*/
int flushLen = (ptr - start);
if (flushLen > 0) {
_writer.write(_outputBuffer, start, flushLen);
if (ptr >= end) {
break output_loop;
}
}
++ptr;
// So; either try to prepend (most likely), or write directly:
start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]);
}
}
/**
* This method called when the string content is already in
* a char buffer, and need not be copied for processing.
*/
private final void _writeString(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
if (_characterEscapes != null) {
_writeStringCustom(text, offset, len);
return;
}
if (_maximumNonEscapedChar != 0) {
_writeStringASCII(text, offset, len, _maximumNonEscapedChar);
return;
}
/* Let's just find longest spans of non-escapable
* content, and for each see if it makes sense
* to copy them, or write through
*/
len += offset; // -> len marks the end from now on
final int[] escCodes = _outputEscapes;
final int escLen = escCodes.length;
while (offset < len) {
int start = offset;
while (true) {
char c = text[offset];
if (c < escLen && escCodes[c] != 0) {
break;
}
if (++offset >= len) {
break;
}
}
// Short span? Better just copy it to buffer first:
int newAmount = offset - start;
if (newAmount < SHORT_WRITE) {
// Note: let's reserve room for escaped char (up to 6 chars)
if ((_outputTail + newAmount) > _outputEnd) {
_flushBuffer();
}
if (newAmount > 0) {
System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
_outputTail += newAmount;
}
} else { // Nope: better just write through
_flushBuffer();
_writer.write(text, start, newAmount);
}
// Was this the end?
if (offset >= len) { // yup
break;
}
// Nope, need to escape the char.
char c = text[offset++];
_appendCharacterEscape(c, escCodes[c]);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with additional escaping (ASCII or such)
/* (since 1.8; see [JACKSON-102])
/**********************************************************
*/
/* 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, _maximumNonEscapedChar+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 final void _writeSegmentASCII(int end, final int maxNonEscaped)
throws IOException, JsonGenerationException
{
final int[] escCodes = _outputEscapes;
final int escLimit = Math.min(escCodes.length, _maximumNonEscapedChar+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 final 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)
/* (since 1.8; see [JACKSON-106])
/**********************************************************
*/
/* 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 final 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, _maximumNonEscapedChar+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 final 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);
}
}
/*
/**********************************************************
/* 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 final 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 final 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 final void _appendCharacterEscape(char ch, int escCode)
throws IOException, JsonGenerationException
{
if (escCode >= 0) { // \\N (2 char)
if ((_outputTail + 2) > _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = (char) escCode;
return;
}
if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
if ((_outputTail + 2) > _outputEnd) {
_flushBuffer();
}
int ptr = _outputTail;
char[] buf = _outputBuffer;
buf[ptr++] = '\\';
buf[ptr++] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
if (ch > 0xFF) { // beyond 8 bytes
int hi = (ch >> 8) & 0xFF;
buf[ptr++] = HEX_CHARS[hi >> 4];
buf[ptr++] = HEX_CHARS[hi & 0xF];
ch &= 0xFF;
} else {
buf[ptr++] = '0';
buf[ptr++] = '0';
}
buf[ptr++] = HEX_CHARS[ch >> 4];
buf[ptr] = HEX_CHARS[ch & 0xF];
_outputTail = ptr;
return;
}
String escape;
if (_currentEscape == null) {
escape = _characterEscapes.getEscapeSequence(ch).getValue();
} else {
escape = _currentEscape.getValue();
_currentEscape = null;
}
int len = escape.length();
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible
_writer.write(escape);
return;
}
}
escape.getChars(0, len, _outputBuffer, _outputTail);
_outputTail += len;
}
private char[] _allocateEntityBuffer()
{
char[] buf = new char[14];
// first 2 chars, non-numeric escapes (like \n)
buf[0] = '\\';
// next 6; 8-bit escapes (control chars mostly)
buf[2] = '\\';
buf[3] = 'u';
buf[4] = '0';
buf[5] = '0';
// last 6, beyond 8 bits
buf[8] = '\\';
buf[9] = 'u';
_entityBuffer = buf;
return buf;
}
protected final void _flushBuffer() throws IOException
{
int len = _outputTail - _outputHead;
if (len > 0) {
int offset = _outputHead;
_outputTail = _outputHead = 0;
_writer.write(_outputBuffer, offset, len);
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/ReaderBasedParser.java 0000644 0001750 0001750 00000172624 11655120726 027277 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import java.io.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.sym.CharsToNameCanonicalizer;
import org.codehaus.jackson.util.*;
/**
* This is a concrete implementation of {@link JsonParser}, which is
* based on a {@link java.io.Reader} to handle low-level character
* conversion tasks.
*/
public final class ReaderBasedParser
extends JsonParserBase
{
/*
/**********************************************************
/* Input configuration
/**********************************************************
*/
/**
* Reader that can be used for reading more content, if one
* buffer from input source, but in some cases pre-loaded buffer
* is handed to the parser.
*/
protected Reader _reader;
/**
* Current buffer from which data is read; generally data is read into
* buffer from input source.
*/
protected char[] _inputBuffer;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
protected ObjectCodec _objectCodec;
final protected CharsToNameCanonicalizer _symbols;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Flag that indicates that the current token has not yet
* been fully processed, and needs to be finished for
* some access (or skipped to obtain the next token)
*/
protected boolean _tokenIncomplete = false;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public ReaderBasedParser(IOContext ctxt, int features, Reader r,
ObjectCodec codec, CharsToNameCanonicalizer st)
{
super(ctxt, features);
_reader = r;
_inputBuffer = ctxt.allocTokenBuffer();
_objectCodec = codec;
_symbols = st;
}
/*
/**********************************************************
/* 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 final boolean loadMore() throws IOException
{
_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;
if (_reader != null) {
int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd);
}
}
return false;
}
protected char getNextChar(String eofMsg)
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(eofMsg);
}
}
return _inputBuffer[_inputPtr++];
}
@Override
protected void _closeInput() throws IOException
{
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside is that when using our optimized
* Reader (granted, we only do that for UTF-32...) this
* means that buffer recycling won't work correctly.
*/
if (_reader != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_reader.close();
}
_reader = null;
}
}
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers()
throws IOException
{
super._releaseBuffers();
char[] buf = _inputBuffer;
if (buf != null) {
_inputBuffer = null;
_ioContext.releaseTokenBuffer(buf);
}
}
/*
/**********************************************************
/* Public API, data access
/**********************************************************
*/
/**
* Method for accessing textual representation of the current event;
* if no current event (before first call to {@link #nextToken}, or
* after encountering end-of-input), returns null.
* Method can be called for any event.
*/
@Override
public final String getText()
throws IOException, JsonParseException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
return _getText2(t);
}
protected final String _getText2(JsonToken t)
{
if (t == null) {
return null;
}
switch (t) {
case FIELD_NAME:
return _parsingContext.getCurrentName();
case VALUE_STRING:
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.contentsAsString();
}
return t.asString();
}
@Override
public char[] getTextCharacters()
throws IOException, JsonParseException
{
if (_currToken != null) { // null only before/after document
switch (_currToken) {
case FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
case VALUE_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.getTextBuffer();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public int getTextLength()
throws IOException, JsonParseException
{
if (_currToken != null) { // null only before/after document
switch (_currToken) {
case FIELD_NAME:
return _parsingContext.getCurrentName().length();
case VALUE_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.size();
default:
return _currToken.asCharArray().length;
}
}
return 0;
}
@Override
public int getTextOffset() throws IOException, JsonParseException
{
// Most have offset of 0, only some may have other values:
if (_currToken != null) {
switch (_currToken) {
case FIELD_NAME:
return 0;
case VALUE_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return _textBuffer.getTextOffset();
}
}
return 0;
}
@Override
public byte[] getBinaryValue(Base64Variant b64variant)
throws IOException, JsonParseException
{
if (_currToken != JsonToken.VALUE_STRING &&
(_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
}
/* To ensure that we won't see inconsistent data, better clear up
* state...
*/
if (_tokenIncomplete) {
try {
_binaryValue = _decodeBase64(b64variant);
} catch (IllegalArgumentException iae) {
throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
}
/* let's clear incomplete only now; allows for accessing other
* textual content in error cases
*/
_tokenIncomplete = false;
}
return _binaryValue;
}
/*
/**********************************************************
/* Public API, traversal
/**********************************************************
*/
/**
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
@Override
public JsonToken nextToken()
throws IOException, JsonParseException
{
_numTypesValid = NR_UNKNOWN;
/* First: field names are special -- we will always tokenize
* (part of) value along with field name to simplify
* state handling. If so, can and need to use secondary token:
*/
if (_currToken == JsonToken.FIELD_NAME) {
return _nextAfterName();
}
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
/* 19-Feb-2009, tatu: Should actually close/release things
* like input source, symbol table and recyclable buffers now.
*/
close();
return (_currToken = null);
}
/* First, need to ensure we know the starting location of token
* after skipping leading white space
*/
_tokenInputTotal = _currInputProcessed + _inputPtr - 1;
_tokenInputRow = _currInputRow;
_tokenInputCol = _inputPtr - _currInputRowStart - 1;
// finally: clear any data retained so far
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET) {
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(i, '}');
}
_parsingContext = _parsingContext.getParent();
return (_currToken = JsonToken.END_ARRAY);
}
if (i == INT_RCURLY) {
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker(i, ']');
}
_parsingContext = _parsingContext.getParent();
return (_currToken = JsonToken.END_OBJECT);
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
}
i = _skipWS();
}
/* And should we now have a name? Always true for
* Object contexts, since the intermediate 'expect-value'
* state is never retained.
*/
boolean inObject = _parsingContext.inObject();
if (inObject) {
// First, field name itself:
String name = _parseFieldName(i);
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
i = _skipWS();
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
i = _skipWS();
}
// Ok: we must have a value... what is it?
JsonToken t;
switch (i) {
case INT_QUOTE:
_tokenIncomplete = true;
t = JsonToken.VALUE_STRING;
break;
case INT_LBRACKET:
if (!inObject) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
}
t = JsonToken.START_ARRAY;
break;
case INT_LCURLY:
if (!inObject) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
t = JsonToken.START_OBJECT;
break;
case INT_RBRACKET:
case INT_RCURLY:
// Error: neither is valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(i, "expected a value");
case INT_t:
_matchToken("true", 1);
t = JsonToken.VALUE_TRUE;
break;
case INT_f:
_matchToken("false", 1);
t = JsonToken.VALUE_FALSE;
break;
case INT_n:
_matchToken("null", 1);
t = JsonToken.VALUE_NULL;
break;
case INT_MINUS:
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicate by a more specific error message.
*/
case INT_0:
case INT_1:
case INT_2:
case INT_3:
case INT_4:
case INT_5:
case INT_6:
case INT_7:
case INT_8:
case INT_9:
t = parseNumberText(i);
break;
default:
t = _handleUnexpectedValue(i);
break;
}
if (inObject) {
_nextToken = t;
return _currToken;
}
_currToken = t;
return t;
}
private final JsonToken _nextAfterName()
{
_nameCopied = false; // need to invalidate if it was copied
JsonToken t = _nextToken;
_nextToken = null;
// Also: may need to start new context?
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return (_currToken = t);
}
/*
@Override
public boolean nextFieldName(SerializableString str)
throws IOException, JsonParseException
*/
// note: identical to one in Utf8StreamParser
@Override
public String nextTextValue()
throws IOException, JsonParseException
{
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString();
}
return _textBuffer.contentsAsString();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
// note: identical to one in Utf8StreamParser
@Override
public int nextIntValue(int defaultValue)
throws IOException, JsonParseException
{
if (_currToken == JsonToken.FIELD_NAME) {
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getIntValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
// note: identical to one in Utf8StreamParser
@Override
public long nextLongValue(long defaultValue)
throws IOException, JsonParseException
{
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getLongValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
// note: identical to one in Utf8StreamParser
@Override
public Boolean nextBooleanValue()
throws IOException, JsonParseException
{
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
switch (nextToken()) {
case VALUE_TRUE:
return Boolean.TRUE;
case VALUE_FALSE:
return Boolean.FALSE;
}
return null;
}
@Override
public void close() throws IOException
{
super.close();
_symbols.release();
}
/*
/**********************************************************
/* Internal methods, number parsing
/* (note: in 1.8 and prior, part of "ReaderBasedNumericParser"
/**********************************************************
*/
/**
* Initial parsing method for number values. It needs to be able
* to parse enough input to be able to determine whether the
* value is to be considered a simple integer value, or a more
* generic decimal value: latter of which needs to be expressed
* as a floating point number. The basic rule is that if the number
* has no fractional or exponential part, it is an integer; otherwise
* a floating point number.
*
* Because much of input has to be processed in any case, no partial
* parsing is done: all input text will be stored for further
* processing. However, actual numeric value conversion will be
* deferred, since it is usually the most complicated and costliest
* part of processing.
*/
protected final JsonToken parseNumberText(int ch)
throws IOException, JsonParseException
{
/* Although we will always be complete with respect to textual
* representation (that is, all characters will be parsed),
* actual conversion to a number is deferred. Thus, need to
* note that no representations are valid yet
*/
boolean negative = (ch == INT_MINUS);
int ptr = _inputPtr;
int startPtr = ptr-1; // to include sign/digit already read
final int inputLen = _inputEnd;
dummy_loop:
do { // dummy loop, to be able to break out
if (negative) { // need to read the next digit
if (ptr >= _inputEnd) {
break dummy_loop;
}
ch = _inputBuffer[ptr++];
// First check: must have a digit to follow minus sign
if (ch > INT_9 || ch < INT_0) {
_inputPtr = ptr;
return _handleInvalidNumberStart(ch, true);
}
/* (note: has been checked for non-negative already, in
* the dispatching code that determined it should be
* a numeric value)
*/
}
// One special case, leading zero(es):
if (ch == INT_0) {
break dummy_loop;
}
/* First, let's see if the whole number is contained within
* the input buffer unsplit. This should be the common case;
* and to simplify processing, we will just reparse contents
* in the alternative case (number split on buffer boundary)
*/
int intLen = 1; // already got one
// First let's get the obligatory integer part:
int_loop:
while (true) {
if (ptr >= _inputEnd) {
break dummy_loop;
}
ch = (int) _inputBuffer[ptr++];
if (ch < INT_0 || ch > INT_9) {
break int_loop;
}
++intLen;
}
int fractLen = 0;
// And then see if we get other parts
if (ch == INT_DECIMAL_POINT) { // yes, fraction
fract_loop:
while (true) {
if (ptr >= inputLen) {
break dummy_loop;
}
ch = (int) _inputBuffer[ptr++];
if (ch < INT_0 || ch > INT_9) {
break fract_loop;
}
++fractLen;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (ch == INT_e || ch == INT_E) { // and/or exponent
if (ptr >= inputLen) {
break dummy_loop;
}
// Sign indicator?
ch = (int) _inputBuffer[ptr++];
if (ch == INT_MINUS || ch == INT_PLUS) { // yup, skip for now
if (ptr >= inputLen) {
break dummy_loop;
}
ch = (int) _inputBuffer[ptr++];
}
while (ch <= INT_9 && ch >= INT_0) {
++expLen;
if (ptr >= inputLen) {
break dummy_loop;
}
ch = (int) _inputBuffer[ptr++];
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit");
}
}
// Got it all: let's add to text buffer for parsing, access
--ptr; // need to push back following separator
_inputPtr = ptr;
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
return reset(negative, intLen, fractLen, expLen);
} while (false);
_inputPtr = negative ? (startPtr+1) : startPtr;
return parseNumberText2(negative);
}
/**
* Method called to parse a number, when the primary parse
* method has failed to parse it, due to it being split on
* buffer boundary. As a result code is very similar, except
* that it has to explicitly copy contents to the text buffer
* instead of just sharing the main input buffer.
*/
private final JsonToken parseNumberText2(boolean negative)
throws IOException, JsonParseException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
// Need to prepend sign?
if (negative) {
outBuf[outPtr++] = '-';
}
// This is the place to do leading-zero check(s) too:
int intLen = 0;
char c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++] : getNextChar("No digit following minus sign");
if (c == '0') {
c = _verifyNoLeadingZeroes();
}
boolean eof = false;
// Ok, first the obligatory integer part:
int_loop:
while (c >= '0' && c <= '9') {
++intLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
if (_inputPtr >= _inputEnd && !loadMore()) {
// EOF is legal for main level int values
c = CHAR_NULL;
eof = true;
break int_loop;
}
c = _inputBuffer[_inputPtr++];
}
// Also, integer part is not optional
if (intLen == 0) {
reportInvalidNumber("Missing integer part (next char "+_getCharDesc(c)+")");
}
int fractLen = 0;
// And then see if we get other parts
if (c == '.') { // yes, fraction
outBuf[outPtr++] = c;
fract_loop:
while (true) {
if (_inputPtr >= _inputEnd && !loadMore()) {
eof = true;
break fract_loop;
}
c = _inputBuffer[_inputPtr++];
if (c < INT_0 || c > INT_9) {
break fract_loop;
}
++fractLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (c == 'e' || c == 'E') { // exponent?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
// Not optional, can require that we get one more char
c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
: getNextChar("expected a digit for number exponent");
// Sign indicator?
if (c == '-' || c == '+') {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
// Likewise, non optional:
c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
: getNextChar("expected a digit for number exponent");
}
exp_loop:
while (c <= INT_9 && c >= INT_0) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
if (_inputPtr >= _inputEnd && !loadMore()) {
eof = true;
break exp_loop;
}
c = _inputBuffer[_inputPtr++];
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
}
}
// Ok; unless we hit end-of-input, need to push last char read back
if (!eof) {
--_inputPtr;
}
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return reset(negative, intLen, fractLen, expLen);
}
/**
* Method called when we have seen one zero, and want to ensure
* it is not followed by another
*/
private final char _verifyNoLeadingZeroes()
throws IOException, JsonParseException
{
// Ok to have plain "0"
if (_inputPtr >= _inputEnd && !loadMore()) {
return '0';
}
char ch = _inputBuffer[_inputPtr];
// if not followed by a number (probably '.'); return zero as is, to be included
if (ch < '0' || ch > '9') {
return '0';
}
if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) {
reportInvalidNumber("Leading zeroes not allowed");
}
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
++_inputPtr; // Leading zero to be skipped
if (ch == INT_0) {
while (_inputPtr < _inputEnd || loadMore()) {
ch = _inputBuffer[_inputPtr];
if (ch < '0' || ch > '9') { // followed by non-number; retain one zero
return '0';
}
++_inputPtr; // skip previous zero
if (ch != '0') { // followed by other number; return
break;
}
}
}
return ch;
}
/**
* Method called if expected numeric value (due to leading sign) does not
* look like a number
*/
protected JsonToken _handleInvalidNumberStart(int ch, boolean negative)
throws IOException, JsonParseException
{
if (ch == 'I') {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOFInValue();
}
}
ch = _inputBuffer[_inputPtr++];
if (ch == 'N') {
String match = negative ? "-INF" :"+INF";
_matchToken(match, 3);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
} else if (ch == 'n') {
String match = negative ? "-Infinity" :"+Infinity";
_matchToken(match, 3);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
}
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
return null;
}
/*
/**********************************************************
/* Internal methods, secondary parsing
/**********************************************************
*/
protected final String _parseFieldName(int i)
throws IOException, JsonParseException
{
if (i != INT_QUOTE) {
return _handleUnusualFieldName(i);
}
/* First: let's try to see if we have a simple name: one that does
* not cross input buffer boundary, and does not contain escape
* sequences.
*/
int ptr = _inputPtr;
int hash = 0;
final int inputLen = _inputEnd;
if (ptr < inputLen) {
final int[] codes = CharTypes.getInputCodeLatin1();
final int maxCode = codes.length;
do {
int ch = _inputBuffer[ptr];
if (ch < maxCode && codes[ch] != 0) {
if (ch == '"') {
int start = _inputPtr;
_inputPtr = ptr+1; // to skip the quote
return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
}
break;
}
hash = (hash * 31) + ch;
++ptr;
} while (ptr < inputLen);
}
int start = _inputPtr;
_inputPtr = ptr;
return _parseFieldName2(start, hash, INT_QUOTE);
}
private String _parseFieldName2(int startPtr, int hash, int endChar)
throws IOException, JsonParseException
{
_textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr));
/* Output pointers; calls will also ensure that the buffer is
* not shared and has room for at least one more char.
*/
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing '"+((char) endChar)+"' for name");
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i <= INT_BACKSLASH) {
if (i == INT_BACKSLASH) {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
c = _decodeEscaped();
} else if (i <= endChar) {
if (i == endChar) {
break;
}
if (i < INT_SPACE) {
_throwUnquotedSpace(i, "name");
}
}
}
hash = (hash * 31) + i;
// Ok, let's add char to output:
outBuf[outPtr++] = c;
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
}
_textBuffer.setCurrentLength(outPtr);
{
TextBuffer tb = _textBuffer;
char[] buf = tb.getTextBuffer();
int start = tb.getTextOffset();
int len = tb.size();
return _symbols.findSymbol(buf, start, len, hash);
}
}
/**
* Method called when we see non-white space character other
* than double quote, when expecting a field name.
* In standard mode will just throw an expection; but
* in non-standard modes may be able to parse name.
*
* @since 1.2
*/
protected final String _handleUnusualFieldName(int i)
throws IOException, JsonParseException
{
// [JACKSON-173]: allow single quotes
if (i == INT_APOSTROPHE && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
return _parseApostropheFieldName();
}
// [JACKSON-69]: allow unquoted names if feature enabled:
if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) {
_reportUnexpectedChar(i, "was expecting double-quote to start field name");
}
final int[] codes = CharTypes.getInputCodeLatin1JsNames();
final int maxCode = codes.length;
// Also: first char must be a valid name char, but NOT be number
boolean firstOk;
if (i < maxCode) { // identifier, and not a number
firstOk = (codes[i] == 0) && (i < INT_0 || i > INT_9);
} else {
firstOk = Character.isJavaIdentifierPart((char) i);
}
if (!firstOk) {
_reportUnexpectedChar(i, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
}
int ptr = _inputPtr;
int hash = 0;
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 * 31) + ch;
++ptr;
} while (ptr < inputLen);
}
int start = _inputPtr-1;
_inputPtr = ptr;
return _parseUnusualFieldName2(start, hash, codes);
}
protected final String _parseApostropheFieldName()
throws IOException, JsonParseException
{
// Note: mostly copy of_parseFieldName
int ptr = _inputPtr;
int hash = 0;
final int inputLen = _inputEnd;
if (ptr < inputLen) {
final int[] codes = CharTypes.getInputCodeLatin1();
final int maxCode = codes.length;
do {
int ch = _inputBuffer[ptr];
if (ch == '\'') {
int start = _inputPtr;
_inputPtr = ptr+1; // to skip the quote
return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
}
if (ch < maxCode && codes[ch] != 0) {
break;
}
hash = (hash * 31) + ch;
++ptr;
} while (ptr < inputLen);
}
int start = _inputPtr;
_inputPtr = ptr;
return _parseFieldName2(start, hash, INT_APOSTROPHE);
}
/**
* Method for handling cases where first non-space character
* of an expected value token is not legal for standard JSON content.
*
* @since 1.3
*/
protected final JsonToken _handleUnexpectedValue(int i)
throws IOException, JsonParseException
{
// Most likely an error, unless we are to allow single-quote-strings
switch (i) {
case '\'':
/* [JACKSON-173]: allow single quotes. Unlike with regular
* Strings, we'll eagerly parse contents; this so that there's
* no need to store information on quote char used.
*
* Also, no separation to fast/slow parsing; we'll just do
* one regular (~= slowish) parsing, to keep code simple
*/
if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
return _handleApostropheValue();
}
break;
case 'N':
_matchToken("NaN", 1);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return resetAsNaN("NaN", Double.NaN);
}
_reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case '+': // note: '-' is taken as number
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOFInValue();
}
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false);
}
_reportUnexpectedChar(i, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')");
return null;
}
/**
* @since 1.8
*/
protected final JsonToken _handleApostropheValue()
throws IOException, JsonParseException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value");
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i <= INT_BACKSLASH) {
if (i == INT_BACKSLASH) {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
c = _decodeEscaped();
} else if (i <= INT_APOSTROPHE) {
if (i == INT_APOSTROPHE) {
break;
}
if (i < INT_SPACE) {
_throwUnquotedSpace(i, "string value");
}
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = c;
}
_textBuffer.setCurrentLength(outPtr);
return JsonToken.VALUE_STRING;
}
/**
* @since 1.2
*/
private String _parseUnusualFieldName2(int startPtr, int hash, int[] codes)
throws IOException, JsonParseException
{
_textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr));
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
final int maxCode = codes.length;
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) { // acceptable for now (will error out later)
break;
}
}
char c = _inputBuffer[_inputPtr];
int i = (int) c;
if (i <= maxCode) {
if (codes[i] != 0) {
break;
}
} else if (!Character.isJavaIdentifierPart(c)) {
break;
}
++_inputPtr;
hash = (hash * 31) + i;
// Ok, let's add char to output:
outBuf[outPtr++] = c;
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
}
_textBuffer.setCurrentLength(outPtr);
{
TextBuffer tb = _textBuffer;
char[] buf = tb.getTextBuffer();
int start = tb.getTextOffset();
int len = tb.size();
return _symbols.findSymbol(buf, start, len, hash);
}
}
@Override
protected void _finishString()
throws IOException, JsonParseException
{
/* First: let's try to see if we have simple String value: one
* that does not cross input buffer boundary, and does not
* contain escape sequences.
*/
int ptr = _inputPtr;
final int inputLen = _inputEnd;
if (ptr < inputLen) {
final int[] codes = CharTypes.getInputCodeLatin1();
final int maxCode = codes.length;
do {
int ch = _inputBuffer[ptr];
if (ch < maxCode && codes[ch] != 0) {
if (ch == '"') {
_textBuffer.resetWithShared(_inputBuffer, _inputPtr, (ptr-_inputPtr));
_inputPtr = ptr+1;
// Yes, we got it all
return;
}
break;
}
++ptr;
} while (ptr < inputLen);
}
/* Either ran out of input, or bumped into an escape
* sequence...
*/
_textBuffer.resetWithCopy(_inputBuffer, _inputPtr, (ptr-_inputPtr));
_inputPtr = ptr;
_finishString2();
}
protected void _finishString2()
throws IOException, JsonParseException
{
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value");
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i <= INT_BACKSLASH) {
if (i == INT_BACKSLASH) {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
c = _decodeEscaped();
} else if (i <= INT_QUOTE) {
if (i == INT_QUOTE) {
break;
}
if (i < INT_SPACE) {
_throwUnquotedSpace(i, "string value");
}
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = c;
}
_textBuffer.setCurrentLength(outPtr);
}
/**
* Method called to skim through rest of unparsed String value,
* if it is not needed. This can be done bit faster if contents
* need not be stored for future access.
*/
protected void _skipString()
throws IOException, JsonParseException
{
_tokenIncomplete = false;
int inputPtr = _inputPtr;
int inputLen = _inputEnd;
char[] inputBuffer = _inputBuffer;
while (true) {
if (inputPtr >= inputLen) {
_inputPtr = inputPtr;
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value");
}
inputPtr = _inputPtr;
inputLen = _inputEnd;
}
char c = inputBuffer[inputPtr++];
int i = (int) c;
if (i <= INT_BACKSLASH) {
if (i == INT_BACKSLASH) {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
_inputPtr = inputPtr;
c = _decodeEscaped();
inputPtr = _inputPtr;
inputLen = _inputEnd;
} else if (i <= INT_QUOTE) {
if (i == INT_QUOTE) {
_inputPtr = inputPtr;
break;
}
if (i < INT_SPACE) {
_inputPtr = inputPtr;
_throwUnquotedSpace(i, "string value");
}
}
}
}
}
/*
/**********************************************************
/* Internal methods, other parsing
/**********************************************************
*/
/**
* We actually need to check the character value here
* (to see if we have \n following \r).
*/
protected final void _skipCR() throws IOException
{
if (_inputPtr < _inputEnd || loadMore()) {
if (_inputBuffer[_inputPtr] == '\n') {
++_inputPtr;
}
}
++_currInputRow;
_currInputRowStart = _inputPtr;
}
protected final void _skipLF() throws IOException
{
++_currInputRow;
_currInputRowStart = _inputPtr;
}
private final int _skipWS()
throws IOException, JsonParseException
{
while (_inputPtr < _inputEnd || loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i != INT_SLASH) {
return i;
}
_skipComment();
} else if (i != INT_SPACE) {
if (i == INT_LF) {
_skipLF();
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
}
private final int _skipWSOrEnd()
throws IOException, JsonParseException
{
while ((_inputPtr < _inputEnd) || loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
_skipLF();
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
// We ran out of input...
_handleEOF();
return -1;
}
private final void _skipComment()
throws IOException, JsonParseException
{
if (!isEnabled(Feature.ALLOW_COMMENTS)) {
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// First: check which comment (if either) it is:
if (_inputPtr >= _inputEnd && !loadMore()) {
_reportInvalidEOF(" in a comment");
}
char c = _inputBuffer[_inputPtr++];
if (c == '/') {
_skipCppComment();
} else if (c == '*') {
_skipCComment();
} else {
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
}
}
private final void _skipCComment()
throws IOException, JsonParseException
{
// Ok: need the matching '*/'
main_loop:
while ((_inputPtr < _inputEnd) || loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i <= INT_ASTERISK) {
if (i == INT_ASTERISK) { // end?
if ((_inputPtr >= _inputEnd) && !loadMore()) {
break main_loop;
}
if (_inputBuffer[_inputPtr] == INT_SLASH) {
++_inputPtr;
return;
}
continue;
}
if (i < INT_SPACE) {
if (i == INT_LF) {
_skipLF();
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
}
_reportInvalidEOF(" in a comment");
}
private final void _skipCppComment()
throws IOException, JsonParseException
{
// Ok: need to find EOF or linefeed
while ((_inputPtr < _inputEnd) || loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i < INT_SPACE) {
if (i == INT_LF) {
_skipLF();
break;
} else if (i == INT_CR) {
_skipCR();
break;
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
}
@Override
protected final char _decodeEscaped()
throws IOException, JsonParseException
{
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in character escape sequence");
}
}
char c = _inputBuffer[_inputPtr++];
switch ((int) c) {
// First, ones that are mapped
case INT_b:
return '\b';
case INT_t:
return '\t';
case INT_n:
return '\n';
case INT_f:
return '\f';
case INT_r:
return '\r';
// And these are to be returned as they are
case INT_QUOTE:
case INT_SLASH:
case INT_BACKSLASH:
return c;
case INT_u: // and finally hex-escaped
break;
default:
return _handleUnrecognizedCharacterEscape(c);
}
// Ok, a hex escape. Need 4 characters
int value = 0;
for (int i = 0; i < 4; ++i) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(" in character escape sequence");
}
}
int ch = (int) _inputBuffer[_inputPtr++];
int digit = CharTypes.charToHex(ch);
if (digit < 0) {
_reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
}
value = (value << 4) | digit;
}
return (char) value;
}
/**
* Helper method for checking whether input matches expected token
*
* @since 1.8
*/
protected final void _matchToken(String matchStr, int i)
throws IOException, JsonParseException
{
final int len = matchStr.length();
do {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOFInValue();
}
}
if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
_reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
}
++_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)) {
++_inputPtr;
_reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
}
return;
}
/*
/**********************************************************
/* Binary access
/**********************************************************
*/
@Override
protected byte[] _decodeBase64(Base64Variant b64variant)
throws IOException, JsonParseException
{
ByteArrayBuilder builder = _getByteArrayBuilder();
//main_loop:
while (true) {
// first, we'll skip preceding white space, if any
char ch;
do {
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (ch == '"') { // reached the end, fair and square?
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"' && !b64variant.usesPadding()) {
decodedData >>= 4;
builder.append(decodedData);
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get more padding chars, then
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
if (!b64variant.usesPaddingChar(ch)) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
builder.append(decodedData);
continue;
}
// otherwise we got escaped other char, to be processed below
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"' && !b64variant.usesPadding()) {
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
/* With padding we only get 2 bytes; but we have
* to shift it a bit so it is identical to triplet
* case with partial output.
* 3 chars gives 3x6 == 18 bits, of which 2 are
* dummies, need to discard:
*/
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
continue;
}
// otherwise we got escaped other char, to be processed below
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
builder.appendThreeBytes(decodedData);
}
}
/*
/**********************************************************
/* Error reporting
/**********************************************************
*/
protected void _reportInvalidToken(String matchedPart, String msg)
throws IOException, JsonParseException
{
StringBuilder sb = new StringBuilder(matchedPart);
/* Let's just try to find what appears to be the token, using
* regular Java identifier character rules. It's just a heuristic,
* nothing fancy here.
*/
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
break;
}
}
char c = _inputBuffer[_inputPtr];
if (!Character.isJavaIdentifierPart(c)) {
break;
}
++_inputPtr;
sb.append(c);
}
_reportError("Unrecognized token '"+sb.toString()+"': was expecting ");
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/impl/JsonReadContext.java 0000644 0001750 0001750 00000012006 11655120726 027016 0 ustar jamespage jamespage package org.codehaus.jackson.impl;
import org.codehaus.jackson.*;
import org.codehaus.jackson.util.CharTypes;
/**
* Extension of {@link JsonStreamContext}, which implements
* core methods needed, and also exposes
* more complete API to parser implementation classes.
*/
public final class JsonReadContext
extends JsonStreamContext
{
// // // Configuration
protected final JsonReadContext _parent;
// // // Location information (minus source reference)
protected int _lineNr;
protected int _columnNr;
protected String _currentName;
/*
/**********************************************************
/* Simple instance reuse slots; speeds up things
/* a bit (10-15%) for docs with lots of small
/* arrays/objects (for which allocation was
/* visible in profile stack frames)
/**********************************************************
*/
protected JsonReadContext _child = null;
/*
/**********************************************************
/* Instance construction, reuse
/**********************************************************
*/
public JsonReadContext(JsonReadContext parent, int type, int lineNr, int colNr)
{
super();
_type = type;
_parent = parent;
_lineNr = lineNr;
_columnNr = colNr;
_index = -1;
}
protected final void reset(int type, int lineNr, int colNr)
{
_type = type;
_index = -1;
_lineNr = lineNr;
_columnNr = colNr;
_currentName = null;
}
// // // Factory methods
public static JsonReadContext createRootContext(int lineNr, int colNr)
{
return new JsonReadContext(null, TYPE_ROOT, lineNr, colNr);
}
/**
* @since 1.9
*/
public static JsonReadContext createRootContext()
{
return new JsonReadContext(null, TYPE_ROOT, 1, 0);
}
public final JsonReadContext createChildArrayContext(int lineNr, int colNr)
{
JsonReadContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonReadContext(this, TYPE_ARRAY, lineNr, colNr);
return ctxt;
}
ctxt.reset(TYPE_ARRAY, lineNr, colNr);
return ctxt;
}
public final JsonReadContext createChildObjectContext(int lineNr, int colNr)
{
JsonReadContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonReadContext(this, TYPE_OBJECT, lineNr, colNr);
return ctxt;
}
ctxt.reset(TYPE_OBJECT, lineNr, colNr);
return ctxt;
}
/*
/**********************************************************
/* Abstract method implementation
/**********************************************************
*/
@Override
public final String getCurrentName() { return _currentName; }
@Override
public final JsonReadContext getParent() { return _parent; }
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
/**
* @return Location pointing to the point where the context
* start marker was found
*/
public final JsonLocation getStartLocation(Object srcRef)
{
/* We don't keep track of offsets at this level (only
* reader does)
*/
long totalChars = -1L;
return new JsonLocation(srcRef, totalChars, _lineNr, _columnNr);
}
/*
/**********************************************************
/* State changes
/**********************************************************
*/
public final boolean expectComma()
{
/* Assumption here is that we will be getting a value (at least
* before calling this method again), and
* so will auto-increment index to avoid having to do another call
*/
int ix = ++_index; // starts from -1
return (_type != TYPE_ROOT && ix > 0);
}
public void setCurrentName(String name)
{
_currentName = name;
}
/*
/**********************************************************
/* Overridden standard methods
/**********************************************************
*/
/**
* Overridden to provide developer readable "JsonPath" representation
* of the context.
*/
@Override
public final String toString()
{
StringBuilder sb = new StringBuilder(64);
switch (_type) {
case TYPE_ROOT:
sb.append("/");
break;
case TYPE_ARRAY:
sb.append('[');
sb.append(getCurrentIndex());
sb.append(']');
break;
case TYPE_OBJECT:
sb.append('{');
if (_currentName != null) {
sb.append('"');
CharTypes.appendQuoted(sb, _currentName);
sb.append('"');
} else {
sb.append('?');
}
sb.append('}');
break;
}
return sb.toString();
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonToken.java 0000644 0001750 0001750 00000011413 11655120726 024716 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* 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.
*
* @since 0.9.7
*/
NOT_AVAILABLE(null),
/**
* START_OBJECT is returned when encountering '{'
* which signals starting of an Object value.
*/
START_OBJECT("{"),
/**
* START_OBJECT is returned when encountering '}'
* which signals ending of an Object value
*/
END_OBJECT("}"),
/**
* START_OBJECT is returned when encountering '['
* which signals starting of an Array value
*/
START_ARRAY("["),
/**
* START_OBJECT is returned when encountering ']'
* which signals ending of an Array value
*/
END_ARRAY("]"),
/**
* FIELD_NAME is returned when a String token is encountered
* as a field name (same lexical value, different function)
*/
FIELD_NAME(null),
/**
* Placeholder token returned when the input source has a concept
* of embedded Object that are not accessible as usual structure
* (of starting with {@link #START_OBJECT}, having values, ending with
* {@link #END_OBJECT}), but as "raw" objects.
*
* Note: this token is never returned by regular JSON readers, but
* only by readers that expose other kinds of source (like
* {@link JsonNode}-based JSON trees, Maps, Lists and such).
*
* @since 1.1
*/
VALUE_EMBEDDED_OBJECT(null),
/**
* VALUE_STRING is returned when a String token is encountered
* in value context (array element, field value, or root-level
* stand-alone value)
*/
VALUE_STRING(null),
/**
* VALUE_NUMBER_INT is returned when an integer numeric token is
* encountered in value context: that is, a number that does
* not have floating point or exponent marker in it (consists
* only of an optional sign, followed by one or more digits)
*/
VALUE_NUMBER_INT(null),
/**
* VALUE_NUMBER_INT is returned when a numeric token other
* that is not an integer is encountered: that is, a number that does
* have floating point or exponent marker in it, in addition
* to one or more digits.
*/
VALUE_NUMBER_FLOAT(null),
/**
* VALUE_TRUE is returned when encountering literal "true" in
* value context
*/
VALUE_TRUE("true"),
/**
* VALUE_FALSE is returned when encountering literal "false" in
* value context
*/
VALUE_FALSE("false"),
/**
* VALUE_NULL is returned when encountering literal "null" in
* value context
*/
VALUE_NULL("null")
;
final String _serialized;
final char[] _serializedChars;
final byte[] _serializedBytes;
/**
* @param Textual representation for this token, if there is a
* single static representation; null otherwise
*/
JsonToken(String token)
{
if (token == null) {
_serialized = null;
_serializedChars = null;
_serializedBytes = null;
} else {
_serialized = token;
_serializedChars = token.toCharArray();
// It's all in ascii, can just case...
int len = _serializedChars.length;
_serializedBytes = new byte[len];
for (int i = 0; i < len; ++i) {
_serializedBytes[i] = (byte) _serializedChars[i];
}
}
}
public String asString() { return _serialized; }
public char[] asCharArray() { return _serializedChars; }
public byte[] asByteArray() { return _serializedBytes; }
public boolean isNumeric() {
return (this == VALUE_NUMBER_INT) || (this == VALUE_NUMBER_FLOAT);
}
/**
* Method that can be used to check whether this token represents
* a valid non-structured value. This means all tokens other than
* Object/Array start/end markers all field names.
*/
public boolean isScalarValue() {
// note: up to 1.5, VALUE_EMBEDDED_OBJECT was incorrectly considered non-scalar!
return ordinal() >= VALUE_EMBEDDED_OBJECT.ordinal();
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/format/ 0000755 0001750 0001750 00000000000 11672662540 023435 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/format/DataFormatDetector.java 0000644 0001750 0001750 00000014002 11655120726 030005 0 ustar jamespage jamespage package org.codehaus.jackson.format;
import java.io.*;
import java.util.*;
import org.codehaus.jackson.*;
/**
* 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.
*
* @since 1.7
*/
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));
}
/*
/**********************************************************
/* 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);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/format/package-info.java 0000644 0001750 0001750 00000000353 11655120726 026621 0 ustar jamespage jamespage /**
* Package that contains interfaces needed for dynamic, pluggable
* format (auto)detection; as well as basic utility classes for
* simple format detection functionality.
*
* @since 1.8
*/
package org.codehaus.jackson.format;
jackson-src-1.9.2/src/java/org/codehaus/jackson/format/DataFormatMatcher.java 0000644 0001750 0001750 00000007034 11655120726 027626 0 ustar jamespage jamespage package org.codehaus.jackson.format;
import java.io.*;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.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;
/**
* Number of bytes in {@link #_bufferedData} that were read.
*/
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 bufferedLength,
JsonFactory match, MatchStrength strength)
{
_originalStream = in;
_bufferedData = buffered;
_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.createJsonParser(_bufferedData, 0, _bufferedLength);
}
return _match.createJsonParser(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, 0, _bufferedLength);
}
return new MergedStream(null, _originalStream, _bufferedData, 0, _bufferedLength);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/format/InputAccessor.java 0000644 0001750 0001750 00000007156 11655120726 027067 0 ustar jamespage jamespage package org.codehaus.jackson.format;
import java.io.*;
import org.codehaus.jackson.JsonFactory;
/**
* Interface used to expose beginning of a data file to data format
* detection code.
*
* @since 1.8
*/
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.
*/
public boolean hasMoreBytes() throws IOException;
/**
* Returns next byte available, if any; if no more bytes are
* available, will throw {@link java.io.EOFException}.
*/
public byte nextByte() throws IOException;
/**
* Method that can be called to reset accessor to read from beginning
* of input.
*/
public void reset();
/*
/**********************************************************
/* Standard implementation
/**********************************************************
*/
/**
* Basic implementation that reads data from given
* {@link InputStream} and buffers it as necessary.
*/
public class Std implements InputAccessor
{
protected final InputStream _in;
protected final byte[] _buffer;
/**
* Number of bytes in {@link #_buffer} that are valid
* buffered content.
*/
protected int _bufferedAmount;
/**
* 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;
_bufferedAmount = 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:
_bufferedAmount = inputDocument.length;
}
@Override
public boolean hasMoreBytes() throws IOException
{
if (_ptr < _bufferedAmount) { // already got more
return true;
}
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;
}
_bufferedAmount += count;
return true;
}
@Override
public byte nextByte() throws IOException
{
// should we just try loading more automatically?
if (_ptr >- _bufferedAmount) {
if (!hasMoreBytes()) {
throw new EOFException("Could not read more than "+_ptr+" bytes (max buffer size: "+_buffer.length+")");
}
}
return _buffer[_ptr++];
}
@Override
public void reset() {
_ptr = 0;
}
/*
/**********************************************************
/* Extended API for DataFormatDetector/Matcher
/**********************************************************
*/
public DataFormatMatcher createMatcher(JsonFactory match, MatchStrength matchStrength)
{
return new DataFormatMatcher(_in, _buffer, _bufferedAmount, match, matchStrength);
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/format/MatchStrength.java 0000644 0001750 0001750 00000005042 11655120726 027050 0 ustar jamespage jamespage package org.codehaus.jackson.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.
*
* @since 1.8
*/
public enum MatchStrength
{
/**
* Value that indicates that given data can not be in given format.
*/
NO_MATCH,
/**
* Value that indicates that detector can not find out whether could
* be a match or not.
* This can occur for example for textual data formats t
* when there are so many leading spaces that detector can not
* find the first data byte (because detectors typically limit lookahead
* to some smallish value).
*/
INCONCLUSIVE,
/**
* Value that indicates that given data could be of specified format (i.e.
* it can not be ruled out). This can occur for example when seen data
* is both not in canonical formats (for example: JSON data should be a JSON Array or Object
* not a scalar value, as per JSON specification) and there are known use case
* where a format detected is actually used (plain JSON Strings are actually used, even
* though specification does not indicate that as valid usage: as such, seeing a leading
* double-quote could indicate a JSON String, which plausibly could indicate
* non-standard JSON usage).
*/
WEAK_MATCH,
/**
* Value that indicates that given data conforms to (one of) canonical form(s) of
* the data format.
*
* For example, when testing for XML data format,
* seeing a less-than character ("<") alone (with possible leading spaces)
* would be a strong indication that data could
* be in xml format (but see below for {@link #FULL_MATCH} description for more)
*/
SOLID_MATCH,
/**
* Value that indicates that given data contains a signature that is deemed
* specific enough to uniquely indicate data format used.
*
* For example, when testing for XML data format,
* seing "<xml" as the first data bytes ("XML declaration", as per XML specification)
* could give full confidence that data is indeed in XML format.
* Not all data formats have unique leading identifiers to allow full matches; for example,
* JSON only has heuristic matches and can have at most {@link #SOLID_MATCH}) match.
*/
FULL_MATCH
;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/SerializableString.java 0000644 0001750 0001750 00000003036 11655120726 026603 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* 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.
*
* @since 1.7 (1.6 introduced implementation, but interface extracted later)
*
* @see org.codehaus.jackson.io.SerializedString
*/
public interface SerializableString
{
/**
* Returns unquoted String that this object represents (and offers
* serialized forms for)
*/
public String getValue();
/**
* Returns length of the (unquoted) String as characters.
* Functionally equvalent to:
*
* getValue().length();
*
*/
public int charLength();
/**
* 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.
*/
public char[] asQuotedChars();
/**
* Returns UTF-8 encoded version of unquoted String.
* Functionally equivalent to (but more efficient than):
*
* getValue().getBytes("UTF-8");
*
*/
public byte[] asUnquotedUTF8();
/**
* Returns UTF-8 encoded version of JSON-quoted String.
* Functionally equivalent to (but more efficient than):
*
* new String(asQuotedChars()).getBytes("UTF-8");
*
*/
public byte[] asQuotedUTF8();
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/ 0000755 0001750 0001750 00000000000 11672662540 022554 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/io/InputDecorator.java 0000644 0001750 0001750 00000005557 11655120726 026371 0 ustar jamespage jamespage package org.codehaus.jackson.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.
*
* @since 1.8
*/
public abstract class InputDecorator
{
/**
* Method called by {@link org.codehaus.jackson.JsonFactory} instance when
* creating parser given an {@link InputStream}, when this decorator
* has been registered.
*
* @param ctxt IO context in use (provides access to declared encoding).
* NOTE: at this point context may not have all information initialized;
* specifically auto-detected encoding is only available once parsing starts,
* which may occur only after this method is called.
* @param in Original input source
*
* @return InputStream to use; either passed in argument, or something that
* calls it
*/
public abstract InputStream decorate(IOContext ctxt, InputStream in)
throws IOException;
/**
* Method called by {@link org.codehaus.jackson.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 org.codehaus.jackson.JsonFactory} instance when
* creating parser given an {@link Reader}, when this decorator
* has been registered.
*
* @param ctxt IO context in use (provides access to declared encoding)
* NOTE: at this point context may not have all information initialized;
* specifically auto-detected encoding is only available once parsing starts,
* which may occur only after this method is called.
* @param src Original input source
*
* @return Reader to use; either passed in argument, or something that
* calls it (for example, a {@link FilterReader})
*/
public abstract Reader decorate(IOContext ctxt, Reader src) throws IOException;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/BaseReader.java 0000644 0001750 0001750 00000005631 11655120726 025415 0 ustar jamespage jamespage
package org.codehaus.jackson.io;
import java.io.*;
/**
* Simple basic class for optimized readers in this package; implements
* "cookie-cutter" methods that are used by all actual implementations.
*/
abstract class BaseReader
extends Reader
{
/**
* JSON actually limits available Unicode range in the high end
* to the same as xml (to basically limit UTF-8 max byte sequence
* length to 4)
*/
final protected static int LAST_VALID_UNICODE_CHAR = 0x10FFFF;
final protected static char NULL_CHAR = (char) 0;
final protected static char NULL_BYTE = (byte) 0;
final protected IOContext _context;
protected InputStream _in;
protected byte[] _buffer;
protected int _ptr;
protected int _length;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected BaseReader(IOContext context,
InputStream in, byte[] buf, int ptr, int len)
{
_context = context;
_in = in;
_buffer = buf;
_ptr = ptr;
_length = len;
}
/*
/**********************************************************
/* Reader API
/**********************************************************
*/
@Override
public void close() throws IOException
{
InputStream in = _in;
if (in != null) {
_in = null;
freeBuffers();
in.close();
}
}
protected char[] _tmpBuf = null;
/**
* Although this method is implemented by the base class, AND it should
* never be called by main code, let's still implement it bit more
* efficiently just in case
*/
@Override
public int read() throws IOException
{
if (_tmpBuf == null) {
_tmpBuf = new char[1];
}
if (read(_tmpBuf, 0, 1) < 1) {
return -1;
}
return _tmpBuf[0];
}
/*
/**********************************************************
/* Internal/package methods:
/**********************************************************
*/
/**
* This method should be called along with (or instead of) normal
* close. After calling this method, no further reads should be tried.
* Method will try to recycle read buffers (if any).
*/
public final void freeBuffers()
{
byte[] buf = _buffer;
if (buf != null) {
_buffer = null;
_context.releaseReadIOBuffer(buf);
}
}
protected void reportBounds(char[] cbuf, int start, int len)
throws IOException
{
throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]");
}
protected void reportStrangeStream()
throws IOException
{
throw new IOException("Strange I/O stream, returned 0 bytes on read");
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/UTF32Reader.java 0000644 0001750 0001750 00000014725 11655120726 025352 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import java.io.*;
/**
* Since JDK does not come with UTF-32/UCS-4, let's implement a simple
* decoder to use.
*/
public final class UTF32Reader
extends BaseReader
{
final boolean mBigEndian;
/**
* 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.
*/
char mSurrogate = NULL_CHAR;
/**
* Total read character count; used for error reporting purposes
*/
int mCharCount = 0;
/**
* Total read byte count; used for error reporting purposes
*/
int mByteCount = 0;
/*
////////////////////////////////////////
// Life-cycle
////////////////////////////////////////
*/
public UTF32Reader(IOContext ctxt,
InputStream in, byte[] buf, int ptr, int len,
boolean isBigEndian)
{
super(ctxt, in, buf, ptr, len);
mBigEndian = isBigEndian;
}
/*
////////////////////////////////////////
// Public API
////////////////////////////////////////
*/
@Override
public int read(char[] cbuf, int start, int len)
throws IOException
{
// Already EOF?
if (_buffer == null) {
return -1;
}
if (len < 1) {
return len;
}
// Let's then ensure there's enough room...
if (start < 0 || (start+len) > cbuf.length) {
reportBounds(cbuf, start, len);
}
len += start;
int outPtr = start;
// Ok, first; do we have a surrogate from last round?
if (mSurrogate != NULL_CHAR) {
cbuf[outPtr++] = mSurrogate;
mSurrogate = NULL_CHAR;
// No need to load more, already got one char
} else {
/* Note: we'll try to avoid blocking as much as possible. As a
* result, we only need to get 4 bytes for a full char.
*/
int left = (_length - _ptr);
if (left < 4) {
if (!loadMore(left)) { // (legal) EOF?
return -1;
}
}
}
main_loop:
while (outPtr < len) {
int ptr = _ptr;
int ch;
if (mBigEndian) {
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
mSurrogate = (char) ch;
break main_loop;
}
}
cbuf[outPtr++] = (char) ch;
if (_ptr >= _length) {
break main_loop;
}
}
len = outPtr - start;
mCharCount += len;
return len;
}
/*
////////////////////////////////////////
// Internal methods
////////////////////////////////////////
*/
private void reportUnexpectedEOF(int gotBytes, int needed)
throws IOException
{
int bytePos = mByteCount + gotBytes;
int charPos = mCharCount;
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 = mByteCount + _ptr - 1;
int charPos = mCharCount + 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
{
mByteCount += (_length - available);
// Bytes that need to be moved to the beginning of buffer?
if (available > 0) {
if (_ptr > 0) {
for (int i = 0; i < available; ++i) {
_buffer[i] = _buffer[_ptr+i];
}
_ptr = 0;
}
_length = available;
} else {
/* Ok; here we can actually reasonably expect an EOF,
* so let's do a separate read right away:
*/
_ptr = 0;
int count = _in.read(_buffer);
if (count < 1) {
_length = 0;
if (count < 0) { // -1
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.read(_buffer, _length, _buffer.length - _length);
if (count < 1) {
if (count < 0) { // -1, EOF... no good!
freeBuffers(); // to help GC?
reportUnexpectedEOF(_length, 4);
}
// 0 count is no good; let's err out
reportStrangeStream();
}
_length += count;
}
return true;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/NumberInput.java 0000644 0001750 0001750 00000023625 11655120726 025673 0 ustar jamespage jamespage package org.codehaus.jackson.io;
public final class NumberInput
{
/**
* Textual representation of a double constant that can cause nasty problems
* with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308).
*/
public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308";
/**
* Constants needed for parsing longs from basic int parsing methods
*/
final static long L_BILLION = 1000000000;
final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1);
final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE);
/**
* Fast method for parsing integers that are known to fit into
* regular 32-bit signed int type. This means that length is
* between 1 and 9 digits (inclusive)
*
* Note: public to let unit tests call it
*/
public final static int parseInt(char[] digitChars, int offset, int len)
{
int num = digitChars[offset] - '0';
len += offset;
// This looks ugly, but appears the fastest way (as per measurements)
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
if (++offset < len) {
num = (num * 10) + (digitChars[offset] - '0');
}
}
}
}
}
}
}
}
return num;
}
/**
* Helper method to (more) efficiently parse integer numbers from
* String values.
*
* @since 1.7
*/
public final static int parseInt(String str)
{
/* Ok: let's keep strategy simple: ignoring optional minus sign,
* we'll accept 1 - 9 digits and parse things efficiently;
* otherwise just defer to JDK parse functionality.
*/
char c = str.charAt(0);
int length = str.length();
boolean negative = (c == '-');
int offset = 1;
// must have 1 - 9 digits after optional sign:
// negative?
if (negative) {
if (length == 1 || length > 10) {
return Integer.parseInt(str);
}
c = str.charAt(offset++);
} else {
if (length > 9) {
return Integer.parseInt(str);
}
}
if (c > '9' || c < '0') {
return Integer.parseInt(str);
}
int num = c - '0';
if (offset < length) {
c = str.charAt(offset++);
if (c > '9' || c < '0') {
return Integer.parseInt(str);
}
num = (num * 10) + (c - '0');
if (offset < length) {
c = str.charAt(offset++);
if (c > '9' || c < '0') {
return Integer.parseInt(str);
}
num = (num * 10) + (c - '0');
// Let's just loop if we have more than 3 digits:
if (offset < length) {
do {
c = str.charAt(offset++);
if (c > '9' || c < '0') {
return Integer.parseInt(str);
}
num = (num * 10) + (c - '0');
} while (offset < length);
}
}
}
return negative ? -num : num;
}
public final static long parseLong(char[] digitChars, int offset, int len)
{
// Note: caller must ensure length is [10, 18]
int len1 = len-9;
long val = parseInt(digitChars, offset, len1) * L_BILLION;
return val + (long) parseInt(digitChars, offset+len1, 9);
}
public final static long parseLong(String str)
{
/* Ok, now; as the very first thing, let's just optimize case of "fake longs";
* that is, if we know they must be ints, call int parsing
*/
int length = str.length();
if (length <= 9) {
return (long) parseInt(str);
}
// !!! TODO: implement efficient 2-int parsing...
return Long.parseLong(str);
}
/**
* Helper method for determining if given String representation of
* an integral number would fit in 64-bit Java long or not.
* Note that input String must NOT contain leading minus sign (even
* if 'negative' is set to true).
*
* @param negative Whether original number had a minus sign (which is
* NOT passed to this method) or not
*/
public final static boolean inLongRange(char[] digitChars, int offset, int len,
boolean negative)
{
String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
int cmpLen = cmpStr.length();
if (len < cmpLen) return true;
if (len > cmpLen) return false;
for (int i = 0; i < cmpLen; ++i) {
int diff = digitChars[offset+i] - cmpStr.charAt(i);
if (diff != 0) {
return (diff < 0);
}
}
return true;
}
/**
* Similar to {@link #inLongRange(char[],int,int,boolean)}, but
* with String argument
*
* @param negative Whether original number had a minus sign (which is
* NOT passed to this method) or not
*
* @since 1.5.0
*/
public final static boolean inLongRange(String numberStr, boolean negative)
{
String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
int cmpLen = cmpStr.length();
int actualLen = numberStr.length();
if (actualLen < cmpLen) return true;
if (actualLen > cmpLen) return false;
// could perhaps just use String.compareTo()?
for (int i = 0; i < cmpLen; ++i) {
int diff = numberStr.charAt(i) - cmpStr.charAt(i);
if (diff != 0) {
return (diff < 0);
}
}
return true;
}
/**
* @since 1.6
*/
public static int parseAsInt(String input, int defaultValue)
{
if (input == null) {
return defaultValue;
}
input = input.trim();
int len = input.length();
if (len == 0) {
return defaultValue;
}
// One more thing: use integer parsing for 'simple'
int i = 0;
if (i < len) { // skip leading sign:
char c = input.charAt(0);
if (c == '+') { // for plus, actually physically remove
input = input.substring(1);
len = input.length();
} else if (c == '-') { // minus, just skip for checks, must retain
++i;
}
}
for (; i < len; ++i) {
char c = input.charAt(i);
// if other symbols, parse as Double, coerce
if (c > '9' || c < '0') {
try {
return (int) parseDouble(input);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) { }
return defaultValue;
}
/**
* @since 1.6
*/
public static long parseAsLong(String input, long defaultValue)
{
if (input == null) {
return defaultValue;
}
input = input.trim();
int len = input.length();
if (len == 0) {
return defaultValue;
}
// One more thing: use long parsing for 'simple'
int i = 0;
if (i < len) { // skip leading sign:
char c = input.charAt(0);
if (c == '+') { // for plus, actually physically remove
input = input.substring(1);
len = input.length();
} else if (c == '-') { // minus, just skip for checks, must retain
++i;
}
}
for (; i < len; ++i) {
char c = input.charAt(i);
// if other symbols, parse as Double, coerce
if (c > '9' || c < '0') {
try {
return (long) parseDouble(input);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
try {
return Long.parseLong(input);
} catch (NumberFormatException e) { }
return defaultValue;
}
/**
* @since 1.6
*/
public static double parseAsDouble(String input, double defaultValue)
{
if (input == null) {
return defaultValue;
}
input = input.trim();
int len = input.length();
if (len == 0) {
return defaultValue;
}
try {
return parseDouble(input);
} catch (NumberFormatException e) { }
return defaultValue;
}
/**
* @since 1.8
*/
public final static double parseDouble(String numStr) throws NumberFormatException
{
// [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
if (NASTY_SMALL_DOUBLE.equals(numStr)) {
return Double.MIN_NORMAL;
}
return Double.parseDouble(numStr);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/NumberOutput.java 0000644 0001750 0001750 00000032230 11655120726 026064 0 ustar jamespage jamespage package org.codehaus.jackson.io;
public final class NumberOutput
{
private final static char NULL_CHAR = (char) 0;
private static int MILLION = 1000000;
private static int BILLION = 1000000000;
private static long TEN_BILLION_L = 10000000000L;
private static long THOUSAND_L = 1000L;
private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE);
final static char[] LEADING_TRIPLETS = new char[4000];
final static char[] FULL_TRIPLETS = new char[4000];
static {
/* Let's fill it with NULLs for ignorable leading digits,
* and digit chars for others
*/
int ix = 0;
for (int i1 = 0; i1 < 10; ++i1) {
char f1 = (char) ('0' + i1);
char l1 = (i1 == 0) ? NULL_CHAR : f1;
for (int i2 = 0; i2 < 10; ++i2) {
char f2 = (char) ('0' + i2);
char l2 = (i1 == 0 && i2 == 0) ? NULL_CHAR : f2;
for (int i3 = 0; i3 < 10; ++i3) {
// Last is never to be empty
char f3 = (char) ('0' + i3);
LEADING_TRIPLETS[ix] = l1;
LEADING_TRIPLETS[ix+1] = l2;
LEADING_TRIPLETS[ix+2] = f3;
FULL_TRIPLETS[ix] = f1;
FULL_TRIPLETS[ix+1] = f2;
FULL_TRIPLETS[ix+2] = f3;
ix += 4;
}
}
}
}
final static byte[] FULL_TRIPLETS_B = new byte[4000];
static {
for (int i = 0; i < 4000; ++i) {
FULL_TRIPLETS_B[i] = (byte) FULL_TRIPLETS[i];
}
}
final static String[] sSmallIntStrs = new String[] {
"0","1","2","3","4","5","6","7","8","9","10"
};
final static String[] sSmallIntStrs2 = new String[] {
"-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"
};
/*
/**********************************************************
/* Efficient serialization methods using raw buffers
/**********************************************************
*/
/**
* @return Offset within buffer after outputting int
*/
public static int outputInt(int value, char[] buffer, int offset)
{
if (value < 0) {
if (value == Integer.MIN_VALUE) {
/* Special case: no matching positive value within range;
* let's then "upgrade" to long and output as such.
*/
return outputLong((long) value, buffer, offset);
}
buffer[offset++] = '-';
value = -value;
}
if (value < MILLION) { // at most 2 triplets...
if (value < 1000) {
if (value < 10) {
buffer[offset++] = (char) ('0' + value);
} else {
offset = outputLeadingTriplet(value, buffer, offset);
}
} else {
int thousands = value / 1000;
value -= (thousands * 1000); // == value % 1000
offset = outputLeadingTriplet(thousands, buffer, offset);
offset = outputFullTriplet(value, buffer, offset);
}
return offset;
}
// ok, all 3 triplets included
/* Let's first hand possible billions separately before
* handling 3 triplets. This is possible since we know we
* can have at most '2' as billion count.
*/
boolean hasBillions = (value >= BILLION);
if (hasBillions) {
value -= BILLION;
if (value >= BILLION) {
value -= BILLION;
buffer[offset++] = '2';
} else {
buffer[offset++] = '1';
}
}
int newValue = value / 1000;
int ones = (value - (newValue * 1000)); // == value % 1000
value = newValue;
newValue /= 1000;
int thousands = (value - (newValue * 1000));
// value now has millions, which have 1, 2 or 3 digits
if (hasBillions) {
offset = outputFullTriplet(newValue, buffer, offset);
} else {
offset = outputLeadingTriplet(newValue, buffer, offset);
}
offset = outputFullTriplet(thousands, buffer, offset);
offset = outputFullTriplet(ones, buffer, offset);
return offset;
}
public static int outputInt(int value, byte[] buffer, int offset)
{
if (value < 0) {
if (value == Integer.MIN_VALUE) {
return outputLong((long) value, buffer, offset);
}
buffer[offset++] = '-';
value = -value;
}
if (value < MILLION) { // at most 2 triplets...
if (value < 1000) {
if (value < 10) {
buffer[offset++] = (byte) ('0' + value);
} else {
offset = outputLeadingTriplet(value, buffer, offset);
}
} else {
int thousands = value / 1000;
value -= (thousands * 1000); // == value % 1000
offset = outputLeadingTriplet(thousands, buffer, offset);
offset = outputFullTriplet(value, buffer, offset);
}
return offset;
}
boolean hasBillions = (value >= BILLION);
if (hasBillions) {
value -= BILLION;
if (value >= BILLION) {
value -= BILLION;
buffer[offset++] = '2';
} else {
buffer[offset++] = '1';
}
}
int newValue = value / 1000;
int ones = (value - (newValue * 1000)); // == value % 1000
value = newValue;
newValue /= 1000;
int thousands = (value - (newValue * 1000));
if (hasBillions) {
offset = outputFullTriplet(newValue, buffer, offset);
} else {
offset = outputLeadingTriplet(newValue, buffer, offset);
}
offset = outputFullTriplet(thousands, buffer, offset);
offset = outputFullTriplet(ones, buffer, offset);
return offset;
}
/**
* @return Offset within buffer after outputting int
*/
public static int outputLong(long value, char[] buffer, int offset)
{
// First: does it actually fit in an int?
if (value < 0L) {
/* MIN_INT is actually printed as long, just because its
* negation is not an int but long
*/
if (value > MIN_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
if (value == Long.MIN_VALUE) {
// Special case: no matching positive value within range
int len = SMALLEST_LONG.length();
SMALLEST_LONG.getChars(0, len, buffer, offset);
return (offset + len);
}
buffer[offset++] = '-';
value = -value;
} else {
if (value <= MAX_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
}
/* Ok: real long print. Need to first figure out length
* in characters, and then print in from end to beginning
*/
int origOffset = offset;
offset += calcLongStrLength(value);
int ptr = offset;
// First, with long arithmetics:
while (value > MAX_INT_AS_LONG) { // full triplet
ptr -= 3;
long newValue = value / THOUSAND_L;
int triplet = (int) (value - newValue * THOUSAND_L);
outputFullTriplet(triplet, buffer, ptr);
value = newValue;
}
// Then with int arithmetics:
int ivalue = (int) value;
while (ivalue >= 1000) { // still full triplet
ptr -= 3;
int newValue = ivalue / 1000;
int triplet = ivalue - (newValue * 1000);
outputFullTriplet(triplet, buffer, ptr);
ivalue = newValue;
}
// And finally, if anything remains, partial triplet
outputLeadingTriplet(ivalue, buffer, origOffset);
return offset;
}
public static int outputLong(long value, byte[] buffer, int offset)
{
if (value < 0L) {
if (value > MIN_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
if (value == Long.MIN_VALUE) {
// Special case: no matching positive value within range
int len = SMALLEST_LONG.length();
for (int i = 0; i < len; ++i) {
buffer[offset++] = (byte) SMALLEST_LONG.charAt(i);
}
return offset;
}
buffer[offset++] = '-';
value = -value;
} else {
if (value <= MAX_INT_AS_LONG) {
return outputInt((int) value, buffer, offset);
}
}
int origOffset = offset;
offset += calcLongStrLength(value);
int ptr = offset;
// First, with long arithmetics:
while (value > MAX_INT_AS_LONG) { // full triplet
ptr -= 3;
long newValue = value / THOUSAND_L;
int triplet = (int) (value - newValue * THOUSAND_L);
outputFullTriplet(triplet, buffer, ptr);
value = newValue;
}
// Then with int arithmetics:
int ivalue = (int) value;
while (ivalue >= 1000) { // still full triplet
ptr -= 3;
int newValue = ivalue / 1000;
int triplet = ivalue - (newValue * 1000);
outputFullTriplet(triplet, buffer, ptr);
ivalue = newValue;
}
outputLeadingTriplet(ivalue, buffer, origOffset);
return offset;
}
/*
/**********************************************************
/* Secondary convenience serialization methods
/**********************************************************
*/
/* !!! 05-Aug-2008, tatus: Any ways to further optimize
* these? (or need: only called by diagnostics methods?)
*/
public static String toString(int value)
{
// Lookup table for small values
if (value < sSmallIntStrs.length) {
if (value >= 0) {
return sSmallIntStrs[value];
}
int v2 = -value - 1;
if (v2 < sSmallIntStrs2.length) {
return sSmallIntStrs2[v2];
}
}
return Integer.toString(value);
}
public static String toString(long value)
{
if (value <= Integer.MAX_VALUE &&
value >= Integer.MIN_VALUE) {
return toString((int) value);
}
return Long.toString(value);
}
public static String toString(double value)
{
return Double.toString(value);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private static int outputLeadingTriplet(int triplet, char[] buffer, int offset)
{
int digitOffset = (triplet << 2);
char c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = c;
}
c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = c;
}
// Last is required to be non-empty
buffer[offset++] = LEADING_TRIPLETS[digitOffset];
return offset;
}
private static int outputLeadingTriplet(int triplet, byte[] buffer, int offset)
{
int digitOffset = (triplet << 2);
char c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = (byte) c;
}
c = LEADING_TRIPLETS[digitOffset++];
if (c != NULL_CHAR) {
buffer[offset++] = (byte) c;
}
// Last is required to be non-empty
buffer[offset++] = (byte) LEADING_TRIPLETS[digitOffset];
return offset;
}
private static int outputFullTriplet(int triplet, char[] buffer, int offset)
{
int digitOffset = (triplet << 2);
buffer[offset++] = FULL_TRIPLETS[digitOffset++];
buffer[offset++] = FULL_TRIPLETS[digitOffset++];
buffer[offset++] = FULL_TRIPLETS[digitOffset];
return offset;
}
private static int outputFullTriplet(int triplet, byte[] buffer, int offset)
{
int digitOffset = (triplet << 2);
buffer[offset++] = FULL_TRIPLETS_B[digitOffset++];
buffer[offset++] = FULL_TRIPLETS_B[digitOffset++];
buffer[offset++] = FULL_TRIPLETS_B[digitOffset];
return offset;
}
/**
*
* Pre-conditions: posValue is positive, and larger than
* Integer.MAX_VALUE (about 2 billions).
*/
private static int calcLongStrLength(long posValue)
{
int len = 10;
long comp = TEN_BILLION_L;
// 19 is longest, need to worry about overflow
while (posValue >= comp) {
if (len == 19) {
break;
}
++len;
comp = (comp << 3) + (comp << 1); // 10x
}
return len;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/JsonStringEncoder.java 0000644 0001750 0001750 00000037606 11655120726 027027 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import java.lang.ref.SoftReference;
import org.codehaus.jackson.util.BufferRecycler;
import org.codehaus.jackson.util.ByteArrayBuilder;
import org.codehaus.jackson.util.CharTypes;
import org.codehaus.jackson.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.
*
* @since 1.6
*/
public final class JsonStringEncoder
{
private final static char[] HEX_CHARS = CharTypes.copyHexChars();
private final static byte[] HEX_BYTES = CharTypes.copyHexBytes();
private final static int SURR1_FIRST = 0xD800;
private final static int SURR1_LAST = 0xDBFF;
private final static int SURR2_FIRST = 0xDC00;
private final static int SURR2_LAST = 0xDFFF;
private final static int INT_BACKSLASH = '\\';
private final static int INT_U = 'u';
private final static int INT_0 = '0';
/**
* This ThreadLocal
contains a {@link java.lang.ref.SoftRerefence}
* to a {@link BufferRecycler} used to provide a low-cost
* buffer recycling between reader and writer instances.
*/
final protected static ThreadLocal> _threadEncoder
= new ThreadLocal>();
/**
* Lazily constructed text buffer used to produce JSON encoded Strings
* as characters (without UTF-8 encoding)
*/
protected TextBuffer _textBuffer;
/**
* Lazily-constructed builder used for UTF-8 encoding of text values
* (quoted and unquoted)
*/
protected ByteArrayBuilder _byteBuilder;
/**
* Temporary buffer used for composing quote/escape sequences
*/
protected final char[] _quoteBuffer;
/*
/**********************************************************
/* Construction, instance access
/**********************************************************
*/
public JsonStringEncoder()
{
_quoteBuffer = new char[6];
_quoteBuffer[0] = '\\';
_quoteBuffer[2] = '0';
_quoteBuffer[3] = '0';
}
/**
* Factory method for getting an instance; this is either recycled per-thread instance,
* or a newly constructed one.
*/
public static JsonStringEncoder getInstance()
{
SoftReference ref = _threadEncoder.get();
JsonStringEncoder enc = (ref == null) ? null : ref.get();
if (enc == null) {
enc = new JsonStringEncoder();
_threadEncoder.set(new SoftReference(enc));
}
return enc;
}
/*
/**********************************************************
/* Public API
/**********************************************************
*/
/**
* Method that will quote text contents using JSON standard quoting,
* and return results as a character array
*/
public char[] quoteAsString(String input)
{
TextBuffer textBuffer = _textBuffer;
if (textBuffer == null) {
// no allocator; can add if we must, shouldn't need to
_textBuffer = textBuffer = new TextBuffer(null);
}
char[] outputBuffer = textBuffer.emptyAndGetCurrentSegment();
final int[] escCodes = CharTypes.get7BitOutputEscapes();
final int escCodeCount = escCodes.length;
int inPtr = 0;
final int inputLen = input.length();
int outPtr = 0;
outer_loop:
while (inPtr < inputLen) {
tight_loop:
while (true) {
char c = input.charAt(inPtr);
if (c < escCodeCount && escCodes[c] != 0) {
break tight_loop;
}
if (outPtr >= outputBuffer.length) {
outputBuffer = textBuffer.finishCurrentSegment();
outPtr = 0;
}
outputBuffer[outPtr++] = c;
if (++inPtr >= inputLen) {
break outer_loop;
}
}
// something to escape; 2 or 6-char variant?
int escCode = escCodes[input.charAt(inPtr++)];
int length = _appendSingleEscape(escCode, _quoteBuffer);
if ((outPtr + length) > outputBuffer.length) {
int first = outputBuffer.length - outPtr;
if (first > 0) {
System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, first);
}
outputBuffer = textBuffer.finishCurrentSegment();
int second = length - first;
System.arraycopy(_quoteBuffer, first, outputBuffer, outPtr, second);
outPtr += second;
} else {
System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, length);
outPtr += length;
}
}
textBuffer.setCurrentLength(outPtr);
return textBuffer.contentsAsArray();
}
/**
* Will quote given JSON String value using standard quoting, encode
* results as UTF-8, and return result as a byte array.
*/
public byte[] quoteAsUTF8(String text)
{
ByteArrayBuilder byteBuilder = _byteBuilder;
if (byteBuilder == null) {
// no allocator; can add if we must, shouldn't need to
_byteBuilder = byteBuilder = new ByteArrayBuilder(null);
}
int inputPtr = 0;
int inputEnd = text.length();
int outputPtr = 0;
byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment();
main_loop:
while (inputPtr < inputEnd) {
final int[] escCodes = CharTypes.get7BitOutputEscapes();
inner_loop: // ascii and escapes
while (true) {
int ch = text.charAt(inputPtr);
if (ch > 0x7F || escCodes[ch] != 0) {
break inner_loop;
}
if (outputPtr >= outputBuffer.length) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) ch;
if (++inputPtr >= inputEnd) {
break main_loop;
}
}
if (outputPtr >= outputBuffer.length) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputPtr = 0;
}
// Ok, so what did we hit?
int ch = (int) text.charAt(inputPtr++);
if (ch <= 0x7F) { // needs quoting
int escape = escCodes[ch];
// ctrl-char, 6-byte escape...
outputPtr = _appendByteEscape(ch, escape, byteBuilder, outputPtr);
outputBuffer = byteBuilder.getCurrentSegment();
continue main_loop;
} else if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
ch = (0x80 | (ch & 0x3f));
} else { // 3 or 4 bytes
// Surrogates?
if (ch < SURR1_FIRST || ch > SURR2_LAST) { // nope
outputBuffer[outputPtr++] = (byte) (0xe0 | (ch >> 12));
if (outputPtr >= outputBuffer.length) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
ch = (0x80 | (ch & 0x3f));
} else { // yes, surrogate pair
if (ch > SURR1_LAST) { // must be from first range
_throwIllegalSurrogate(ch);
}
// and if so, followed by another from next range
if (inputPtr >= inputEnd) {
_throwIllegalSurrogate(ch);
}
ch = _convertSurrogate(ch, text.charAt(inputPtr++));
if (ch > 0x10FFFF) { // illegal, as per RFC 4627
_throwIllegalSurrogate(ch);
}
outputBuffer[outputPtr++] = (byte) (0xf0 | (ch >> 18));
if (outputPtr >= outputBuffer.length) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 12) & 0x3f));
if (outputPtr >= outputBuffer.length) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
ch = (0x80 | (ch & 0x3f));
}
}
if (outputPtr >= outputBuffer.length) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) ch;
}
return _byteBuilder.completeAndCoalesce(outputPtr);
}
/**
* Will encode given String as UTF-8 (without any quoting), return
* resulting byte array.
*/
public byte[] encodeAsUTF8(String text)
{
ByteArrayBuilder byteBuilder = _byteBuilder;
if (byteBuilder == null) {
// no allocator; can add if we must, shouldn't need to
_byteBuilder = byteBuilder = new ByteArrayBuilder(null);
}
int inputPtr = 0;
int inputEnd = text.length();
int outputPtr = 0;
byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment();
int outputEnd = outputBuffer.length;
main_loop:
while (inputPtr < inputEnd) {
int c = text.charAt(inputPtr++);
// first tight loop for ascii
while (c <= 0x7F) {
if (outputPtr >= outputEnd) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) c;
if (inputPtr >= inputEnd) {
break main_loop;
}
c = text.charAt(inputPtr++);
}
// then multi-byte...
if (outputPtr >= outputEnd) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
if (c < 0x800) { // 2-byte
outputBuffer[outputPtr++] = (byte) (0xc0 | (c >> 6));
} else { // 3 or 4 bytes
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope
outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12));
if (outputPtr >= outputEnd) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
} else { // yes, surrogate pair
if (c > SURR1_LAST) { // must be from first range
_throwIllegalSurrogate(c);
}
// and if so, followed by another from next range
if (inputPtr >= inputEnd) {
_throwIllegalSurrogate(c);
}
c = _convertSurrogate(c, text.charAt(inputPtr++));
if (c > 0x10FFFF) { // illegal, as per RFC 4627
_throwIllegalSurrogate(c);
}
outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18));
if (outputPtr >= outputEnd) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
if (outputPtr >= outputEnd) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
}
}
if (outputPtr >= outputEnd) {
outputBuffer = byteBuilder.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f));
}
return _byteBuilder.completeAndCoalesce(outputPtr);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private int _appendSingleEscape(int escCode, char[] quoteBuffer)
{
if (escCode < 0) { // control char, value -(char + 1)
int value = -(escCode + 1);
quoteBuffer[1] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
quoteBuffer[4] = HEX_CHARS[value >> 4];
quoteBuffer[5] = HEX_CHARS[value & 0xF];
return 6;
}
quoteBuffer[1] = (char) escCode;
return 2;
}
private int _appendByteEscape(int ch, int escCode, ByteArrayBuilder byteBuilder, int ptr)
{
byteBuilder.setCurrentSegmentLength(ptr);
byteBuilder.append(INT_BACKSLASH);
if (escCode < 0) { // standard escape
byteBuilder.append(INT_U);
if (ch > 0xFF) {
int hi = (ch >> 8);
byteBuilder.append(HEX_BYTES[hi >> 4]);
byteBuilder.append(HEX_BYTES[hi & 0xF]);
ch &= 0xFF;
} else {
byteBuilder.append(INT_0);
byteBuilder.append(INT_0);
}
byteBuilder.append(HEX_BYTES[ch >> 4]);
byteBuilder.append(HEX_BYTES[ch & 0xF]);
} else { // 2-char simple escape
byteBuilder.append((byte) escCode);
}
return byteBuilder.getCurrentSegmentLength();
}
/**
* Method called to calculate UTF code point, from a surrogate pair.
*/
private int _convertSurrogate(int firstPart, int secondPart)
{
// Ok, then, is the second part valid?
if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
}
return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
}
private void _throwIllegalSurrogate(int code)
{
if (code > 0x10FFFF) { // over max?
throw new IllegalArgumentException("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?)
throw new IllegalArgumentException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
}
throw new IllegalArgumentException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
}
// should we ever get this?
throw new IllegalArgumentException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/package.html 0000644 0001750 0001750 00000000172 11655120726 025031 0 ustar jamespage jamespage
This package contains I/O helper classes Jackson itself uses, but that
are not exposed for external reuse.
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/SerializedString.java 0000644 0001750 0001750 00000006777 11655120726 026716 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import org.codehaus.jackson.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)
*
* @since 1.6
*/
public class SerializedString implements SerializableString
{
protected final String _value;
/* 13-Dec-2010, tatu: Whether use volatile or not is actually an important
* decision for multi-core use cases. Cost of volatility can be non-trivial
* for heavy use cases, and serialized-string instances are accessed often.
* Given that all code paths with common Jackson usage patterns go through
* a few memory barriers (mostly with cache/reuse pool access) it seems safe
* enough to omit volatiles here, given how simple lazy initialization is.
* This can be compared to how {@link String#intern} works; lazily and
* without synchronization or use of volatile keyword.
*/
protected /*volatile*/ byte[] _quotedUTF8Ref;
protected /*volatile*/ byte[] _unquotedUTF8Ref;
protected /*volatile*/ char[] _quotedChars;
public SerializedString(String v) { _value = v; }
/*
/**********************************************************
/* 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;
}
/*
/**********************************************************
/* Standard method overrides
/**********************************************************
*/
@Override
public final String toString() { return _value; }
@Override
public final int hashCode() { return _value.hashCode(); }
@Override
public final boolean equals(Object o)
{
if (o == this) return true;
if (o == null || o.getClass() != getClass()) return false;
SerializedString other = (SerializedString) o;
return _value.equals(other._value);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/SegmentedStringWriter.java 0000644 0001750 0001750 00000005017 11655120726 027715 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import java.io.*;
import org.codehaus.jackson.util.BufferRecycler;
import org.codehaus.jackson.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
.
*
* @since 1.3
*/
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, 0, str.length());
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
/**
* Main access method that will construct a String that contains
* all the contents, release all internal buffers we may have,
* and return result String.
* Note that the method is not idempotent -- if called second time,
* will just return an empty String.
*/
public String getAndClear()
{
String result = _buffer.contentsAsString();
_buffer.releaseBuffers();
return result;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/UTF8Writer.java 0000644 0001750 0001750 00000031136 11655120726 025342 0 ustar jamespage jamespage package org.codehaus.jackson.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 protected IOContext _context;
OutputStream _out;
byte[] _outBuffer;
final int _outBufferEnd;
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
*/
int _surrogate = 0;
public UTF8Writer(IOContext ctxt, OutputStream out)
{
_context = ctxt;
_out = out;
_outBuffer = ctxt.allocWriteEncodingBuffer();
/* Max. expansion for a single char (in unmodified UTF-8) is
* 4 bytes (or 3 depending on how you view it -- 4 when recombining
* surrogate pairs)
*/
_outBufferEnd = _outBuffer.length - 4;
_outPtr = 0;
}
@Override
public Writer append(char c)
throws IOException
{
write(c);
return this;
}
@Override
public void close()
throws IOException
{
if (_out != null) {
if (_outPtr > 0) {
_out.write(_outBuffer, 0, _outPtr);
_outPtr = 0;
}
OutputStream out = _out;
_out = null;
byte[] buf = _outBuffer;
if (buf != null) {
_outBuffer = null;
_context.releaseWriteEncodingBuffer(buf);
}
out.close();
/* Let's 'flush' orphan surrogate, no matter what; but only
* after cleanly closing everything else.
*/
int code = _surrogate;
_surrogate = 0;
if (code > 0) {
throwIllegal(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;
throwIllegal(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;
throwIllegal(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) {
throwIllegal(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
throwIllegal(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;
throwIllegal(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;
throwIllegal(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.
*/
private 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);
}
private void throwIllegal(int code)
throws IOException
{
if (code > 0x10FFFF) { // over max?
throw new IOException("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?)
throw new IOException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
}
throw new IOException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
}
// should we ever get this?
throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/OutputDecorator.java 0000644 0001750 0001750 00000002571 11655120726 026563 0 ustar jamespage jamespage package org.codehaus.jackson.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.
*
* @since 1.8
*/
public abstract class OutputDecorator
{
/**
* Method called by {@link org.codehaus.jackson.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 org.codehaus.jackson.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;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/CharacterEscapes.java 0000644 0001750 0001750 00000005424 11655120726 026620 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import org.codehaus.jackson.SerializableString;
import org.codehaus.jackson.util.CharTypes;
/**
* 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.
*
* @since 1.8
*/
public abstract class CharacterEscapes
{
/**
* Value used for lookup tables to indicate that matching characters
* do not need to be escaped.
*/
public final static int ESCAPE_NONE = 0;
/**
* Value used for lookup tables to indicate that matching characters
* are to be escaped using standard escaping; for JSON this means
* (for example) using "backslash - u" escape method.
*/
public final static int ESCAPE_STANDARD = -1;
/**
* Value used for lookup tables to indicate that matching characters
* will need custom escapes; and that another call
* to {@link #getEscapeSequence} is needed to figure out exact escape
* sequence to output.
*/
public final static int ESCAPE_CUSTOM = -2;
/**
* Method generators can call to get lookup table for determining
* escape handling for first 128 characters of Unicode (ASCII
* characters. Caller is not to modify contents of this array, since
* this is expected to be a shared copy.
*
* @return Array with size of at least 128, where first 128 entries
* have either one of ESCAPE_xxx
constants, or non-zero positive
* integer (meaning of which is data format specific; for JSON it means
* that combination of backslash and character with that value is to be used)
* to indicate that specific escape sequence is to be used.
*/
public abstract int[] getEscapeCodesForAscii();
/**
* Method generators can call to get lookup table for determining
* exact escape sequence to use for given character.
* It can be called for any character, but typically is called for
* either for ASCII characters for which custom escape
* sequence is needed; or for any non-ASCII character.
*/
public abstract SerializableString getEscapeSequence(int ch);
/**
* Helper method that can be used to get a copy of standard JSON
* escape definitions; this is useful when just wanting to slightly
* customize definitions. Caller can modify this array as it sees
* fit and usually returns modified instance via {@link #getEscapeCodesForAscii}
*/
public static int[] standardAsciiEscapesForJSON()
{
int[] esc = CharTypes.get7BitOutputEscapes();
int len = esc.length;
int[] result = new int[len];
System.arraycopy(esc, 0, result, 0, esc.length);
return result;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/IOContext.java 0000644 0001750 0001750 00000020034 11655120726 025266 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.util.BufferRecycler;
import org.codehaus.jackson.util.TextBuffer;
/**
* To limit number of configuration and state objects to pass, all
* contextual objects that need to be passed by the factory to
* readers and writers are combined under this object. One instance
* is created for each reader and writer.
*/
public final class IOContext
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Reference to the source object, which can be used for displaying
* location information
*/
protected final Object _sourceRef;
/**
* Encoding used by the underlying stream, if known.
*/
protected JsonEncoding _encoding;
/**
* Flag that indicates whether underlying input/output source/target
* object is fully managed by the owner of this context (parser or
* generator). If true, it is, and is to be closed by parser/generator;
* if false, calling application has to do closing (unless auto-closing
* feature is enabled for the parser/generator in question; in which
* case it acts like the owner).
*/
protected final boolean _managedResource;
/*
/**********************************************************
/* Buffer handling, recycling
/**********************************************************
*/
/**
* Recycler used for actual allocation/deallocation/reuse
*/
protected final BufferRecycler _bufferRecycler;
/**
* Reference to the allocated I/O buffer for low-level input reading,
* if any allocated.
*/
protected byte[] _readIOBuffer = null;
/**
* Reference to the allocated I/O buffer used for low-level
* encoding-related buffering.
*/
protected byte[] _writeEncodingBuffer = null;
/**
* Reference to the buffer allocated for tokenization purposes,
* in which character input is read, and from which it can be
* further returned.
*/
protected char[] _tokenCBuffer = null;
/**
* Reference to the buffer allocated for buffering it for
* output, before being encoded: generally this means concatenating
* output, then encoding when buffer fills up.
*/
protected char[] _concatCBuffer = null;
/**
* Reference temporary buffer Parser instances need if calling
* app decides it wants to access name via 'getTextCharacters' method.
* Regular text buffer can not be used as it may contain textual
* representation of the value token.
*/
protected char[] _nameCopyBuffer = null;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource)
{
_bufferRecycler = br;
_sourceRef = sourceRef;
_managedResource = managedResource;
}
public void setEncoding(JsonEncoding enc)
{
_encoding = enc;
}
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
public final Object getSourceReference() { return _sourceRef; }
public final JsonEncoding getEncoding() { return _encoding; }
public final boolean isResourceManaged() { return _managedResource; }
/*
/**********************************************************
/* Public API, buffer management
/**********************************************************
*/
public final 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 final byte[] allocReadIOBuffer()
{
if (_readIOBuffer != null) {
throw new IllegalStateException("Trying to call allocReadIOBuffer() second time");
}
_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER);
return _readIOBuffer;
}
public final byte[] allocWriteEncodingBuffer()
{
if (_writeEncodingBuffer != null) {
throw new IllegalStateException("Trying to call allocWriteEncodingBuffer() second time");
}
_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER);
return _writeEncodingBuffer;
}
public final char[] allocTokenBuffer()
{
if (_tokenCBuffer != null) {
throw new IllegalStateException("Trying to call allocTokenBuffer() second time");
}
_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER);
return _tokenCBuffer;
}
public final char[] allocConcatBuffer()
{
if (_concatCBuffer != null) {
throw new IllegalStateException("Trying to call allocConcatBuffer() second time");
}
_concatCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER);
return _concatCBuffer;
}
public final char[] allocNameCopyBuffer(int minSize)
{
if (_nameCopyBuffer != null) {
throw new IllegalStateException("Trying to call allocNameCopyBuffer() second time");
}
_nameCopyBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, minSize);
return _nameCopyBuffer;
}
/**
* Method to call when all the processing buffers can be safely
* recycled.
*/
public final 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
*/
if (buf != _readIOBuffer) {
throw new IllegalArgumentException("Trying to release buffer not owned by the context");
}
_readIOBuffer = null;
_bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER, buf);
}
}
public final 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
*/
if (buf != _writeEncodingBuffer) {
throw new IllegalArgumentException("Trying to release buffer not owned by the context");
}
_writeEncodingBuffer = null;
_bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER, buf);
}
}
public final void releaseTokenBuffer(char[] buf)
{
if (buf != null) {
if (buf != _tokenCBuffer) {
throw new IllegalArgumentException("Trying to release buffer not owned by the context");
}
_tokenCBuffer = null;
_bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER, buf);
}
}
public final void releaseConcatBuffer(char[] buf)
{
if (buf != null) {
if (buf != _concatCBuffer) {
throw new IllegalArgumentException("Trying to release buffer not owned by the context");
}
_concatCBuffer = null;
_bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER, buf);
}
}
public final void releaseNameCopyBuffer(char[] buf)
{
if (buf != null) {
if (buf != _nameCopyBuffer) {
throw new IllegalArgumentException("Trying to release buffer not owned by the context");
}
_nameCopyBuffer = null;
_bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, buf);
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/io/MergedStream.java 0000644 0001750 0001750 00000006272 11655120726 026001 0 ustar jamespage jamespage package org.codehaus.jackson.io;
import java.io.*;
/**
* Simple {@link InputStream} implementation that is used to "unwind" some
* data previously read from an input stream; so that as long as some of
* that data remains, it's returned; but as long as it's read, we'll
* just use data from the underlying original stream.
* This is similar to {@link java.io.PushbackInputStream}, but here there's
* only one implicit pushback, when instance is constructed.
*/
public final class MergedStream
extends InputStream
{
final protected IOContext _context;
final InputStream _in;
byte[] _buffer;
int _ptr;
final int _end;
public MergedStream(IOContext context,
InputStream in, byte[] buf, int start, int end)
{
_context = context;
_in = in;
_buffer = buf;
_ptr = start;
_end = end;
}
@Override
public int available() throws IOException
{
if (_buffer != null) {
return _end - _ptr;
}
return _in.available();
}
@Override
public void close() throws IOException
{
freeMergedBuffer();
_in.close();
}
@Override
public void mark(int readlimit)
{
if (_buffer == null) {
_in.mark(readlimit);
}
}
@Override
public boolean markSupported()
{
// Only supports marks past the initial rewindable section...
return (_buffer == null) && _in.markSupported();
}
@Override
public int read() throws IOException
{
if (_buffer != null) {
int c = _buffer[_ptr++] & 0xFF;
if (_ptr >= _end) {
freeMergedBuffer();
}
return c;
}
return _in.read();
}
@Override
public int read(byte[] b) throws IOException
{
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException
{
if (_buffer != null) {
int avail = _end - _ptr;
if (len > avail) {
len = avail;
}
System.arraycopy(_buffer, _ptr, b, off, len);
_ptr += len;
if (_ptr >= _end) {
freeMergedBuffer();
}
return len;
}
return _in.read(b, off, len);
}
@Override
public void reset() throws IOException
{
if (_buffer == null) {
_in.reset();
}
}
@Override
public long skip(long n) throws IOException
{
long count = 0L;
if (_buffer != null) {
int amount = _end - _ptr;
if (amount > n) { // all in pushed back segment?
_ptr += (int) n;
return n;
}
freeMergedBuffer();
count += amount;
n -= amount;
}
if (n > 0) {
count += _in.skip(n);
}
return count;
}
private void freeMergedBuffer()
{
byte[] buf = _buffer;
if (buf != null) {
_buffer = null;
if (_context != null) {
_context.releaseReadIOBuffer(buf);
}
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/ 0000755 0001750 0001750 00000000000 11672662540 023122 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/util/MinimalPrettyPrinter.java 0000644 0001750 0001750 00000010521 11655120726 030122 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.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).
*
* @since 1.6
*/
public class MinimalPrettyPrinter
implements PrettyPrinter
{
/**
* 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);
}
/**
* @since 1.9
*/
public MinimalPrettyPrinter(String rootValueSeparator) {
_rootValueSeparator = rootValueSeparator;
}
public void setRootValueSeparator(String sep) {
_rootValueSeparator = sep;
}
/*
/**********************************************************
/* PrettyPrinter impl
/**********************************************************
*/
@Override
public void writeRootValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException
{
if (_rootValueSeparator != null) {
jg.writeRaw(_rootValueSeparator);
}
}
@Override
public void writeStartObject(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw('{');
}
@Override
public void beforeObjectEntries(JsonGenerator jg)
throws IOException, JsonGenerationException
{
// nothing special, since no indentation is added
}
/**
* Method called after an object field has been output, but
* before the value is output.
*
* Default handling will just output a single
* colon to separate the two, without additional spaces.
*/
@Override
public void writeObjectFieldValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw(':');
}
/**
* Method called after an object entry (field:value) has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two.
*/
@Override
public void writeObjectEntrySeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw(',');
}
@Override
public void writeEndObject(JsonGenerator jg, int nrOfEntries)
throws IOException, JsonGenerationException
{
jg.writeRaw('}');
}
@Override
public void writeStartArray(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw('[');
}
@Override
public void beforeArrayValues(JsonGenerator jg)
throws IOException, JsonGenerationException
{
// nothing special, since no indentation is added
}
/**
* Method called after an array value has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate values.
*/
@Override
public void writeArrayValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw(',');
}
@Override
public void writeEndArray(JsonGenerator jg, int nrOfValues)
throws IOException, JsonGenerationException
{
jg.writeRaw(']');
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/VersionUtil.java 0000644 0001750 0001750 00000005103 11655120726 026243 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.*;
import java.util.regex.Pattern;
import org.codehaus.jackson.Version;
/**
* Functionality for supporting exposing of component {@link Version}s.
*
* @since 1.6
*/
public class VersionUtil
{
public final static String VERSION_FILE = "VERSION.txt";
private final static Pattern VERSION_SEPARATOR = Pattern.compile("[-_./;:]");
/**
* Helper method that will try to load version information for specified
* class. Implementation is simple: class loader that loaded specified
* class is asked to load resource with name "VERSION" from same
* location (package) as class itself had.
* If no version information is found, {@link Version#unknownVersion()} is
* returned.
*/
public static Version versionFor(Class> cls)
{
InputStream in;
Version version = null;
try {
in = cls.getResourceAsStream(VERSION_FILE);
if (in != null) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
version = parseVersion(br.readLine());
} finally {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
} catch (IOException e) { }
return (version == null) ? Version.unknownVersion() : version;
}
public static Version parseVersion(String versionStr)
{
if (versionStr == null) return null;
versionStr = versionStr.trim();
if (versionStr.length() == 0) return null;
String[] parts = VERSION_SEPARATOR.split(versionStr);
// Let's not bother if there's no separate parts; otherwise use whatever we got
if (parts.length < 2) {
return null;
}
int major = parseVersionPart(parts[0]);
int minor = parseVersionPart(parts[1]);
int patch = (parts.length > 2) ? parseVersionPart(parts[2]) : 0;
String snapshot = (parts.length > 3) ? parts[3] : null;
return new Version(major, minor, patch, snapshot);
}
protected static int parseVersionPart(String partStr)
{
partStr = partStr.toString();
int len = partStr.length();
int number = 0;
for (int i = 0; i < len; ++i) {
char c = partStr.charAt(i);
if (c > '9' || c < '0') break;
number = (number * 10) + (c - '0');
}
return number;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/InternCache.java 0000644 0001750 0001750 00000002474 11655120726 026153 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.util.Map;
import java.util.LinkedHashMap;
/**
* Singleton class that adds a simple first-level cache in front of
* regular String.intern() functionality. This is done as a minor
* performance optimization, to avoid calling native intern() method
* in cases where same String is being interned multiple times.
*
* Note: that this class extends {@link LinkedHashMap} is an implementation
* detail -- no code should ever directly call Map methods.
*/
@SuppressWarnings("serial")
public final class InternCache
extends LinkedHashMap
{
/**
* Size to use is somewhat arbitrary, so let's choose something that's
* neither too small (low hit ratio) nor too large (waste of memory)
*/
private final static int MAX_ENTRIES = 192;
public final static InternCache instance = new InternCache();
private InternCache() {
super(MAX_ENTRIES, 0.8f, true);
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest)
{
return size() > MAX_ENTRIES;
}
public synchronized String intern(String input)
{
String result = get(input);
if (result == null) {
result = input.intern();
put(result, result);
}
return result;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/ByteArrayBuilder.java 0000644 0001750 0001750 00000020722 11655120726 027175 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson.util;
import java.io.OutputStream;
import java.util.*;
/**
* Helper class that is similar to {@link java.io.ByteArrayOutputStream}
* in usage, but more geared to Jackson use cases internally.
* Specific changes include segment storage (no need to have linear
* backing buffer, can avoid reallocs, copying), as well API
* not based on {@link java.io.OutputStream}. In short, a very much
* specialized builder object.
*
* Since version 1.5, also implements {@link OutputStream} to allow
* efficient aggregation of output content as a byte array, similar
* to how {@link java.io.ByteArrayOutputStream} works, but somewhat more
* efficiently for many use cases.
*/
public final class ByteArrayBuilder
extends OutputStream
{
private final static byte[] NO_BYTES = new byte[0];
/**
* Size of the first block we will allocate.
*/
private final static int INITIAL_BLOCK_SIZE = 500;
/**
* Maximum block size we will use for individual non-aggregated
* blocks. Let's limit to using 256k chunks.
*/
private final static int MAX_BLOCK_SIZE = (1 << 18);
final static int DEFAULT_BLOCK_ARRAY_SIZE = 40;
/**
* Optional buffer recycler instance that we can use for allocating
* the first block.
*
* @since 1.5
*/
private final BufferRecycler _bufferRecycler;
private final LinkedList _pastBlocks = new LinkedList();
/**
* Number of bytes within byte arrays in {@link _pastBlocks}.
*/
private int _pastLen;
private byte[] _currBlock;
private int _currBlockPtr;
public ByteArrayBuilder() { this(null); }
public ByteArrayBuilder(BufferRecycler br) { this(br, INITIAL_BLOCK_SIZE); }
public ByteArrayBuilder(int firstBlockSize) { this(null, firstBlockSize); }
public ByteArrayBuilder(BufferRecycler br, int firstBlockSize)
{
_bufferRecycler = br;
if (br == null) {
_currBlock = new byte[firstBlockSize];
} else {
_currBlock = br.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_CONCAT_BUFFER);
}
}
public void reset()
{
_pastLen = 0;
_currBlockPtr = 0;
if (!_pastBlocks.isEmpty()) {
_pastBlocks.clear();
}
}
/**
* Clean up method to call to release all buffers this object may be
* using. After calling the method, no other accessors can be used (and
* attempt to do so may result in an exception)
*/
public void release() {
reset();
if (_bufferRecycler != null && _currBlock != null) {
_bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.WRITE_CONCAT_BUFFER, _currBlock);
_currBlock = null;
}
}
public void append(int i)
{
if (_currBlockPtr >= _currBlock.length) {
_allocMore();
}
_currBlock[_currBlockPtr++] = (byte) i;
}
public void appendTwoBytes(int b16)
{
if ((_currBlockPtr + 1) < _currBlock.length) {
_currBlock[_currBlockPtr++] = (byte) (b16 >> 8);
_currBlock[_currBlockPtr++] = (byte) b16;
} else {
append(b16 >> 8);
append(b16);
}
}
public void appendThreeBytes(int b24)
{
if ((_currBlockPtr + 2) < _currBlock.length) {
_currBlock[_currBlockPtr++] = (byte) (b24 >> 16);
_currBlock[_currBlockPtr++] = (byte) (b24 >> 8);
_currBlock[_currBlockPtr++] = (byte) b24;
} else {
append(b24 >> 16);
append(b24 >> 8);
append(b24);
}
}
/**
* Method called when results are finalized and we can get the
* full aggregated result buffer to return to the caller
*/
public byte[] toByteArray()
{
int totalLen = _pastLen + _currBlockPtr;
if (totalLen == 0) { // quick check: nothing aggregated?
return NO_BYTES;
}
byte[] result = new byte[totalLen];
int offset = 0;
for (byte[] block : _pastBlocks) {
int len = block.length;
System.arraycopy(block, 0, result, offset, len);
offset += len;
}
System.arraycopy(_currBlock, 0, result, offset, _currBlockPtr);
offset += _currBlockPtr;
if (offset != totalLen) { // just a sanity check
throw new RuntimeException("Internal error: total len assumed to be "+totalLen+", copied "+offset+" bytes");
}
// Let's only reset if there's sizable use, otherwise will get reset later on
if (!_pastBlocks.isEmpty()) {
reset();
}
return result;
}
/*
/**********************************************************
/* Non-stream API (similar to TextBuffer), since 1.6
/**********************************************************
*/
/**
* Method called when starting "manual" output: will clear out
* current state and return the first segment buffer to fill
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/JsonParserSequence.java 0000644 0001750 0001750 00000010706 11655120726 027544 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.IOException;
import java.util.*;
import org.codehaus.jackson.*;
/**
* 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
*
* @author tatu
* @since 1.5
*/
public class JsonParserSequence extends JsonParserDelegate
{
/**
* Parsers other than the first one (which is initially assigned
* as delegate)
*/
protected final JsonParser[] _parsers;
/**
* Index of the next parser in {@link #_parsers}.
*/
protected int _nextParser;
/*
*******************************************************
* Construction
*******************************************************
*/
protected JsonParserSequence(JsonParser[] parsers)
{
super(parsers[0]);
_parsers = parsers;
_nextParser = 1;
}
/**
* Method that will construct a parser (possibly a sequence) that
* contains all given sub-parsers.
* All parsers given are checked to see if they are sequences: and
* if so, they will be "flattened", that is, contained parsers are
* directly added in a new sequence instead of adding sequences
* within sequences. This is done to minimize delegation depth,
* ideally only having just a single level of delegation.
*/
public static JsonParserSequence createFlattened(JsonParser first, JsonParser second)
{
if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
// simple:
return new JsonParserSequence(new JsonParser[] { first, second });
}
ArrayList p = new ArrayList();
if (first instanceof JsonParserSequence) {
((JsonParserSequence) first).addFlattenedActiveParsers(p);
} else {
p.add(first);
}
if (second instanceof JsonParserSequence) {
((JsonParserSequence) second).addFlattenedActiveParsers(p);
} else {
p.add(second);
}
return new JsonParserSequence(p.toArray(new JsonParser[p.size()]));
}
protected void addFlattenedActiveParsers(List result)
{
for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) {
JsonParser p = _parsers[i];
if (p instanceof JsonParserSequence) {
((JsonParserSequence) p).addFlattenedActiveParsers(result);
} else {
result.add(p);
}
}
}
/*
*******************************************************
* Overridden methods, needed: cases where default
* delegation does not work
*******************************************************
*/
@Override
public void close() throws IOException
{
do {
delegate.close();
} while (switchToNext());
}
@Override
public JsonToken nextToken() throws IOException, JsonParseException
{
JsonToken t = delegate.nextToken();
if (t != null) return t;
while (switchToNext()) {
t = delegate.nextToken();
if (t != null) return t;
}
return null;
}
/*
/*******************************************************
/* Additional extended API
/*******************************************************
*/
/**
* Method that is most useful for debugging or testing;
* returns actual number of underlying parsers sequence
* was constructed with (nor just ones remaining active)
*/
public int containedParsersCount() {
return _parsers.length;
}
/*
/*******************************************************
/* Helper methods
/*******************************************************
*/
/**
* Method that will switch active parser from the current one
* to next parser in sequence, if there is another parser left,
* making this the new delegate. Old delegate is returned if
* switch succeeds.
*
* @return True if switch succeeded; false otherwise
*/
protected boolean switchToNext()
{
if (_nextParser >= _parsers.length) {
return false;
}
delegate = _parsers[_nextParser++];
return true;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/package-info.java 0000644 0001750 0001750 00000000142 11655120726 026302 0 ustar jamespage jamespage /**
* Utility classes used by Jackson Core functionality.
*/
package org.codehaus.jackson.util;
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/DefaultPrettyPrinter.java 0000644 0001750 0001750 00000017717 11655120726 030136 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.*;
import java.util.Arrays;
import org.codehaus.jackson.*;
import org.codehaus.jackson.impl.Indenter;
/**
* 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.
*/
public class DefaultPrettyPrinter
implements PrettyPrinter
{
// // // Config, indentation
/**
* By default, let's use only spaces to separate array values.
*/
protected Indenter _arrayIndenter = new FixedSpaceIndenter();
/**
* 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 = new Lf2SpacesIndenter();
// // // 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 int _nesting = 0;
/*
/**********************************************************
/* Life-cycle (construct, configure)
/**********************************************************
*/
public DefaultPrettyPrinter() { }
public void indentArraysWith(Indenter i)
{
_arrayIndenter = (i == null) ? new NopIndenter() : i;
}
public void indentObjectsWith(Indenter i)
{
_objectIndenter = (i == null) ? new NopIndenter() : i;
}
public void spacesInObjectEntries(boolean b) { _spacesInObjectEntries = b; }
/*
/**********************************************************
/* PrettyPrinter impl
/**********************************************************
*/
@Override
public void writeRootValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw(' ');
}
@Override
public void writeStartObject(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw('{');
if (!_objectIndenter.isInline()) {
++_nesting;
}
}
@Override
public void beforeObjectEntries(JsonGenerator jg)
throws IOException, JsonGenerationException
{
_objectIndenter.writeIndentation(jg, _nesting);
}
/**
* Method called after an object field has been output, but
* before the value is output.
*
* Default handling (without pretty-printing) will output a single
* colon to separate the two. Pretty-printer is
* to output a colon as well, but can surround that with other
* (white-space) decoration.
*/
@Override
public void writeObjectFieldValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
if (_spacesInObjectEntries) {
jg.writeRaw(" : ");
} else {
jg.writeRaw(':');
}
}
/**
* Method called after an object entry (field:value) has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
@Override
public void writeObjectEntrySeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw(',');
_objectIndenter.writeIndentation(jg, _nesting);
}
@Override
public void writeEndObject(JsonGenerator jg, int nrOfEntries)
throws IOException, JsonGenerationException
{
if (!_objectIndenter.isInline()) {
--_nesting;
}
if (nrOfEntries > 0) {
_objectIndenter.writeIndentation(jg, _nesting);
} else {
jg.writeRaw(' ');
}
jg.writeRaw('}');
}
@Override
public void writeStartArray(JsonGenerator jg)
throws IOException, JsonGenerationException
{
if (!_arrayIndenter.isInline()) {
++_nesting;
}
jg.writeRaw('[');
}
@Override
public void beforeArrayValues(JsonGenerator jg)
throws IOException, JsonGenerationException
{
_arrayIndenter.writeIndentation(jg, _nesting);
}
/**
* Method called after an array value has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
@Override
public void writeArrayValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException
{
jg.writeRaw(',');
_arrayIndenter.writeIndentation(jg, _nesting);
}
@Override
public void writeEndArray(JsonGenerator jg, int nrOfValues)
throws IOException, JsonGenerationException
{
if (!_arrayIndenter.isInline()) {
--_nesting;
}
if (nrOfValues > 0) {
_arrayIndenter.writeIndentation(jg, _nesting);
} else {
jg.writeRaw(' ');
}
jg.writeRaw(']');
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Dummy implementation that adds no indentation whatsoever
*/
public static class NopIndenter
implements Indenter
{
public NopIndenter() { }
@Override
public void writeIndentation(JsonGenerator jg, int level) { }
@Override
public boolean isInline() { return true; }
}
/**
* This is a very simple indenter that only every adds a
* single space for indentation. It is used as the default
* indenter for array values.
*/
public static class FixedSpaceIndenter
implements Indenter
{
public FixedSpaceIndenter() { }
@Override
public void writeIndentation(JsonGenerator jg, int level)
throws IOException, JsonGenerationException
{
jg.writeRaw(' ');
}
@Override
public boolean isInline() { return true; }
}
/**
* Default linefeed-based indenter uses system-specific linefeeds and
* 2 spaces for indentation per level.
*/
public static class Lf2SpacesIndenter
implements Indenter
{
final static String SYSTEM_LINE_SEPARATOR;
static {
String lf = null;
try {
lf = System.getProperty("line.separator");
} catch (Throwable t) { } // access exception?
SYSTEM_LINE_SEPARATOR = (lf == null) ? "\n" : lf;
}
final static int SPACE_COUNT = 64;
final static char[] SPACES = new char[SPACE_COUNT];
static {
Arrays.fill(SPACES, ' ');
}
public Lf2SpacesIndenter() { }
@Override
public boolean isInline() { return false; }
@Override
public void writeIndentation(JsonGenerator jg, int level)
throws IOException, JsonGenerationException
{
jg.writeRaw(SYSTEM_LINE_SEPARATOR);
level += level; // 2 spaces per level
while (level > SPACE_COUNT) { // should never happen but...
jg.writeRaw(SPACES, 0, SPACE_COUNT);
level -= SPACES.length;
}
jg.writeRaw(SPACES, 0, level);
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/BufferRecycler.java 0000644 0001750 0001750 00000006145 11655120726 026671 0 ustar jamespage jamespage package org.codehaus.jackson.util;
/**
* This is a small utility class, whose main functionality is to allow
* simple reuse of raw byte/char buffers. It is usually used through
* ThreadLocal
member of the owning class pointing to
* instance of this class through a SoftReference
. The
* end result is a low-overhead GC-cleanable recycling: hopefully
* ideal for use by stream readers.
*/
public class BufferRecycler
{
public final static int DEFAULT_WRITE_CONCAT_BUFFER_LEN = 2000;
public enum ByteBufferType {
READ_IO_BUFFER(4000)
/**
* Buffer used for temporarily storing encoded content; used
* for example by UTF-8 encoding writer
*/
,WRITE_ENCODING_BUFFER(4000)
/**
* Buffer used for temporarily concatenating output; used for
* example when requesting output as byte array.
*/
,WRITE_CONCAT_BUFFER(2000)
;
private final int size;
ByteBufferType(int size) { this.size = size; }
}
public enum CharBufferType {
TOKEN_BUFFER(2000) // Tokenizable input
,CONCAT_BUFFER(2000) // concatenated output
,TEXT_BUFFER(200) // Text content from input
,NAME_COPY_BUFFER(200) // Temporary buffer for getting name characters
;
private final int size;
CharBufferType(int size) { this.size = size; }
}
final protected byte[][] _byteBuffers = new byte[ByteBufferType.values().length][];
final protected char[][] _charBuffers = new char[CharBufferType.values().length][];
public BufferRecycler() { }
public final byte[] allocByteBuffer(ByteBufferType type)
{
int ix = type.ordinal();
byte[] buffer = _byteBuffers[ix];
if (buffer == null) {
buffer = balloc(type.size);
} else {
_byteBuffers[ix] = null;
}
return buffer;
}
public final void releaseByteBuffer(ByteBufferType type, byte[] buffer)
{
_byteBuffers[type.ordinal()] = buffer;
}
public final char[] allocCharBuffer(CharBufferType type)
{
return allocCharBuffer(type, 0);
}
public final char[] allocCharBuffer(CharBufferType type, int minSize)
{
if (type.size > minSize) {
minSize = type.size;
}
int ix = type.ordinal();
char[] buffer = _charBuffers[ix];
if (buffer == null || buffer.length < minSize) {
buffer = calloc(minSize);
} else {
_charBuffers[ix] = null;
}
return buffer;
}
public final void releaseCharBuffer(CharBufferType type, char[] buffer)
{
_charBuffers[type.ordinal()] = buffer;
}
/*
/**********************************************************
/* Actual allocations separated for easier debugging/profiling
/**********************************************************
*/
private final byte[] balloc(int size)
{
return new byte[size];
}
private final char[] calloc(int size)
{
return new char[size];
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/JsonGeneratorDelegate.java 0000644 0001750 0001750 00000016043 11655120726 030200 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.SerializedString;
public class JsonGeneratorDelegate extends JsonGenerator
{
/**
* Delegate object that method calls are delegated to.
*/
protected JsonGenerator delegate;
public JsonGeneratorDelegate(JsonGenerator d) {
delegate = d;
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException {
delegate.copyCurrentEvent(jp);
}
@Override
public void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException {
delegate.copyCurrentStructure(jp);
}
@Override
public JsonGenerator disable(Feature f) {
return delegate.disable(f);
}
@Override
public JsonGenerator enable(Feature f) {
return delegate.enable(f);
}
@Override
public void flush() throws IOException {
delegate.flush();
}
@Override
public ObjectCodec getCodec() {
return delegate.getCodec();
}
@Override
public JsonStreamContext getOutputContext() {
return delegate.getOutputContext();
}
@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 getOutputTarget() {
return delegate.getOutputTarget();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public boolean isEnabled(Feature f) {
return delegate.isEnabled(f);
}
@Override
public JsonGenerator setCodec(ObjectCodec oc) {
delegate.setCodec(oc);
return this;
}
@Override
public JsonGenerator useDefaultPrettyPrinter() {
delegate.useDefaultPrettyPrinter();
return this;
}
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
delegate.writeBinary(b64variant, data, offset, len);
}
@Override
public void writeBoolean(boolean state) throws IOException, JsonGenerationException {
delegate.writeBoolean(state);
}
@Override
public void writeEndArray() throws IOException, JsonGenerationException {
delegate.writeEndArray();
}
@Override
public void writeEndObject() throws IOException, JsonGenerationException {
delegate.writeEndObject();
}
@Override
public void writeFieldName(String name)
throws IOException, JsonGenerationException
{
delegate.writeFieldName(name);
}
@Override
public void writeFieldName(SerializedString name)
throws IOException, JsonGenerationException
{
delegate.writeFieldName(name);
}
@Override
public void writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
delegate.writeFieldName(name);
}
@Override
public void writeNull() throws IOException, JsonGenerationException {
delegate.writeNull();
}
@Override
public void writeNumber(int v) throws IOException, JsonGenerationException {
delegate.writeNumber(v);
}
@Override
public void writeNumber(long v) throws IOException, JsonGenerationException {
delegate.writeNumber(v);
}
@Override
public void writeNumber(BigInteger v) throws IOException,
JsonGenerationException {
delegate.writeNumber(v);
}
@Override
public void writeNumber(double v) throws IOException,
JsonGenerationException {
delegate.writeNumber(v);
}
@Override
public void writeNumber(float v) throws IOException,
JsonGenerationException {
delegate.writeNumber(v);
}
@Override
public void writeNumber(BigDecimal v) throws IOException,
JsonGenerationException {
delegate.writeNumber(v);
}
@Override
public void writeNumber(String encodedValue) throws IOException, JsonGenerationException, UnsupportedOperationException {
delegate.writeNumber(encodedValue);
}
@Override
public void writeObject(Object pojo) throws IOException,JsonProcessingException {
delegate.writeObject(pojo);
}
@Override
public void writeRaw(String text) throws IOException, JsonGenerationException {
delegate.writeRaw(text);
}
@Override
public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
delegate.writeRaw(text, offset, len);
}
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
delegate.writeRaw(text, offset, len);
}
@Override
public void writeRaw(char c) throws IOException, JsonGenerationException {
delegate.writeRaw(c);
}
@Override
public void writeRawValue(String text) throws IOException, JsonGenerationException {
delegate.writeRawValue(text);
}
@Override
public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
delegate.writeRawValue(text, offset, len);
}
@Override
public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
delegate.writeRawValue(text, offset, len);
}
@Override
public void writeStartArray() throws IOException, JsonGenerationException {
delegate.writeStartArray();
}
@Override
public void writeStartObject() throws IOException, JsonGenerationException {
delegate.writeStartObject();
}
@Override
public void writeString(String text) throws IOException,JsonGenerationException {
delegate.writeString(text);
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException {
delegate.writeString(text, offset, len);
}
@Override
public void writeString(SerializableString text) throws IOException, JsonGenerationException {
delegate.writeString(text);
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
delegate.writeRawUTF8String(text, offset, length);
}
@Override
public void writeUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
delegate.writeUTF8String(text, offset, length);
}
@Override
public void writeTree(JsonNode rootNode) throws IOException, JsonProcessingException {
delegate.writeTree(rootNode);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/JsonParserDelegate.java 0000644 0001750 0001750 00000014057 11655120726 027511 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
/**
* 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.
*
* @since 1.4
*/
public class JsonParserDelegate extends JsonParser
{
/**
* Delegate object that method calls are delegated to.
*/
protected JsonParser delegate;
public JsonParserDelegate(JsonParser d) {
delegate = d;
}
/*
/**********************************************************
/* Public API, configuration
/**********************************************************
*/
@Override
public void setCodec(ObjectCodec c) {
delegate.setCodec(c);
}
@Override
public ObjectCodec getCodec() {
return delegate.getCodec();
}
@Override
public JsonParser enable(Feature f) {
delegate.enable(f);
return this;
}
@Override
public JsonParser disable(Feature f) {
delegate.disable(f);
return this;
}
@Override
public boolean isEnabled(Feature f) {
return delegate.isEnabled(f);
}
@Override
public 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();
}
/*
/**********************************************************
/* Closeable impl
/**********************************************************
*/
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
/*
/**********************************************************
/* Public API, token accessors
/**********************************************************
*/
@Override
public JsonToken getCurrentToken() {
return delegate.getCurrentToken();
}
@Override
public boolean hasCurrentToken() {
return delegate.hasCurrentToken();
}
@Override
public void clearCurrentToken() {
delegate.clearCurrentToken();
}
@Override
public String getCurrentName() throws IOException, JsonParseException {
return delegate.getCurrentName();
}
@Override
public JsonLocation getCurrentLocation() {
return delegate.getCurrentLocation();
}
@Override
public JsonToken getLastClearedToken() {
return delegate.getLastClearedToken();
}
@Override
public JsonStreamContext getParsingContext() {
return delegate.getParsingContext();
}
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
@Override
public String getText() throws IOException, JsonParseException {
return delegate.getText();
}
@Override
public char[] getTextCharacters() throws IOException, JsonParseException {
return delegate.getTextCharacters();
}
@Override
public int getTextLength() throws IOException, JsonParseException {
return delegate.getTextLength();
}
@Override
public int getTextOffset() throws IOException, JsonParseException {
return delegate.getTextOffset();
}
/*
/**********************************************************
/* Public API, access to token information, numeric
/**********************************************************
*/
@Override
public BigInteger getBigIntegerValue() throws IOException,JsonParseException {
return delegate.getBigIntegerValue();
}
@Override
public byte getByteValue() throws IOException, JsonParseException {
return delegate.getByteValue();
}
@Override
public short getShortValue() throws IOException, JsonParseException {
return delegate.getShortValue();
}
@Override
public BigDecimal getDecimalValue() throws IOException, JsonParseException {
return delegate.getDecimalValue();
}
@Override
public double getDoubleValue() throws IOException, JsonParseException {
return delegate.getDoubleValue();
}
@Override
public float getFloatValue() throws IOException, JsonParseException {
return delegate.getFloatValue();
}
@Override
public int getIntValue() throws IOException, JsonParseException {
return delegate.getIntValue();
}
@Override
public long getLongValue() throws IOException, JsonParseException {
return delegate.getLongValue();
}
@Override
public NumberType getNumberType() throws IOException, JsonParseException {
return delegate.getNumberType();
}
@Override
public Number getNumberValue() throws IOException, JsonParseException {
return delegate.getNumberValue();
}
@Override
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException {
return delegate.getBinaryValue(b64variant);
}
@Override
public JsonLocation getTokenLocation() {
return delegate.getTokenLocation();
}
@Override
public JsonToken nextToken() throws IOException, JsonParseException {
return delegate.nextToken();
}
@Override
public JsonParser skipChildren() throws IOException, JsonParseException {
delegate.skipChildren();
// NOTE: must NOT delegate this method to delegate, needs to be self-reference for chaining
return this;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/TextBuffer.java 0000644 0001750 0001750 00000052277 11655120726 026054 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.math.BigDecimal;
import java.util.ArrayList;
import org.codehaus.jackson.io.NumberInput;
/**
* TextBuffer is a class similar to {@link StringBuffer}, with
* following differences:
*
* TextBuffer uses segments character arrays, to avoid having
* to do additional array copies when array is not big enough.
* This means that only reallocating that is necessary is done only once:
* if and when caller
* wants to access contents in a linear array (char[], String).
*
* TextBuffer can also be initialized in "shared mode", in which
* it will just act as a wrapper to a single char array managed
* by another object (like parser that owns it)
*
* TextBuffer is not synchronized.
*
*
*/
public final class TextBuffer
{
final static char[] NO_CHARS = new char[0];
/**
* Let's start with sizable but not huge buffer, will grow as necessary
*/
final static int MIN_SEGMENT_LEN = 1000;
/**
* Let's limit maximum segment length to something sensible
* like 256k
*/
final static int MAX_SEGMENT_LEN = 0x40000;
/*
/**********************************************************
/* Configuration:
/**********************************************************
*/
private final BufferRecycler _allocator;
/*
/**********************************************************
/* Shared input buffers
/**********************************************************
*/
/**
* Shared input buffer; stored here in case some input can be returned
* as is, without being copied to collector's own buffers. Note that
* this is read-only for this Object.
*/
private char[] _inputBuffer;
/**
* Character offset of first char in input buffer; -1 to indicate
* that input buffer currently does not contain any useful char data
*/
private int _inputStart;
private int _inputLen;
/*
/**********************************************************
/* Aggregation segments (when not using input buf)
/**********************************************************
*/
/**
* List of segments prior to currently active segment.
*/
private ArrayList _segments;
/**
* Flag that indicates whether _seqments is non-empty
*/
private boolean _hasSegments = false;
// // // Currently used segment; not (yet) contained in _seqments
/**
* Amount of characters in segments in {@link _segments}
*/
private int _segmentSize;
private char[] _currentSegment;
/**
* Number of characters in currently active (last) segment
*/
private int _currentSize;
/*
/**********************************************************
/* Caching of results
/**********************************************************
*/
/**
* String that will be constructed when the whole contents are
* needed; will be temporarily stored in case asked for again.
*/
private String _resultString;
private char[] _resultArray;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public TextBuffer(BufferRecycler allocator)
{
_allocator = allocator;
}
/**
* Method called to indicate that the underlying buffers should now
* be recycled if they haven't yet been recycled. Although caller
* can still use this text buffer, it is not advisable to call this
* method if that is likely, since next time a buffer is needed,
* buffers need to reallocated.
* Note: calling this method automatically also clears contents
* of the buffer.
*/
public void releaseBuffers()
{
if (_allocator == null) {
resetWithEmpty();
} else {
if (_currentSegment != null) {
// First, let's get rid of all but the largest char array
resetWithEmpty();
// And then return that array
char[] buf = _currentSegment;
_currentSegment = null;
_allocator.releaseCharBuffer(BufferRecycler.CharBufferType.TEXT_BUFFER, buf);
}
}
}
/**
* Method called to clear out any content text buffer may have, and
* initializes buffer to use non-shared data.
*/
public void resetWithEmpty()
{
_inputStart = -1; // indicates shared buffer not used
_currentSize = 0;
_inputLen = 0;
_inputBuffer = null;
_resultString = null;
_resultArray = null;
// And then reset internal input buffers, if necessary:
if (_hasSegments) {
clearSegments();
}
}
/**
* Method called to initialize the buffer with a shared copy of data;
* this means that buffer will just have pointers to actual data. It
* also means that if anything is to be appended to the buffer, it
* will first have to unshare it (make a local copy).
*/
public void resetWithShared(char[] buf, int start, int len)
{
// First, let's clear intermediate values, if any:
_resultString = null;
_resultArray = null;
// Then let's mark things we need about input buffer
_inputBuffer = buf;
_inputStart = start;
_inputLen = len;
// And then reset internal input buffers, if necessary:
if (_hasSegments) {
clearSegments();
}
}
public void resetWithCopy(char[] buf, int start, int len)
{
_inputBuffer = null;
_inputStart = -1; // indicates shared buffer not used
_inputLen = 0;
_resultString = null;
_resultArray = null;
// And then reset internal input buffers, if necessary:
if (_hasSegments) {
clearSegments();
} else if (_currentSegment == null) {
_currentSegment = findBuffer(len);
}
_currentSize = _segmentSize = 0;
append(buf, start, len);
}
public void resetWithString(String value)
{
_inputBuffer = null;
_inputStart = -1;
_inputLen = 0;
_resultString = value;
_resultArray = null;
if (_hasSegments) {
clearSegments();
}
_currentSize = 0;
}
/**
* Helper method used to find a buffer to use, ideally one
* recycled earlier.
*/
private final char[] findBuffer(int needed)
{
if (_allocator != null) {
return _allocator.allocCharBuffer(BufferRecycler.CharBufferType.TEXT_BUFFER, needed);
}
return new char[Math.max(needed, MIN_SEGMENT_LEN)];
}
private final 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}.
*
* @since 1.9
*/
public boolean hasTextAsCharacters()
{
// if we have array in some form, sure
if (_inputStart >= 0 || _resultArray != null) {
return true;
}
// not if we have String as value
if (_resultString != null) {
return false;
}
return true;
}
public char[] getTextBuffer()
{
// Are we just using shared input buffer?
if (_inputStart >= 0) {
return _inputBuffer;
}
if (_resultArray != null) {
return _resultArray;
}
if (_resultString != null) {
return (_resultArray = _resultString.toCharArray());
}
// Nope; but does it fit in just one segment?
if (!_hasSegments) {
return _currentSegment;
}
// Nope, need to have/create a non-segmented array and return it
return contentsAsArray();
}
/*
/**********************************************************
/* Other accessors:
/**********************************************************
*/
public String contentsAsString()
{
if (_resultString == null) {
// Has array been requested? Can make a shortcut, if so:
if (_resultArray != null) {
_resultString = new String(_resultArray);
} else {
// Do we use shared array?
if (_inputStart >= 0) {
if (_inputLen < 1) {
return (_resultString = "");
}
_resultString = new String(_inputBuffer, _inputStart, _inputLen);
} else { // nope... need to copy
// But first, let's see if we have just one buffer
int segLen = _segmentSize;
int currLen = _currentSize;
if (segLen == 0) { // yup
_resultString = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen);
} else { // no, need to combine
StringBuilder sb = new StringBuilder(segLen + currLen);
// First stored segments
if (_segments != null) {
for (int i = 0, len = _segments.size(); i < len; ++i) {
char[] curr = _segments.get(i);
sb.append(curr, 0, curr.length);
}
}
// And finally, current segment:
sb.append(_currentSegment, 0, _currentSize);
_resultString = sb.toString();
}
}
}
}
return _resultString;
}
public char[] contentsAsArray()
{
char[] result = _resultArray;
if (result == null) {
_resultArray = result = buildResultArray();
}
return result;
}
/**
* Convenience method for converting contents of the buffer
* into a {@link BigDecimal}.
*/
public BigDecimal contentsAsDecimal()
throws NumberFormatException
{
// Already got a pre-cut array?
if (_resultArray != null) {
return new BigDecimal(_resultArray);
}
// Or a shared buffer?
if (_inputStart >= 0) {
return new BigDecimal(_inputBuffer, _inputStart, _inputLen);
}
// Or if not, just a single buffer (the usual case)
if (_segmentSize == 0) {
return new BigDecimal(_currentSegment, 0, _currentSize);
}
// If not, let's just get it aggregated...
return new BigDecimal(contentsAsArray());
}
/**
* Convenience method for converting contents of the buffer
* into a Double value.
*/
public double contentsAsDouble()
throws NumberFormatException
{
return NumberInput.parseDouble(contentsAsString());
}
/*
/**********************************************************
/* Public mutators:
/**********************************************************
*/
/**
* Method called to make sure that buffer is not using shared input
* buffer; if it is, it will copy such contents to private buffer.
*/
public void ensureNotShared() {
if (_inputStart >= 0) {
unshare(16);
}
}
public void append(char c) {
// Using shared buffer so far?
if (_inputStart >= 0) {
unshare(16);
}
_resultString = null;
_resultArray = null;
// Room in current segment?
char[] curr = _currentSegment;
if (_currentSize >= curr.length) {
expand(1);
curr = _currentSegment;
}
curr[_currentSize++] = c;
}
public void append(char[] c, int start, int len)
{
// Can't append to shared buf (sanity check)
if (_inputStart >= 0) {
unshare(len);
}
_resultString = null;
_resultArray = null;
// Room in current segment?
char[] curr = _currentSegment;
int max = curr.length - _currentSize;
if (max >= len) {
System.arraycopy(c, start, curr, _currentSize, len);
_currentSize += len;
} else {
// 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.
expand(len); // note: curr != _currentSegment after this
System.arraycopy(c, start, _currentSegment, 0, len);
_currentSize = len;
}
}
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;
} else {
// 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.
*/
expand(len);
str.getChars(offset, offset+len, _currentSegment, 0);
_currentSize = len;
}
}
/*
/**********************************************************
/* Raw access, for high-performance use:
/**********************************************************
*/
public char[] getCurrentSegment()
{
/* Since the intention of the caller is to directly add stuff into
* buffers, we should NOT have anything in shared buffer... ie. may
* need to unshare contents.
*/
if (_inputStart >= 0) {
unshare(1);
} else {
char[] curr = _currentSegment;
if (curr == null) {
_currentSegment = findBuffer(0);
} else if (_currentSize >= curr.length) {
// Plus, we better have room for at least one more char
expand(1);
}
}
return _currentSegment;
}
public final char[] emptyAndGetCurrentSegment()
{
// inlined 'resetWithEmpty()'
_inputStart = -1; // indicates shared buffer not used
_currentSize = 0;
_inputLen = 0;
_inputBuffer = null;
_resultString = null;
_resultArray = null;
// And then reset internal input buffers, if necessary:
if (_hasSegments) {
clearSegments();
}
char[] curr = _currentSegment;
if (curr == null) {
_currentSegment = curr = findBuffer(0);
}
return curr;
}
public int getCurrentSegmentSize() {
return _currentSize;
}
public void setCurrentLength(int len) {
_currentSize = len;
}
public char[] finishCurrentSegment()
{
if (_segments == null) {
_segments = new ArrayList();
}
_hasSegments = true;
_segments.add(_currentSegment);
int oldLen = _currentSegment.length;
_segmentSize += oldLen;
// Let's grow segments by 50%
int newLen = Math.min(oldLen + (oldLen >> 1), MAX_SEGMENT_LEN);
char[] curr = _charArray(newLen);
_currentSize = 0;
_currentSegment = curr;
return curr;
}
/**
* Method called to expand size of the current segment, to
* accomodate for more contiguous content. Usually only
* used when parsing tokens like names.
*/
public char[] expandCurrentSegment()
{
char[] curr = _currentSegment;
// Let's grow by 50%
int len = curr.length;
// Must grow by at least 1 char, no matter what
int newLen = (len == MAX_SEGMENT_LEN) ?
(MAX_SEGMENT_LEN + 1) : Math.min(MAX_SEGMENT_LEN, len + (len >> 1));
_currentSegment = _charArray(newLen);
System.arraycopy(curr, 0, _currentSegment, 0, len);
return _currentSegment;
}
/*
/**********************************************************
/* Standard methods:
/**********************************************************
*/
/**
* Note: calling this method may not be as efficient as calling
* {@link #contentsAsString}, since it's not guaranteed that resulting
* String is cached.
*/
@Override
public String toString() {
return contentsAsString();
}
/*
/**********************************************************
/* Internal methods:
/**********************************************************
*/
/**
* Method called if/when we need to append content when we have been
* initialized to use shared buffer.
*/
private void unshare(int needExtra)
{
int sharedLen = _inputLen;
_inputLen = 0;
char[] inputBuf = _inputBuffer;
_inputBuffer = null;
int start = _inputStart;
_inputStart = -1;
// Is buffer big enough, or do we need to reallocate?
int needed = sharedLen+needExtra;
if (_currentSegment == null || needed > _currentSegment.length) {
_currentSegment = findBuffer(needed);
}
if (sharedLen > 0) {
System.arraycopy(inputBuf, start, _currentSegment, 0, sharedLen);
}
_segmentSize = 0;
_currentSize = sharedLen;
}
/**
* Method called when current segment is full, to allocate new
* segment.
*/
private void expand(int minNewSegmentSize)
{
// First, let's move current segment to segment list:
if (_segments == null) {
_segments = new ArrayList();
}
char[] curr = _currentSegment;
_hasSegments = true;
_segments.add(curr);
_segmentSize += curr.length;
int oldLen = curr.length;
// Let's grow segments by 50% minimum
int sizeAddition = oldLen >> 1;
if (sizeAddition < minNewSegmentSize) {
sizeAddition = minNewSegmentSize;
}
curr = _charArray(Math.min(MAX_SEGMENT_LEN, oldLen + sizeAddition));
_currentSize = 0;
_currentSegment = curr;
}
private char[] buildResultArray()
{
if (_resultString != null) { // Can take a shortcut...
return _resultString.toCharArray();
}
char[] result;
// Do we use shared array?
if (_inputStart >= 0) {
if (_inputLen < 1) {
return NO_CHARS;
}
result = _charArray(_inputLen);
System.arraycopy(_inputBuffer, _inputStart, result, 0,
_inputLen);
} else { // nope
int size = size();
if (size < 1) {
return NO_CHARS;
}
int offset = 0;
result = _charArray(size);
if (_segments != null) {
for (int i = 0, len = _segments.size(); i < len; ++i) {
char[] curr = (char[]) _segments.get(i);
int currLen = curr.length;
System.arraycopy(curr, 0, result, offset, currLen);
offset += currLen;
}
}
System.arraycopy(_currentSegment, 0, result, offset, _currentSize);
}
return result;
}
private final char[] _charArray(int len) {
return new char[len];
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/TokenBuffer.java 0000644 0001750 0001750 00000126556 11655120726 026212 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.impl.JsonParserMinimalBase;
import org.codehaus.jackson.impl.JsonReadContext;
import org.codehaus.jackson.impl.JsonWriteContext;
import org.codehaus.jackson.io.SerializedString;
/**
* 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.
*
* @since 1.5
*/
public class TokenBuffer
/* Won't use JsonGeneratorBase, to minimize overhead for validity
* checking
*/
extends JsonGenerator
{
protected final static int DEFAULT_PARSER_FEATURES = JsonParser.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 org.codehaus.jackson.JsonGenerator.Feature}s
* are enabled.
*
* NOTE: most features have no effect on this class
*/
protected int _generatorFeatures;
protected boolean _closed;
/*
/**********************************************************
/* 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 _appendOffset;
/*
/**********************************************************
/* 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.
*/
public TokenBuffer(ObjectCodec codec)
{
_objectCodec = codec;
_generatorFeatures = DEFAULT_PARSER_FEATURES;
_writeContext = JsonWriteContext.createRootContext();
// at first we have just one segment
_first = _last = new Segment();
_appendOffset = 0;
}
/**
* 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);
}
/**
* @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());
p.setLocation(src.getTokenLocation());
return p;
}
/*
/**********************************************************
/* Other custom methods not needed for implementing interfaces
/**********************************************************
*/
/**
* 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 jgen)
throws IOException, JsonGenerationException
{
Segment segment = _first;
int ptr = -1;
while (true) {
if (++ptr >= Segment.TOKENS_PER_SEGMENT) {
ptr = 0;
segment = segment.next();
if (segment == null) break;
}
JsonToken t = segment.type(ptr);
if (t == null) break;
// Note: copied from 'copyCurrentEvent'...
switch (t) {
case START_OBJECT:
jgen.writeStartObject();
break;
case END_OBJECT:
jgen.writeEndObject();
break;
case START_ARRAY:
jgen.writeStartArray();
break;
case END_ARRAY:
jgen.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) {
jgen.writeFieldName((SerializableString) ob);
} else {
jgen.writeFieldName((String) ob);
}
}
break;
case VALUE_STRING:
{
Object ob = segment.get(ptr);
if (ob instanceof SerializableString) {
jgen.writeString((SerializableString) ob);
} else {
jgen.writeString((String) ob);
}
}
break;
case VALUE_NUMBER_INT:
{
Number n = (Number) segment.get(ptr);
if (n instanceof BigInteger) {
jgen.writeNumber((BigInteger) n);
} else if (n instanceof Long) {
jgen.writeNumber(n.longValue());
} else {
jgen.writeNumber(n.intValue());
}
}
break;
case VALUE_NUMBER_FLOAT:
{
Object n = segment.get(ptr);
if (n instanceof BigDecimal) {
jgen.writeNumber((BigDecimal) n);
} else if (n instanceof Float) {
jgen.writeNumber(((Float) n).floatValue());
} else if (n instanceof Double) {
jgen.writeNumber(((Double) n).doubleValue());
} else if (n == null) {
jgen.writeNull();
} else if (n instanceof String) {
jgen.writeNumber((String) n);
} else {
throw new JsonGenerationException("Unrecognized value type for VALUE_NUMBER_FLOAT: "+n.getClass().getName()+", can not serialize");
}
}
break;
case VALUE_TRUE:
jgen.writeBoolean(true);
break;
case VALUE_FALSE:
jgen.writeBoolean(false);
break;
case VALUE_NULL:
jgen.writeNull();
break;
case VALUE_EMBEDDED_OBJECT:
jgen.writeObject(segment.get(ptr));
break;
default:
throw new RuntimeException("Internal error: should never end up through this code path");
}
}
}
@Override
public String toString()
{
// Let's print up to 100 first tokens...
final int MAX_COUNT = 100;
StringBuilder sb = new StringBuilder();
sb.append("[TokenBuffer: ");
JsonParser jp = asParser();
int count = 0;
while (true) {
JsonToken t;
try {
t = jp.nextToken();
} catch (IOException ioe) { // should never occur
throw new IllegalStateException(ioe);
}
if (t == null) break;
if (count < MAX_COUNT) {
if (count > 0) {
sb.append(", ");
}
sb.append(t.toString());
}
++count;
}
if (count >= MAX_COUNT) {
sb.append(" ... (truncated ").append(count-MAX_COUNT).append(" entries)");
}
sb.append(']');
return sb.toString();
}
/*
/**********************************************************
/* 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(Feature f, boolean state) { }
@Override
public boolean isEnabled(Feature f) {
return (_generatorFeatures & f.getMask()) != 0;
}
@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: 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, JsonGenerationException
{
_append(JsonToken.START_ARRAY);
_writeContext = _writeContext.createChildArrayContext();
}
@Override
public final void writeEndArray()
throws IOException, JsonGenerationException
{
_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, JsonGenerationException
{
_append(JsonToken.START_OBJECT);
_writeContext = _writeContext.createChildObjectContext();
}
@Override
public final void writeEndObject()
throws IOException, JsonGenerationException
{
_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, JsonGenerationException
{
_append(JsonToken.FIELD_NAME, name);
_writeContext.writeFieldName(name);
}
@Override
public void writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
_append(JsonToken.FIELD_NAME, name);
_writeContext.writeFieldName(name.getValue());
}
@Override
public void writeFieldName(SerializedString name)
throws IOException, JsonGenerationException
{
_append(JsonToken.FIELD_NAME, name);
_writeContext.writeFieldName(name.getValue());
}
/*
/**********************************************************
/* JsonGenerator implementation: write methods, textual
/**********************************************************
*/
@Override
public void writeString(String text) throws IOException,JsonGenerationException {
if (text == null) {
writeNull();
} else {
_append(JsonToken.VALUE_STRING, text);
}
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException {
writeString(new String(text, offset, len));
}
@Override
public void writeString(SerializableString text) throws IOException, JsonGenerationException {
if (text == null) {
writeNull();
} else {
_append(JsonToken.VALUE_STRING, text);
}
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
// could add support for buffering if we really want it...
_reportUnsupportedOperation();
}
@Override
public void writeUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException
{
// could add support for buffering if we really want it...
_reportUnsupportedOperation();
}
@Override
public void writeRaw(String text) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
@Override
public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
@Override
public void writeRaw(char c) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
@Override
public void writeRawValue(String text) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
@Override
public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
@Override
public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
_reportUnsupportedOperation();
}
/*
/**********************************************************
/* JsonGenerator implementation: write methods, primitive types
/**********************************************************
*/
@Override
public void writeNumber(int i) throws IOException, JsonGenerationException {
_append(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i));
}
@Override
public void writeNumber(long l) throws IOException, JsonGenerationException {
_append(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l));
}
@Override
public void writeNumber(double d) throws IOException,JsonGenerationException {
_append(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d));
}
@Override
public void writeNumber(float f) throws IOException, JsonGenerationException {
_append(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f));
}
@Override
public void writeNumber(BigDecimal dec) throws IOException,JsonGenerationException {
if (dec == null) {
writeNull();
} else {
_append(JsonToken.VALUE_NUMBER_FLOAT, dec);
}
}
@Override
public void writeNumber(BigInteger v) throws IOException, JsonGenerationException {
if (v == null) {
writeNull();
} else {
_append(JsonToken.VALUE_NUMBER_INT, v);
}
}
@Override
public void writeNumber(String encodedValue) throws IOException, JsonGenerationException {
/* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric
* identity as long as possible
*/
_append(JsonToken.VALUE_NUMBER_FLOAT, encodedValue);
}
@Override
public void writeBoolean(boolean state) throws IOException,JsonGenerationException {
_append(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE);
}
@Override
public void writeNull() throws IOException, JsonGenerationException {
_append(JsonToken.VALUE_NULL);
}
/*
/***********************************************************
/* JsonGenerator implementation: write methods for POJOs/trees
/***********************************************************
*/
@Override
public void writeObject(Object value)
throws IOException, JsonProcessingException
{
// embedded means that no conversions should be done...
_append(JsonToken.VALUE_EMBEDDED_OBJECT, value);
}
@Override
public void writeTree(JsonNode rootNode)
throws IOException, JsonProcessingException
{
/* 31-Dec-2009, tatu: no need to convert trees either is there?
* (note: may need to re-evaluate at some point)
*/
_append(JsonToken.VALUE_EMBEDDED_OBJECT, rootNode);
}
/*
/***********************************************************
/* JsonGenerator implementation; binary
/***********************************************************
*/
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
/* 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);
}
/*
/**********************************************************
/* JsonGenerator implementation; pass-through copy
/**********************************************************
*/
@Override
public void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException
{
switch (jp.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(jp.getCurrentName());
break;
case VALUE_STRING:
if (jp.hasTextCharacters()) {
writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
} else {
writeString(jp.getText());
}
break;
case VALUE_NUMBER_INT:
switch (jp.getNumberType()) {
case INT:
writeNumber(jp.getIntValue());
break;
case BIG_INTEGER:
writeNumber(jp.getBigIntegerValue());
break;
default:
writeNumber(jp.getLongValue());
}
break;
case VALUE_NUMBER_FLOAT:
switch (jp.getNumberType()) {
case BIG_DECIMAL:
writeNumber(jp.getDecimalValue());
break;
case FLOAT:
writeNumber(jp.getFloatValue());
break;
default:
writeNumber(jp.getDoubleValue());
}
break;
case VALUE_TRUE:
writeBoolean(true);
break;
case VALUE_FALSE:
writeBoolean(false);
break;
case VALUE_NULL:
writeNull();
break;
case VALUE_EMBEDDED_OBJECT:
writeObject(jp.getEmbeddedObject());
break;
default:
throw new RuntimeException("Internal error: should never end up through this code path");
}
}
@Override
public void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException {
JsonToken t = jp.getCurrentToken();
// Let's handle field-name separately first
if (t == JsonToken.FIELD_NAME) {
writeFieldName(jp.getCurrentName());
t = jp.nextToken();
// fall-through to copy the associated value
}
switch (t) {
case START_ARRAY:
writeStartArray();
while (jp.nextToken() != JsonToken.END_ARRAY) {
copyCurrentStructure(jp);
}
writeEndArray();
break;
case START_OBJECT:
writeStartObject();
while (jp.nextToken() != JsonToken.END_OBJECT) {
copyCurrentStructure(jp);
}
writeEndObject();
break;
default: // others are simple:
copyCurrentEvent(jp);
}
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected final void _append(JsonToken type) {
Segment next = _last.append(_appendOffset, type);
if (next == null) {
++_appendOffset;
} else {
_last = next;
_appendOffset = 1; // since we added first at 0
}
}
protected final void _append(JsonToken type, Object value) {
Segment next = _last.append(_appendOffset, type, value);
if (next == null) {
++_appendOffset;
} else {
_last = next;
_appendOffset = 1;
}
}
protected void _reportUnsupportedOperation() {
throw new UnsupportedOperationException("Called operation not supported for TokenBuffer");
}
/*
/**********************************************************
/* Supporting classes
/**********************************************************
*/
protected final static class Parser
extends JsonParserMinimalBase
{
protected ObjectCodec _codec;
/*
/**********************************************************
/* 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)
{
super(0);
_segment = firstSeg;
_segmentPtr = -1; // not yet read
_codec = codec;
_parsingContext = JsonReadContext.createRootContext(-1, -1);
}
public void setLocation(JsonLocation l) {
_location = l;
}
@Override
public ObjectCodec getCodec() { return _codec; }
@Override
public void setCodec(ObjectCodec c) { _codec = c; }
/*
/**********************************************************
/* Extended API beyond JsonParser
/**********************************************************
*/
public JsonToken peekNextToken()
throws IOException, JsonParseException
{
// 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, JsonParseException
{
// 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(-1, -1);
}
}
return _currToken;
}
@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() { return _parsingContext.getCurrentName(); }
/*
/**********************************************************
/* 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();
}
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, JsonParseException
{
Number n = getNumberValue();
if (n instanceof BigInteger) {
return (BigInteger) n;
}
switch (getNumberType()) {
case 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, JsonParseException
{
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);
}
// float or double
return BigDecimal.valueOf(n.doubleValue());
}
@Override
public double getDoubleValue() throws IOException, JsonParseException {
return getNumberValue().doubleValue();
}
@Override
public float getFloatValue() throws IOException, JsonParseException {
return getNumberValue().floatValue();
}
@Override
public int getIntValue() throws IOException, JsonParseException
{
// optimize common case:
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
return ((Number) _currentObject()).intValue();
}
return getNumberValue().intValue();
}
@Override
public long getLongValue() throws IOException, JsonParseException {
return getNumberValue().longValue();
}
@Override
public NumberType getNumberType() throws IOException, JsonParseException
{
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 Float) return NumberType.FLOAT;
if (n instanceof BigInteger) return NumberType.BIG_INTEGER;
return null;
}
@Override
public final Number getNumberValue() throws IOException, JsonParseException {
_checkIsNumber();
return (Number) _currentObject();
}
/*
/**********************************************************
/* Public API, access to token information, other
/**********************************************************
*/
@Override
public Object getEmbeddedObject()
{
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
return _currentObject();
}
return null;
}
@Override
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);
}
_decodeBase64(str, builder, b64variant);
return builder.toByteArray();
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant)
throws IOException, JsonParseException
{
int ptr = 0;
int len = str.length();
main_loop:
while (ptr < len) {
// first, we'll skip preceding white space, if any
char ch;
do {
ch = str.charAt(ptr++);
if (ptr >= len) {
break main_loop;
}
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
_reportInvalidBase64(b64variant, ch, 0, null);
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (ptr >= len) {
_reportBase64EOF();
}
ch = str.charAt(ptr++);
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
_reportInvalidBase64(b64variant, ch, 1, null);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (ptr >= len) {
// but as per [JACKSON-631] can be end-of-input, iff not using padding
if (!b64variant.usesPadding()) {
decodedData >>= 4;
builder.append(decodedData);
break;
}
_reportBase64EOF();
}
ch = str.charAt(ptr++);
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
_reportInvalidBase64(b64variant, ch, 2, null);
}
// Ok, must get padding
if (ptr >= len) {
_reportBase64EOF();
}
ch = str.charAt(ptr++);
if (!b64variant.usesPaddingChar(ch)) {
_reportInvalidBase64(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
builder.append(decodedData);
continue;
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (ptr >= len) {
// but as per [JACKSON-631] can be end-of-input, iff not using padding
if (!b64variant.usesPadding()) {
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
break;
}
_reportBase64EOF();
}
ch = str.charAt(ptr++);
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
_reportInvalidBase64(b64variant, ch, 3, null);
}
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
} else {
// otherwise, our triple is now complete
decodedData = (decodedData << 6) | bits;
builder.appendThreeBytes(decodedData);
}
}
}
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");
}
}
/**
* @param bindex Relative index within base64 character unit; between 0
* and 3 (as unit has exactly 4 characters)
*/
protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg)
throws JsonParseException
{
String base;
if (ch <= INT_SPACE) {
base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
} else if (b64variant.usesPaddingChar(ch)) {
base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
} else {
base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
}
if (msg != null) {
base = base + ": " + msg;
}
throw _constructError(base);
}
protected void _reportBase64EOF() throws JsonParseException {
throw _constructError("Unexpected end-of-String in base64 content");
}
@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 <= 16 values in JsonToken enum
TOKEN_TYPES_BY_INDEX = new JsonToken[16];
JsonToken[] t = JsonToken.values();
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];
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 Object get(int index) {
return _tokens[index];
}
public Segment next() { return _next; }
// // // 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 value)
{
if (index < TOKENS_PER_SEGMENT) {
set(index, tokenType, value);
return null;
}
_next = new Segment();
_next.set(0, tokenType, value);
return _next;
}
public void set(int index, JsonToken tokenType)
{
long typeCode = tokenType.ordinal();
/* Assumption here is that there are no overwrites, just appends;
* and so no masking is needed
*/
if (index > 0) {
typeCode <<= (index << 2);
}
_tokenTypes |= typeCode;
}
public void set(int index, JsonToken tokenType, Object value)
{
_tokens[index] = value;
long typeCode = tokenType.ordinal();
/* Assumption here is that there are no overwrites, just appends;
* and so no masking is needed
*/
if (index > 0) {
typeCode <<= (index << 2);
}
_tokenTypes |= typeCode;
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/util/CharTypes.java 0000644 0001750 0001750 00000017325 11655120726 025673 0 ustar jamespage jamespage package org.codehaus.jackson.util;
import java.util.Arrays;
import org.codehaus.jackson.io.CharacterEscapes;
public final class CharTypes
{
private final static char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
private final static byte[] HEX_BYTES;
static {
int len = HEX_CHARS.length;
HEX_BYTES = new byte[len];
for (int i = 0; i < len; ++i) {
HEX_BYTES[i] = (byte) HEX_CHARS[i];
}
}
/**
* Lookup table used for determining which input characters
* need special handling when contained in text segment.
*/
final static int[] sInputCodes;
static {
/* 96 would do for most cases (backslash is ascii 94)
* but if we want to do lookups by raw bytes it's better
* to have full table
*/
int[] table = new int[256];
// Control chars and non-space white space are not allowed unquoted
for (int i = 0; i < 32; ++i) {
table[i] = -1;
}
// And then string end and quote markers are special too
table['"'] = 1;
table['\\'] = 1;
sInputCodes = table;
}
/**
* Additionally we can combine UTF-8 decoding info into similar
* data table.
*/
final static int[] sInputCodesUtf8;
static {
int[] table = new int[sInputCodes.length];
System.arraycopy(sInputCodes, 0, table, 0, sInputCodes.length);
for (int c = 128; c < 256; ++c) {
int code;
// We'll add number of bytes needed for decoding
if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
code = 2;
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
code = 3;
} else if ((c & 0xF8) == 0xF0) {
// 4 bytes; double-char with surrogates and all...
code = 4;
} else {
// And -1 seems like a good "universal" error marker...
code = -1;
}
table[c] = code;
}
sInputCodesUtf8 = table;
}
/**
* To support non-default (and -standard) unquoted field names mode,
* need to have alternate checking.
* Basically this is list of 8-bit ASCII characters that are legal
* as part of Javascript identifier
*
* @since 1.2
*/
final static int[] sInputCodesJsNames;
static {
int[] table = new int[256];
// Default is "not a name char", mark ones that are
Arrays.fill(table, -1);
// Assume rules with JS same as Java (change if/as needed)
for (int i = 33; i < 256; ++i) {
if (Character.isJavaIdentifierPart((char) i)) {
table[i] = 0;
}
}
/* As per [JACKSON-267], '@', '#' and '*' are also to be accepted as well.
* And '-' (for hyphenated names); and '+' for sake of symmetricity...
*/
table['@'] = 0;
table['#'] = 0;
table['*'] = 0;
table['-'] = 0;
table['+'] = 0;
sInputCodesJsNames = table;
}
/**
* This table is similar to Latin-1, except that it marks all "high-bit"
* code as ok. They will be validated at a later point, when decoding
* name
*/
final static int[] sInputCodesUtf8JsNames;
static {
int[] table = new int[256];
// start with 8-bit JS names
System.arraycopy(sInputCodesJsNames, 0, table, 0, sInputCodesJsNames.length);
Arrays.fill(table, 128, 128, 0);
sInputCodesUtf8JsNames = table;
}
/**
* Decoding table used to quickly determine characters that are
* relevant within comment content
*/
final static int[] sInputCodesComment = new int[256];
static {
// but first: let's start with UTF-8 multi-byte markers:
System.arraycopy(sInputCodesUtf8, 128, sInputCodesComment, 128, 128);
// default (0) means "ok" (skip); -1 invalid, others marked by char itself
Arrays.fill(sInputCodesComment, 0, 32, -1); // invalid white space
sInputCodesComment['\t'] = 0; // tab is still fine
sInputCodesComment['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment
sInputCodesComment['\r'] = '\r';
sInputCodesComment['*'] = '*'; // end marker for c-style comments
}
/**
* Lookup table used for determining which output characters in
* 7-bit ASCII range need to be quoted.
*/
final static int[] sOutputEscapes128;
static {
int[] table = new int[128];
// Control chars need generic escape sequence
for (int i = 0; i < 32; ++i) {
// 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constants
table[i] = CharacterEscapes.ESCAPE_STANDARD;
}
/* Others (and some within that range too) have explicit shorter
* sequences
*/
table['"'] = '"';
table['\\'] = '\\';
// Escaping of slash is optional, so let's not add it
table[0x08] = 'b';
table[0x09] = 't';
table[0x0C] = 'f';
table[0x0A] = 'n';
table[0x0D] = 'r';
sOutputEscapes128 = table;
}
/**
* Lookup table for the first 128 Unicode characters (7-bit ASCII)
* range. For actual hex digits, contains corresponding value;
* for others -1.
*/
final static int[] sHexValues = new int[128];
static {
Arrays.fill(sHexValues, -1);
for (int i = 0; i < 10; ++i) {
sHexValues['0' + i] = i;
}
for (int i = 0; i < 6; ++i) {
sHexValues['a' + i] = 10 + i;
sHexValues['A' + i] = 10 + i;
}
}
public final static int[] getInputCodeLatin1() { return sInputCodes; }
public final static int[] getInputCodeUtf8() { return sInputCodesUtf8; }
public final static int[] getInputCodeLatin1JsNames() { return sInputCodesJsNames; }
public final static int[] getInputCodeUtf8JsNames() { return sInputCodesUtf8JsNames; }
public final static int[] getInputCodeComment() { return sInputCodesComment; }
/**
* Accessor for getting a read-only encoding table for first 128 Unicode
* code points (single-byte UTF-8 characters).
* Value of 0 means "no escaping"; other positive values that value is character
* to use after backslash; and negative values that generic (backslash - u)
* escaping is to be used.
*/
public final static int[] get7BitOutputEscapes() { return sOutputEscapes128; }
public static int charToHex(int ch)
{
return (ch > 127) ? -1 : sHexValues[ch];
}
public static void appendQuoted(StringBuilder sb, String content)
{
final int[] escCodes = sOutputEscapes128;
int escLen = escCodes.length;
for (int i = 0, len = content.length(); i < len; ++i) {
char c = content.charAt(i);
if (c >= escLen || escCodes[c] == 0) {
sb.append(c);
continue;
}
sb.append('\\');
int escCode = escCodes[c];
if (escCode < 0) { // generic quoting (hex value)
// We know that it has to fit in just 2 hex chars
sb.append('u');
sb.append('0');
sb.append('0');
int value = -(escCode + 1);
sb.append(HEX_CHARS[value >> 4]);
sb.append(HEX_CHARS[value & 0xF]);
} else { // "named", i.e. prepend with slash
sb.append((char) escCode);
}
}
}
/**
* @since 1.6
*/
public static char[] copyHexChars()
{
return (char[]) HEX_CHARS.clone();
}
/**
* @since 1.6
*/
public static byte[] copyHexBytes()
{
return (byte[]) HEX_BYTES.clone();
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/PrettyPrinter.java 0000644 0001750 0001750 00000013546 11655120726 025650 0 ustar jamespage jamespage package org.codehaus.jackson;
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.
*/
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).
*/
public 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.
*/
public void writeStartObject(JsonGenerator jg)
throws IOException, JsonGenerationException;
/**
* Method called after an Object value has been completely output
* (minus closing curly bracket).
*
* Default handling (without pretty-printing) will output
* the closing curly bracket.
* Pretty-printer is
* to output a curly bracket as well, but can surround that
* with other (white-space) decoration.
*
* @param nrOfEntries Number of direct members of the array that
* have been output
*/
public void writeEndObject(JsonGenerator jg, int nrOfEntries)
throws IOException, JsonGenerationException;
/**
* Method called after an object entry (field:value) has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
public void writeObjectEntrySeparator(JsonGenerator jg)
throws IOException, JsonGenerationException;
/**
* Method called after an object field has been output, but
* before the value is output.
*
* Default handling (without pretty-printing) will output a single
* colon to separate the two. Pretty-printer is
* to output a colon as well, but can surround that with other
* (white-space) decoration.
*/
public void writeObjectFieldValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException;
// // // Array handling
/**
* Method called when an Array value is to be output, before
* any member/child values are output.
*
* Default handling (without pretty-printing) will output
* the opening bracket.
* Pretty-printer is
* to output a bracket as well, but can surround that
* with other (white-space) decoration.
*/
public void writeStartArray(JsonGenerator jg)
throws IOException, JsonGenerationException;
/**
* Method called after an Array value has been completely output
* (minus closing bracket).
*
* Default handling (without pretty-printing) will output
* the closing bracket.
* Pretty-printer is
* to output a bracket as well, but can surround that
* with other (white-space) decoration.
*
* @param nrOfValues Number of direct members of the array that
* have been output
*/
public void writeEndArray(JsonGenerator jg, int nrOfValues)
throws IOException, JsonGenerationException;
/**
* Method called after an array value has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
public void writeArrayValueSeparator(JsonGenerator jg)
throws IOException, JsonGenerationException;
/*
/**********************************************************
/* Then events that by default do not produce any output
/* but that are often overridden to add white space
/* in pretty-printing mode
/**********************************************************
*/
/**
* Method called after array start marker has been output,
* and right before the first value is to be output.
* It is not called for arrays with no values.
*
* Default handling does not output anything, but pretty-printer
* is free to add any white space decoration.
*/
public void beforeArrayValues(JsonGenerator jg)
throws IOException, JsonGenerationException;
/**
* Method called after object start marker has been output,
* and right before the field name of the first entry is
* to be output.
* It is not called for objects without entries.
*
* Default handling does not output anything, but pretty-printer
* is free to add any white space decoration.
*/
public void beforeObjectEntries(JsonGenerator jg)
throws IOException, JsonGenerationException;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonStreamContext.java 0000644 0001750 0001750 00000007664 11655120726 026453 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson;
/**
* Shared base class for streaming processing contexts used during
* reading and writing of Json content using Streaming API.
* This context is also exposed to applications:
* context object can be used by applications to get an idea of
* relative position of the parser/generator within json content
* being processed. This allows for some contextual processing: for
* example, output within Array context can differ from that of
* Object context.
*/
public abstract class JsonStreamContext
{
// // // Type constants used internally
protected final static int TYPE_ROOT = 0;
protected final static int TYPE_ARRAY = 1;
protected final static int TYPE_OBJECT = 2;
protected int _type;
/**
* Index of the currently processed entry. Starts with -1 to signal
* that no entries have been started, and gets advanced each
* time a new entry is started, either by encountering an expected
* separator, or with new values if no separators are expected
* (the case for root context).
*/
protected int _index;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected JsonStreamContext() { }
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
/**
* Accessor for finding parent context of this context; will
* return null for root context.
*/
public abstract JsonStreamContext getParent();
/**
* Method that returns true if this context is an Array context;
* that is, content is being read from or written to a Json Array.
*/
public final boolean inArray() { return _type == TYPE_ARRAY; }
/**
* Method that returns true if this context is a Root context;
* that is, content is being read from or written to without
* enclosing array or object structure.
*/
public final boolean inRoot() { return _type == TYPE_ROOT; }
/**
* Method that returns true if this context is an Object context;
* that is, content is being read from or written to a Json Object.
*/
public final boolean inObject() { return _type == TYPE_OBJECT; }
/**
* Method for accessing simple type description of current context;
* either ROOT (for root-level values), OBJECT (for field names and
* values of JSON Objects) or ARRAY (for values of JSON Arrays)
*/
public final String getTypeDesc() {
switch (_type) {
case TYPE_ROOT: return "ROOT";
case TYPE_ARRAY: return "ARRAY";
case TYPE_OBJECT: return "OBJECT";
}
return "?";
}
/**
* @return Number of entries that are complete and started.
*/
public final int getEntryCount()
{
return _index + 1;
}
/**
* @return Index of the currently processed entry, if any
*/
public final int getCurrentIndex()
{
return (_index < 0) ? 0 : _index;
}
/**
* Method for accessing name associated with the current location.
* Non-null for FIELD_NAME
and value events that directly
* follow field names; null for root level and array values.
*/
public abstract String getCurrentName();
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonProcessingException.java 0000644 0001750 0001750 00000003721 11655120726 027634 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* 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 mLocation;
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);
}
mLocation = loc;
}
protected JsonProcessingException(String msg)
{
super(msg);
}
protected JsonProcessingException(String msg, JsonLocation loc)
{
this(msg, loc, null);
}
protected JsonProcessingException(String msg, Throwable rootCause)
{
this(msg, null, rootCause);
}
protected JsonProcessingException(Throwable rootCause)
{
this(null, null, rootCause);
}
public JsonLocation getLocation()
{
return mLocation;
}
/**
* 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();
if (loc != null) {
StringBuilder sb = new StringBuilder();
sb.append(msg);
sb.append('\n');
sb.append(" at ");
sb.append(loc.toString());
return sb.toString();
}
return msg;
}
@Override
public String toString() {
return getClass().getName()+": "+getMessage();
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/package-info.java 0000644 0001750 0001750 00000002162 11655120726 025331 0 ustar jamespage jamespage /**
* Main public API classes of the core streaming JSON
* processor: most importantly {@link org.codehaus.jackson.JsonFactory}
* used for constructing
* JSON parser ({@link org.codehaus.jackson.JsonParser})
* and generator
* ({@link org.codehaus.jackson.JsonParser})
* instances.
*
* Public API of the higher-level mapping interfaces ("Mapping API")
* is found from
* under {@link org.codehaus.jackson.map} and not included here,
* except for following base interfaces:
*
*{@link org.codehaus.jackson.JsonNode} is included
*within Streaming API to support integration of the Tree Model
*(which is based on JsonNode
) with the basic
*parsers and generators (iff using mapping-supporting factory: which
*is part of Mapping API, not core)
*
*{@link org.codehaus.jackson.ObjectCodec} is included so that
* reference to the object capable of serializing/deserializing
* Objects to/from JSON (usually, {@link org.codehaus.jackson.map.ObjectMapper})
* can be exposed, without adding direct dependency to implementation.
*
*
*
*/
package org.codehaus.jackson;
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonNode.java 0000644 0001750 0001750 00000076331 11655120726 024535 0 ustar jamespage jamespage package org.codehaus.jackson;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
/**
* 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 org.codehaus.jackson.node.ObjectNode
* and org.codehaus.jackson.node.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 org.codehaus.jackson.node}, which is in 'mapper' jar
* (whereas this class is in 'core' jar, since it is declared as
* nominal type for operations in {@link ObjectCodec})
*/
public abstract class JsonNode
implements Iterable
{
protected final static List NO_NODES = Collections.emptyList();
protected final static List NO_STRINGS = Collections.emptyList();
protected JsonNode() { }
/*
/**********************************************************
/* Public API, type introspection
/**********************************************************
*/
// // First high-level division between values, containers and "missing"
/**
* 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.
*/
public boolean isValueNode() { return false; }
/**
* 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.
*/
public boolean isContainerNode() { return false; }
/**
* 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.
*/
public boolean isMissingNode() { return false; }
// // Then more specific type introspection
// // (along with defaults to be overridden)
/**
* @return True if this node represents Json Array
*/
public boolean isArray() { return false; }
/**
* @return True if this node represents Json Object
*/
public boolean isObject() { return false; }
/**
* 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 {@link org.codehaus.jackson.node.POJONode}.
*
* @return True if this node wraps a POJO
*/
public boolean isPojo() { return false; }
/**
* @return True if this node represents a numeric Json
* value
*/
public boolean isNumber() { return false; }
/**
* @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; }
/**
* @return True if this node represents an integral
* numeric Json value that withs in Java int value space
*/
public boolean isInt() { return false; }
/**
* @return True if this node represents an integral
* numeric Json value that fits in Java long value space
* (but not int value space, i.e. {@link #isInt} returns false)
*/
public boolean isLong() { return false; }
public boolean isDouble() { return false; }
public boolean isBigDecimal() { return false; }
public boolean isBigInteger() { return false; }
public boolean isTextual() { return false; }
/**
* Method that can be used to check if this node was created from
* Json boolean value (literals "true" and "false").
*/
public boolean isBoolean() { return false; }
/**
* Method that can be used to check if this node was created from
* Json liternal null value.
*/
public boolean isNull() { return false; }
/**
* 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 boolean isBinary() { return false; }
/**
* 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)
*
* @since 1.3
*/
public abstract JsonToken asToken();
/**
* If this node is a numeric type (as per {@link #isNumber}),
* returns native type that node uses to store the numeric
* value.
*/
public abstract JsonParser.NumberType getNumberType();
/*
/**********************************************************
/* 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 getTextValue() { 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 #getTextValue} 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[] getBinaryValue() 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 getBooleanValue() { 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 getNumberValue() { return null; }
/**
* 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 getIntValue() { return 0; }
public long getLongValue() { return 0L; }
public double getDoubleValue() { return 0.0; }
public BigDecimal getDecimalValue() { return BigDecimal.ZERO; }
public BigInteger getBigIntegerValue() { return BigInteger.ZERO; }
/**
* 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.
*
* @return Node that represent value of the specified element,
* if this node is an array and has specified element.
* Null otherwise.
*/
public JsonNode get(int index) { return null; }
/**
* 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.
*
* @return Node that represent value of the specified field,
* if this node is an object and has value for the specified
* field. Null otherwise.
*/
public JsonNode get(String fieldName) { return null; }
/*
/**********************************************************
/* Public API, value access with conversion(s)/coercion(s)
/**********************************************************
*/
/**
* Method that will return valid String representation of
* the container value, if the node is a value node
* (method {@link #isValueNode} returns true), otherwise
* empty String.
*
* @since 1.9 (replaces getValueAsText
)
*/
public abstract String asText();
/**
* 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.
*
* @since 1.9 (replaces getValueAsInt
)
*/
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.
*
* @since 1.9 (replaces getValueAsInt
)
*/
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.
*
* @since 1.9 (replaces getValueAsLong
)
*/
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.
*
* @since 1.9 (replaces getValueAsLong
)
*/
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.
*
* @since 1.9 (replaces getValueAsDouble
)
*/
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.
*
* @since 1.9 (replaces getValueAsLong
)
*/
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.
*
* @since 1.9 (replaces getValueAsBoolean
)
*/
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.
*
* @since 1.9 (replaces getValueAsBoolean
)
*/
public boolean asBoolean(boolean defaultValue) {
return defaultValue;
}
/*
/**********************************************************
/* Public API, value access with conversion(s)/coercion(s)
/**********************************************************
*/
/**
* Method that will return valid String representation of
* the container value, if the node is a value node
* (method {@link #isValueNode} returns true), otherwise null.
*
* Note: to serialize nodes of any type, you should call
* {@link #toString} instead.
*
* @deprecated Since 1.9, use {@link #asText} instead
*/
@Deprecated
public String getValueAsText() { return asText(); }
/**
* 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.
*
* @since 1.6
*
* @deprecated Since 1.9, use {@link #asInt} instead
*/
@Deprecated
public int getValueAsInt() { 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.
*
* @since 1.6
*
* @deprecated Since 1.9, use {@link #asInt} instead
*/
@Deprecated
public int getValueAsInt(int defaultValue) { return asInt(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.
*
* @since 1.6
*
* @deprecated Since 1.9, use {@link #asLong} instead
*/
@Deprecated
public long getValueAsLong() { 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.
*
* @since 1.6
*
* @deprecated Since 1.9, use {@link #asLong} instead
*/
@Deprecated
public long getValueAsLong(long defaultValue) { return asLong(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.
*
* @since 1.6
*
* @deprecated Since 1.9, use {@link #asDouble} instead
*/
@Deprecated
public double getValueAsDouble() { 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.
*
* @since 1.6
*
* @deprecated Since 1.9, use {@link #asDouble} instead
*/
@Deprecated
public double getValueAsDouble(double defaultValue) { return asDouble(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.
*
* @since 1.7
*
* @deprecated Since 1.9, use {@link #asBoolean} instead
*/
@Deprecated
public boolean getValueAsBoolean() { 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.
*
* @since 1.7
*
* @deprecated Since 1.9, use {@link #asBoolean} instead
*/
@Deprecated
public boolean getValueAsBoolean(boolean defaultValue) { return asBoolean(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)
*
* @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)
*
* @since 1.6
*/
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
*
*
* @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)
*
* @since 1.6
*/
public boolean has(int index) {
return get(index) != null;
}
/**
* 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
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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 #getValueAsText}.
*
* @since 1.6
*/
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)
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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, container access
/**********************************************************
*/
/**
* 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.
*/
public int size() { return 0; }
/**
* Same as calling {@link #getElements}; implemented so that
* convenience "for-each" loop can be used for looping over elements
* of JSON Array constructs.
*/
@Override
public final Iterator iterator() { return getElements(); }
/**
* 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 getElements() { return NO_NODES.iterator(); }
/**
* Method for accessing names of all fields for this Node, iff
* this node is a JSON Object node.
*/
public Iterator getFieldNames() { return NO_STRINGS.iterator(); }
/**
* @return Iterator that can be used to traverse all key/value pairs for
* object nodes; empty iterator (no contents) for other types
*
* @since 1.8 (although existed in ObjectNode since 1.0 or so)
*/
public Iterator> getFields() {
Collection> coll = Collections.emptyList();
return coll.iterator();
}
/*
/**********************************************************
/* Public API, path handling
/**********************************************************
*/
/**
* 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.
*/
public abstract JsonNode path(String fieldName);
/**
* Alias of {@link #path(String)}.
*
* @deprecated Use {@link #path(String)} instead
*/
@Deprecated
public final JsonNode getPath(String fieldName) { return path(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.
*/
public abstract JsonNode path(int index);
/**
* Alias of {@link #path(int)}.
*
* @deprecated Use {@link #path(int)} instead
*/
@Deprecated
public final JsonNode getPath(int index) { return path(index); }
/**
* Method that can be called on object nodes, to access a property
* that has object value; or if no such property exists, to create and
* return such object node.
* If 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
*
* @since 1.8
*/
public JsonNode with(String propertyName) {
throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
+getClass().getName()+"), can not call with() on it");
}
/*
/**********************************************************
/* Public API: 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.
*/
public abstract JsonParser traverse();
/*
/**********************************************************
/* Overridden standard methods
/**********************************************************
*/
/**
*
* Note: marked as abstract to ensure all implementation
* classes define it properly.
*/
@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);
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonParser.java 0000644 0001750 0001750 00000147472 11655120726 025111 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import org.codehaus.jackson.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;
private final static int MAX_BYTE_I = (int) Byte.MAX_VALUE;
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 togglable 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 (via factory or parser instance).
*
* This feature can be changed for parser instances.
*/
ALLOW_COMMENTS(false),
/**
* Feature that determines whether parser will allow use
* of unquoted field names (which is allowed by Javascript,
* but not by JSON specification).
*
* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by
* default.
*
* This feature can be changed for parser instances.
*
* @since 1.2
*/
ALLOW_UNQUOTED_FIELD_NAMES(false),
/**
* Feature that determines whether parser will allow use
* of single quotes (apostrophe, character '\'') for
* quoting Strings (names and String values). If so,
* this is in addition to other acceptabl markers.
* but not by JSON specification).
*
* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by
* default.
*
* This feature can be changed for parser instances.
*
* @since 1.3
*/
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.
*
* This feature can be changed for parser instances.
*
* @since 1.4
*/
ALLOW_UNQUOTED_CONTROL_CHARS(false),
/**
* Feature that can be enabled to accept quoting of all character
* using backslash qooting mechanism: if not enabled, only characters
* that are explicitly listed by JSON specification can be thus
* escaped (see JSON spec for small list of these characters)
*
* Since JSON specification requires quoting for all control characters,
* this is a non-standard feature, and as such disabled by default.
*
* This feature can be changed for parser instances.
*
* @since 1.6
*/
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.
*
* This feature can be changed for parser instances.
*
* @since 1.8
*/
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):
*
* "INF" (for positive infinity), as well as alias of "Infinity"
* "-INF" (for negative infinity), alias "-Infinity"
* "NaN" (for other not-a-numbers, like result of division by zero)
*
*/
ALLOW_NON_NUMERIC_NUMBERS(false),
// // // Controlling canonicalization (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.
*
* @since 1.3
*/
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}).
*
* @since 1.5
*/
CANONICALIZE_FIELD_NAMES(true),
;
final boolean _defaultState;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (Feature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
private Feature(boolean defaultState) {
_defaultState = defaultState;
}
public boolean enabledByDefault() { return _defaultState; }
public boolean enabledIn(int flags) { return (flags & getMask()) != 0; }
public int getMask() { return (1 << ordinal()); }
};
/*
/**********************************************************
/* Minimal configuration state
/**********************************************************
*/
/**
* Bit flag composed of bits that indicate which
* {@link org.codehaus.jackson.JsonParser.Feature}s
* are enabled.
*/
protected int _features;
/*
/**********************************************************
/* Minimal generic state
/**********************************************************
*/
/**
* Last token retrieved via {@link #nextToken}, if any.
* Null before the first call to nextToken()
,
* as well as if token has been explicitly cleared
* (by call to {@link #clearCurrentToken})
*/
protected JsonToken _currToken;
/**
* Last cleared token, if any: that is, value that was in
* effect when {@link #clearCurrentToken} was called.
*/
protected JsonToken _lastClearedToken;
/*
/**********************************************************
/* 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).
*
* @since 1.3
*/
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).
*
* @since 1.3
*/
public abstract void setCodec(ObjectCodec c);
/**
* 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
*
* @since 1.8
*/
public void setSchema(FormatSchema schema)
{
throw new UnsupportedOperationException("Parser of type "+getClass().getName()+" does not support schema of type '"
+schema.getSchemaType()+"'");
}
/**
* 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
*
* @since 1.8
*/
public boolean canUseSchema(FormatSchema schema) {
return false;
}
/**
* @since 1.6
*/
@Override
public Version version() {
return Version.unknownVersion();
}
/**
* 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.
*
* @since 1.8
*/
public Object getInputSource() {
return null;
}
/*
/**********************************************************
/* 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 org.codehaus.jackson.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
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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)
*
* @since 1.2
*/
public JsonParser enable(Feature f)
{
_features |= f.getMask();
return this;
}
/**
* Method for disabling specified feature
* (check {@link Feature} for list of features)
*
* @since 1.2
*/
public JsonParser disable(Feature f)
{
_features &= ~f.getMask();
return this;
}
/**
* Method for enabling or disabling specified feature
* (check {@link Feature} for list of features)
*
* @since 1.2
*/
public JsonParser configure(Feature f, boolean state)
{
if (state) {
enableFeature(f);
} else {
disableFeature(f);
}
return this;
}
/**
* Method for checking whether specified {@link Feature}
* is enabled.
*
* @since 1.2
*/
public boolean isEnabled(Feature f) {
return (_features & f.getMask()) != 0;
}
/** @deprecated Use {@link #configure} instead
*/
@SuppressWarnings("dep-ann")
public void setFeature(Feature f, boolean state) { configure(f, state); }
/** @deprecated Use {@link #enable(Feature)} instead
*/
@SuppressWarnings("dep-ann")
public void enableFeature(Feature f) { enable(f); }
/** @deprecated Use {@link #disable(Feature)} instead
*/
@SuppressWarnings("dep-ann")
public void disableFeature(Feature f) { disable(f); }
/** @deprecated Use {@link #isEnabled(Feature)} instead
*/
@SuppressWarnings("dep-ann")
public final boolean isFeatureEnabled(Feature f) { return isEnabled(f); }
/*
/**********************************************************
/* 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 JsonToken nextValue()
throws IOException, JsonParseException
{
/* Implementation should be as trivial as follows; only
* needs to change if we are to skip other tokens (for
* example, if comments were exposed as tokens)
*/
JsonToken t = nextToken();
if (t == JsonToken.FIELD_NAME) {
t = nextToken();
}
return t;
}
/**
* 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)
*
* @since 1.9
*/
public boolean nextFieldName(SerializableString str)
throws IOException, JsonParseException
{
return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
}
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* if it is {@link JsonToken#VALUE_STRING} returns contained String value;
* otherwise returns null.
* It is functionally equivalent to:
*
* return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
*
* but may be faster for parser to process, and can therefore be used if caller
* expects to get a String value next from input.
*
* @since 1.9
*/
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.
*
* @since 1.9
*/
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.
*
* @since 1.9
*/
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.
*
* @since 1.9
*/
public Boolean nextBooleanValue()
throws IOException, JsonParseException
{
switch (nextToken()) {
case VALUE_TRUE:
return Boolean.TRUE;
case 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 JsonToken getCurrentToken() {
return _currToken;
}
/**
* 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 boolean hasCurrentToken() {
return _currToken != null;
}
/**
* 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 void clearCurrentToken() {
if (_currToken != null) {
_lastClearedToken = _currToken;
_currToken = null;
}
}
/**
* Method that can be called to get the name associated with
* the current token: for {@link JsonToken#FIELD_NAME}s it will
* be the same as what {@link #getText} returns;
* for field values it will be preceding field name;
* and for others (array values, root-level values) null.
*/
public abstract String getCurrentName()
throws IOException, JsonParseException;
/**
* Method that can be used to access current parsing context reader
* is in. There are 3 different types: root, array and object contexts,
* with slightly different available information. Contexts are
* hierarchically nested, and can be used for example for figuring
* out part of the input document that correspond to specific
* array or object (for highlighting purposes, or error reporting).
* Contexts can also be used for simple xpath-like matching of
* input, if so desired.
*/
public abstract JsonStreamContext getParsingContext();
/**
* Method that return the starting location of the current
* token; that is, position of the first character from input
* that starts the current token.
*/
public abstract JsonLocation getTokenLocation();
/**
* Method that returns location of the last processed character;
* usually for error reporting purposes.
*/
public abstract JsonLocation getCurrentLocation();
/**
* 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 JsonToken getLastClearedToken() {
return _lastClearedToken;
}
/**
* Specialized accessor that can be used to verify that the current
* token indicates start array (usually meaning that current token
* is {@link JsonToken#START_ARRAY}) when start array is expected.
* For some specialized parsers this can return true for other cases
* as well; this is usually done to emulate arrays.
*
* Default implementation is equivalent to:
*
* getCurrentToken() == JsonToken.START_ARRAY
*
* but may be overridden by custom parser implementations.
*
* @return True if the current token can be considered as a
* start-array marker (such {@link JsonToken#START_ARRAY});
* false if not.
*
* @since 1.7
*/
public boolean isExpectedStartArrayToken() {
return getCurrentToken() == JsonToken.START_ARRAY;
}
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
/**
* Method for accessing textual representation of the current token;
* if no current token (before first call to {@link #nextToken}, or
* after encountering end-of-input), returns null.
* Method can be called for any token type.
*/
public abstract String getText()
throws IOException, JsonParseException;
/**
* Method similar to {@link #getText}, but that will return
* underlying (unmodifiable) character array that contains
* textual value, instead of constructing a String object
* to contain this information.
* Note, however, that:
*
* Textual contents are not guaranteed to start at
* index 0 (rather, call {@link #getTextOffset}) to
* know the actual offset
*
* Length of textual contents may be less than the
* length of returned buffer: call {@link #getTextLength}
* for actual length of returned content.
*
*
*
* Note that caller MUST NOT modify the returned
* character array in any way -- doing so may corrupt
* current parser state and render parser instance useless.
*
* The only reason to call this method (over {@link #getText})
* is to avoid construction of a String object (which
* will make a copy of contents).
*/
public abstract char[] getTextCharacters()
throws IOException, JsonParseException;
/**
* Accessor used with {@link #getTextCharacters}, to know length
* of String stored in returned buffer.
*
* @return Number of characters within buffer returned
* by {@link #getTextCharacters} that are part of
* textual content of the current token.
*/
public abstract int getTextLength()
throws IOException, JsonParseException;
/**
* Accessor used with {@link #getTextCharacters}, to know offset
* of the first text content character within buffer.
*
* @return Offset of the first character within buffer returned
* by {@link #getTextCharacters} that is part of
* textual content of the current token.
*/
public abstract int getTextOffset()
throws IOException, JsonParseException;
/**
* Method that can be used to determine whether calling of
* {@link #getTextCharacters} would be the most efficient
* way to access textual content for the event parser currently
* points to.
*
* Default implementation simply returns false since only actual
* implementation class has knowledge of its internal buffering
* state.
* Implementations are strongly encouraged to properly override
* this method, to allow efficient copying of content by other
* code.
*
* @return True if parser currently has character array that can
* be efficiently returned via {@link #getTextCharacters}; false
* means that it may or may not exist
*
* @since 1.6
*/
public boolean hasTextCharacters() {
return false;
}
/*
/**********************************************************
/* Public API, access to token information, numeric
/**********************************************************
*/
/**
* Generic number value accessor method that will work for
* all kinds of numeric values. It will return the optimal
* (simplest/smallest possible) wrapper object that can
* express the numeric value just parsed.
*/
public abstract Number getNumberValue()
throws IOException, JsonParseException;
/**
* If current token is of type
* {@link JsonToken#VALUE_NUMBER_INT} or
* {@link JsonToken#VALUE_NUMBER_FLOAT}, returns
* one of {@link NumberType} constants; otherwise returns null.
*/
public abstract NumberType getNumberType()
throws IOException, JsonParseException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a value of Java byte primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the resulting integer value falls outside range of
* Java byte, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public byte getByteValue()
throws IOException, JsonParseException
{
int value = getIntValue();
// So far so good: but does it fit?
if (value < MIN_BYTE_I || value > MAX_BYTE_I) {
throw _constructError("Numeric value ("+getText()+") out of range of Java byte");
}
return (byte) value;
}
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a value of Java short primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the resulting integer value falls outside range of
* Java short, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public short getShortValue()
throws IOException, JsonParseException
{
int value = getIntValue();
if (value < MIN_SHORT_I || value > MAX_SHORT_I) {
throw _constructError("Numeric value ("+getText()+") out of range of Java short");
}
return (short) value;
}
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a value of Java int primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the resulting integer value falls outside range of
* Java int, a {@link JsonParseException}
* may be thrown to indicate numeric overflow/underflow.
*/
public abstract int getIntValue()
throws IOException, JsonParseException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a Java long primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting to int; except for possible overflow/underflow
* exception.
*
* Note: if the token is an integer, but its value falls
* outside of range of Java long, a {@link JsonParseException}
* may be thrown to indicate numeric overflow/underflow.
*/
public abstract long getLongValue()
throws IOException, JsonParseException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can not be used as a Java long primitive type due to its
* magnitude.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDecimalValue}
* and then constructing a {@link BigInteger} from that value.
*/
public abstract BigInteger getBigIntegerValue()
throws IOException, JsonParseException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and
* it can be expressed as a Java float primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_INT};
* if so, it is equivalent to calling {@link #getLongValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the value falls
* outside of range of Java float, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public abstract float getFloatValue()
throws IOException, JsonParseException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and
* it can be expressed as a Java double primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_INT};
* if so, it is equivalent to calling {@link #getLongValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the value falls
* outside of range of Java double, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public abstract double getDoubleValue()
throws IOException, JsonParseException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} or
* {@link JsonToken#VALUE_NUMBER_INT}. No under/overflow exceptions
* are ever thrown.
*/
public abstract BigDecimal getDecimalValue()
throws IOException, JsonParseException;
/*
/**********************************************************
/* Public API, access to token information, other
/**********************************************************
*/
/**
* Convenience accessor that can be called when the current
* token is {@link JsonToken#VALUE_TRUE} or
* {@link JsonToken#VALUE_FALSE}.
*
* Note: if the token is not of above-mentioned boolean types,
an integer, but its value falls
* outside of range of Java long, a {@link JsonParseException}
* may be thrown to indicate numeric overflow/underflow.
*
* @since 1.3
*/
public boolean getBooleanValue()
throws IOException, JsonParseException
{
if (_currToken == JsonToken.VALUE_TRUE) return true;
if (_currToken == JsonToken.VALUE_FALSE) return false;
throw new JsonParseException("Current token ("+_currToken+") not of boolean type", getCurrentLocation());
}
/**
* Accessor that can be called if (and only if) the current token
* is {@link JsonToken#VALUE_EMBEDDED_OBJECT}. For other token types,
* null is returned.
*
* Note: only some specialized parser implementations support
* embedding of objects (usually ones that are facades on top
* of non-streaming sources, such as object trees).
*
* @since 1.3
*/
public Object getEmbeddedObject()
throws IOException, JsonParseException
{
// By default we will always return null
return null;
}
/*
/**********************************************************
/* Public API, access to token information, binary
/**********************************************************
*/
/**
* Method that can be used to read (and consume -- results
* may not be accessible using other methods after the call)
* base64-encoded binary data
* included in the current textual JSON value.
* It works similar to getting String value via {@link #getText}
* and decoding result (except for decoding part),
* but should be significantly more performant.
*
* Note that non-decoded textual contents of the current token
* are not guaranteed to be accessible after this method
* is called. Current implementation, for example, clears up
* textual content during decoding.
* Decoded binary content, however, will be retained until
* parser is advanced to the next event.
*
* @param b64variant Expected variant of base64 encoded
* content (see {@link Base64Variants} for definitions
* of "standard" variants).
*
* @return Decoded binary data
*/
public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException;
/**
* Convenience alternative to {@link #getBinaryValue(Base64Variant)}
* that defaults to using
* {@link Base64Variants#getDefaultVariant} as the default encoding.
*/
public byte[] getBinaryValue() throws IOException, JsonParseException
{
return getBinaryValue(Base64Variants.getDefaultVariant());
}
/*
/**********************************************************
/* 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.
*
* @since 1.6
*/
public int getValueAsInt() throws IOException, JsonParseException {
return getValueAsInt(0);
}
/**
* Method that will try to convert value of current token to a
* int .
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured type
* markers like start/end Object/Array)
* specified defaultValue will be returned; no exceptions are thrown.
*
* @since 1.6
*/
public int getValueAsInt(int defaultValue) throws IOException, JsonParseException {
return defaultValue;
}
/**
* Method that will try to convert value of current token to a
* long .
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured type
* markers like start/end Object/Array)
* default value of 0 will be returned; no exceptions are thrown.
*
* @since 1.6
*/
public long getValueAsLong() throws IOException, JsonParseException {
return getValueAsInt(0);
}
/**
* Method that will try to convert value of current token to a
* long .
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured type
* markers like start/end Object/Array)
* specified defaultValue will be returned; no exceptions are thrown.
*
* @since 1.6
*/
public long getValueAsLong(long defaultValue) throws IOException, JsonParseException {
return defaultValue;
}
/**
* Method that will try to convert value of current token to a Java
* double .
* Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
* and 1.0 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured types
* like Objects and Arrays),
* default value of 0.0 will be returned; no exceptions are thrown.
*
* @since 1.6
*/
public double getValueAsDouble() throws IOException, JsonParseException {
return getValueAsDouble(0.0);
}
/**
* Method that will try to convert value of current token to a
* Java double .
* Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
* and 1.0 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured types
* like Objects and Arrays),
* specified defaultValue will be returned; no exceptions are thrown.
*
* @since 1.6
*/
public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException {
return defaultValue;
}
/**
* Method that will try to convert value of current token to a
* boolean .
* JSON booleans map naturally; integer numbers other than 0 map to true, and
* 0 maps to false
* and Strings 'true' and 'false' map to corresponding values.
*
* If representation can not be converted to a boolean value (including structured types
* like Objects and Arrays),
* default value of false will be returned; no exceptions are thrown.
*
* @since 1.7
*/
public boolean getValueAsBoolean() throws IOException, JsonParseException {
return getValueAsBoolean(false);
}
/**
* Method that will try to convert value of current token to a
* boolean .
* JSON booleans map naturally; integer numbers other than 0 map to true, and
* 0 maps to false
* and Strings 'true' and 'false' map to corresponding values.
*
* If representation can not be converted to a boolean value (including structured types
* like Objects and Arrays),
* specified defaultValue will be returned; no exceptions are thrown.
*
* @since 1.7
*/
public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException {
return defaultValue;
}
/*
/**********************************************************
/* 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 {@link org.codehaus.jackson.map.MappingJsonFactory} but
* not for {@link JsonFactory} (unless its setCodec
* method has been explicitly called).
*
* This method may advance the event stream, for structured types
* the current token will be the closing end marker (END_ARRAY,
* END_OBJECT) of the bound structure. For non-structured Json types
* (and for {@link JsonToken#VALUE_EMBEDDED_OBJECT})
* stream is not advanced.
*
* Note: this method should NOT be used if the result type is a
* container ({@link java.util.Collection} or {@link java.util.Map}.
* The reason is that due to type erasure, key and value types
* can not be introspected when using this method.
*/
public T readValueAs(Class valueType)
throws IOException, JsonProcessingException
{
ObjectCodec codec = getCodec();
if (codec == null) {
throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
}
return codec.readValue(this, valueType);
}
/**
* Method to deserialize JSON content into a Java type, reference
* to which is passed as argument. Type is passed using so-called
* "super type token"
* and specifically needs to be used if the root type is a
* parameterized (generic) container type.
* Note : method can only be called if the parser has
* an object codec assigned; this is true for parsers constructed
* by {@link org.codehaus.jackson.map.MappingJsonFactory} but
* not for {@link JsonFactory} (unless its setCodec
* method has been explicitly called).
*
* This method may advance the event stream, for structured types
* the current token will be the closing end marker (END_ARRAY,
* END_OBJECT) of the bound structure. For non-structured Json types
* (and for {@link JsonToken#VALUE_EMBEDDED_OBJECT})
* stream is not advanced.
*/
@SuppressWarnings("unchecked")
public T readValueAs(TypeReference> valueTypeRef)
throws IOException, JsonProcessingException
{
ObjectCodec codec = getCodec();
if (codec == null) {
throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
}
/* Ugh. Stupid Java type erasure... can't just chain call,s
* must cast here also.
*/
return (T) codec.readValue(this, valueTypeRef);
}
/**
* Method for reading sequence of Objects from parser stream,
* all with same specified value type.
*
* @since 1.9
*/
public Iterator readValuesAs(Class valueType)
throws IOException, JsonProcessingException
{
ObjectCodec codec = getCodec();
if (codec == null) {
throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
}
return codec.readValues(this, valueType);
}
/**
* Method for reading sequence of Objects from parser stream,
* all with same specified value type.
*
* @since 1.9
*/
public Iterator readValuesAs(TypeReference> valueTypeRef)
throws IOException, JsonProcessingException
{
ObjectCodec codec = getCodec();
if (codec == null) {
throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
}
return codec.readValues(this, valueTypeRef);
}
/**
* Method to deserialize JSON content into equivalent "tree model",
* represented by root {@link JsonNode} 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
*/
public JsonNode readValueAsTree()
throws IOException, JsonProcessingException
{
ObjectCodec codec = getCodec();
if (codec == null) {
throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into JsonNode tree");
}
return codec.readTree(this);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
/**
* Helper method for constructing {@link JsonParseException}s
* based on current state of the parser
*/
protected JsonParseException _constructError(String msg)
{
return new JsonParseException(msg, getCurrentLocation());
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/ 0000755 0001750 0001750 00000000000 11672662540 022755 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/CharsToNameCanonicalizer.java 0000644 0001750 0001750 00000046276 11655120726 030501 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
import java.util.Arrays;
import org.codehaus.jackson.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 (esp. 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 (ie. no modifications done).
*/
public final class CharsToNameCanonicalizer
{
/**
* Default initial table size. Shouldn't be miniscule (as there's
* cost to both array realloc and rehashing), but let's keep
* it reasonably small nonetheless. For systems that properly
* reuse factories it doesn't matter either way; but when
* recreating factories often, initial overhead may dominate.
*/
protected static final int DEFAULT_TABLE_SIZE = 64;
/**
* Let's not expand symbol tables past some maximum size;
* this should protected against OOMEs caused by large documents
* with uniquer (~= random) names.
*
* @since 1.5
*/
protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem
/**
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k;
* this corresponds to 64k main hash index. This should allow for enough distinct
* names for almost any case.
*/
final static int MAX_ENTRIES_FOR_REUSE = 12000;
final static CharsToNameCanonicalizer sBootstrapSymbolTable;
static {
sBootstrapSymbolTable = new CharsToNameCanonicalizer();
}
/*
/****************************************
/* Configuration:
/****************************************
*/
/**
* Sharing of learnt symbols is done by optional linking of symbol
* table instances with their parents. When parent linkage is
* defined, and child instance is released (call to release
),
* parent's shared tables may be updated from the child instance.
*/
protected CharsToNameCanonicalizer _parent;
/**
* Whether canonical symbol Strings are to be intern()ed before added
* to the table or not
*/
final protected boolean _intern;
/**
* Whether any canonicalization should be attempted (whether using
* intern or not)
*/
final protected boolean _canonicalize;
/*
/****************************************
/* Actual symbol table data:
/****************************************
*/
/**
* Primary matching symbols; it's expected most match occur from
* here.
*/
protected String[] _symbols;
/**
* Overflow buckets; if primary doesn't match, lookup is done
* from here.
*
* Note: Number of buckets is half of number of symbol entries, on
* assumption there's less need for buckets.
*/
protected Bucket[] _buckets;
/**
* Current size (number of entries); needed to know if and when
* rehash.
*/
protected int _size;
/**
* Limit that indicates maximum size this instance can hold before
* it needs to be expanded and rehashed. Calculated using fill
* factor passed in to constructor.
*/
protected int _sizeThreshold;
/**
* Mask used to get index from hash values; equal to
* _buckets.length - 1
, when _buckets.length is
* a power of two.
*/
protected int _indexMask;
/*
/****************************************
/* State regarding shared arrays
/****************************************
*/
/**
* Flag that indicates if any changes have been made to the data;
* used to both determine if bucket array needs to be copied when
* (first) change is made, and potentially if updated bucket list
* is to be resync'ed back to master instance.
*/
protected boolean _dirty;
/*
/****************************************
/* Life-cycle
/****************************************
*/
/**
* Method called to create root canonicalizer for a {@link org.codehaus.jackson.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()
{
return sBootstrapSymbolTable.makeOrphan();
}
/**
* Main method for constructing a master symbol table instance.
*
* @param initialSize Minimum initial size for bucket array; internally
* will always use a power of two equal to or bigger than this value.
*/
private CharsToNameCanonicalizer()
{
// these settings don't really matter for the bootstrap instance
_canonicalize = true;
_intern = true;
// And we'll also set flags so no copying of buckets is needed:
_dirty = true;
initTables(DEFAULT_TABLE_SIZE);
}
private void initTables(int initialSize)
{
_symbols = new String[initialSize];
_buckets = new Bucket[initialSize >> 1];
// Mask is easy to calc for powers of two.
_indexMask = initialSize - 1;
_size = 0;
// Hard-coded fill factor is 75%
_sizeThreshold = (initialSize - (initialSize >> 2));
}
/**
* Internal constructor used when creating child instances.
*/
private CharsToNameCanonicalizer(CharsToNameCanonicalizer parent,
boolean canonicalize, boolean intern,
String[] symbols, Bucket[] buckets, int size)
{
_parent = parent;
_canonicalize = canonicalize;
_intern = intern;
_symbols = symbols;
_buckets = buckets;
_size = size;
// Hard-coded fill factor, 75%
int arrayLen = (symbols.length);
_sizeThreshold = arrayLen - (arrayLen >> 2);
_indexMask = (arrayLen - 1);
// 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 synchronized CharsToNameCanonicalizer makeChild(boolean canonicalize, boolean intern)
{
return new CharsToNameCanonicalizer(this, canonicalize, intern, _symbols, _buckets, _size);
}
private CharsToNameCanonicalizer makeOrphan()
{
return new CharsToNameCanonicalizer(null, true, true, _symbols, _buckets, _size);
}
/**
* 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 synchronized 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:
initTables(DEFAULT_TABLE_SIZE);
} 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!
_symbols = child._symbols;
_buckets = child._buckets;
_size = child._size;
_sizeThreshold = child._sizeThreshold;
_indexMask = child._indexMask;
}
/* Dirty flag... well, let's just clear it, to force copying just
* in case. Shouldn't really matter, for master tables.
* (which this is, given something is merged to it etc)
*/
_dirty = false;
}
public void release()
{
// If nothing has been added, nothing to do
if (!maybeDirty()) {
return;
}
if (_parent != null) {
_parent.mergeChild(this);
/* Let's also mark this instance as dirty, so that just in
* case release was too early, there's no corruption
* of possibly shared data.
*/
_dirty = false;
}
}
/*
/****************************************
/* Public API, generic accessors:
/****************************************
*/
public int size() { return _size; }
public boolean maybeDirty() { return _dirty; }
/*
/****************************************
/* Public API, accessing symbols:
/****************************************
*/
public String findSymbol(char[] buffer, int start, int len, int hash)
{
if (len < 1) { // empty Strings are simplest to handle up front
return "";
}
if (!_canonicalize) { // [JACKSON-259]
return new String(buffer, start, len);
}
hash &= _indexMask;
String sym = _symbols[hash];
// Optimal case; checking existing primary symbol for hash index:
if (sym != null) {
// Let's inline primary String equality checking:
if (sym.length() == len) {
int i = 0;
do {
if (sym.charAt(i) != buffer[start+i]) {
break;
}
} while (++i < len);
// Optimal case; primary match found
if (i == len) {
return sym;
}
}
// How about collision bucket?
Bucket b = _buckets[hash >> 1];
if (b != null) {
sym = b.find(buffer, start, len);
if (sym != null) {
return sym;
}
}
}
if (!_dirty) { //need to do copy-on-write?
copyArrays();
_dirty = true;
} else if (_size >= _sizeThreshold) { // Need to expand?
rehash();
/* Need to recalc hash; rare occurence (index mask has been
* recalculated as part of rehash)
*/
hash = calcHash(buffer, start, len) & _indexMask;
}
++_size;
String newSymbol = new String(buffer, start, len);
if (_intern) {
newSymbol = InternCache.instance.intern(newSymbol);
}
// Ok; do we need to add primary entry, or a bucket?
if (_symbols[hash] == null) {
_symbols[hash] = newSymbol;
} else {
int bix = hash >> 1;
_buckets[bix] = new Bucket(newSymbol, _buckets[bix]);
}
return newSymbol;
}
/**
* 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 static int calcHash(char[] buffer, int start, int len) {
int hash = (int) buffer[0];
for (int i = 1; i < len; ++i) {
hash = (hash * 31) + (int) buffer[i];
}
return hash;
}
public static int calcHash(String key) {
int hash = (int) key.charAt(0);
for (int i = 1, len = key.length(); i < len; ++i) {
hash = (hash * 31) + (int) key.charAt(i);
}
return hash;
}
/*
/****************************************
/* Internal methods
/****************************************
*/
/**
* Method called when copy-on-write is needed; generally when first
* change is made to a derived symbol table.
*/
private void copyArrays() {
String[] oldSyms = _symbols;
int size = oldSyms.length;
_symbols = new String[size];
System.arraycopy(oldSyms, 0, _symbols, 0, size);
Bucket[] oldBuckets = _buckets;
size = oldBuckets.length;
_buckets = new Bucket[size];
System.arraycopy(oldBuckets, 0, _buckets, 0, size);
}
/**
* Method called when size (number of entries) of symbol table grows
* so big that load factor is exceeded. Since size has to remain
* power of two, arrays will then always be doubled. Main work
* is really redistributing old entries into new String/Bucket
* entries.
*/
private void rehash()
{
int size = _symbols.length;
int newSize = size + size;
/* 12-Mar-2010, tatu: Let's actually limit maximum size we are
* prepared to use, to guard against OOME in case of unbounded
* name sets (unique [non-repeating] names)
*/
if (newSize > MAX_TABLE_SIZE) {
/* If this happens, there's no point in either growing or
* shrinking hash areas. Rather, it's better to just clean
* them up for reuse.
*/
_size = 0;
Arrays.fill(_symbols, null);
Arrays.fill(_buckets, null);
_dirty = true;
return;
}
String[] oldSyms = _symbols;
Bucket[] oldBuckets = _buckets;
_symbols = new String[newSize];
_buckets = new Bucket[newSize >> 1];
// Let's update index mask, threshold, now (needed for rehashing)
_indexMask = newSize - 1;
_sizeThreshold += _sizeThreshold;
int count = 0; // let's do sanity check
/* Need to do two loops, unfortunately, since spill-over area is
* only half the size:
*/
for (int i = 0; i < size; ++i) {
String symbol = oldSyms[i];
if (symbol != null) {
++count;
int index = calcHash(symbol) & _indexMask;
if (_symbols[index] == null) {
_symbols[index] = symbol;
} else {
int bix = index >> 1;
_buckets[bix] = new Bucket(symbol, _buckets[bix]);
}
}
}
size >>= 1;
for (int i = 0; i < size; ++i) {
Bucket b = oldBuckets[i];
while (b != null) {
++count;
String symbol = b.getSymbol();
int index = calcHash(symbol) & _indexMask;
if (_symbols[index] == null) {
_symbols[index] = symbol;
} else {
int bix = index >> 1;
_buckets[bix] = new Bucket(symbol, _buckets[bix]);
}
b = b.getNext();
}
}
if (count != _size) {
throw new Error("Internal error on SymbolTable.rehash(): had "+_size+" entries; now have "+count+".");
}
}
/*
/****************************************
/* Bucket class
/****************************************
*/
/**
* This class is a symbol table entry. Each entry acts as a node
* in a linked list.
*/
static final class Bucket {
private final String _symbol;
private final Bucket mNext;
public Bucket(String symbol, Bucket next) {
_symbol = symbol;
mNext = next;
}
public String getSymbol() { return _symbol; }
public Bucket getNext() { return mNext; }
public String find(char[] buf, int start, int len) {
String sym = _symbol;
Bucket b = mNext;
while (true) { // Inlined equality comparison:
if (sym.length() == len) {
int i = 0;
do {
if (sym.charAt(i) != buf[start+i]) {
break;
}
} while (++i < len);
if (i == len) {
return sym;
}
}
if (b == null) {
break;
}
sym = b.getSymbol();
b = b.getNext();
}
return null;
}
/* 26-Nov-2008, tatu: not used currently; if not used in near future,
* let's just delete it.
*/
/*
public String find(String str) {
String sym = _symbol;
Bucket b = mNext;
while (true) {
if (sym.equals(str)) {
return sym;
}
if (b == null) {
break;
}
sym = b.getSymbol();
b = b.getNext();
}
return null;
}
*/
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/Name.java 0000644 0001750 0001750 00000002415 11655120726 024476 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
/**
* Base class for tokenized names (key strings in objects) that have
* been tokenized from byte-based input sources (like
* {@link java.io.InputStream}.
*
* @author Tatu Saloranta
*/
public abstract class Name
{
protected final String _name;
protected final int _hashCode;
protected Name(String name, int hashCode) {
_name = name;
_hashCode = hashCode;
}
public String getName() { return _name; }
/*
/**********************************************************
/* Methods for package/core parser
/**********************************************************
*/
public abstract boolean equals(int quad1);
public abstract boolean equals(int quad1, int quad2);
public abstract boolean equals(int[] quads, int qlen);
/*
/**********************************************************
/* Overridden standard methods
/**********************************************************
*/
@Override public String toString() { return _name; }
@Override public final int hashCode() { return _hashCode; }
@Override public boolean equals(Object o)
{
// Canonical instances, can usually just do identity comparison
return (o == this);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/NameN.java 0000644 0001750 0001750 00000003454 11655120726 024620 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
/**
* Generic implementation of PName used for "long" names, where long
* means that its byte (UTF-8) representation is 13 bytes or more.
*/
public final class NameN
extends Name
{
final int[] mQuads;
final int mQuadLen;
NameN(String name, int hash, int[] quads, int quadLen)
{
super(name, hash);
/* We have specialized implementations for shorter
* names, so let's not allow runt instances here
*/
if (quadLen < 3) {
throw new IllegalArgumentException("Qlen must >= 3");
}
mQuads = quads;
mQuadLen = quadLen;
}
// Implies quad length == 1, never matches
@Override
public boolean equals(int quad) { return false; }
// Implies quad length == 2, never matches
@Override
public boolean equals(int quad1, int quad2) { return false; }
@Override
public boolean equals(int[] quads, int qlen)
{
if (qlen != mQuadLen) {
return false;
}
/* 26-Nov-2008, tatus: Strange, but it does look like
* unrolling here is counter-productive, reducing
* speed. Perhaps it prevents inlining by HotSpot or
* something...
*/
// Will always have >= 3 quads, can unroll
/*
if (quads[0] == mQuads[0]
&& quads[1] == mQuads[1]
&& quads[2] == mQuads[2]) {
for (int i = 3; i < qlen; ++i) {
if (quads[i] != mQuads[i]) {
return false;
}
}
return true;
}
*/
// or simpler way without unrolling:
for (int i = 0; i < qlen; ++i) {
if (quads[i] != mQuads[i]) {
return false;
}
}
return true;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/Name2.java 0000644 0001750 0001750 00000001747 11655120726 024567 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
/**
* Specialized implementation of PName: can be used for short Strings
* that consists of 5 to 8 bytes. Usually this means relatively short
* ascii-only names.
*
* The reason for such specialized classes is mostly space efficiency;
* and to a lesser degree performance. Both are achieved for short
* Strings by avoiding another level of indirection (via quad arrays)
*/
public final class Name2
extends Name
{
final int mQuad1;
final int mQuad2;
Name2(String name, int hash, int quad1, int quad2)
{
super(name, hash);
mQuad1 = quad1;
mQuad2 = quad2;
}
@Override
public boolean equals(int quad) { return false; }
@Override
public boolean equals(int quad1, int quad2)
{
return (quad1 == mQuad1) && (quad2 == mQuad2);
}
@Override
public boolean equals(int[] quads, int qlen)
{
return (qlen == 2 && quads[0] == mQuad1 && quads[1] == mQuad2);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/package-info.java 0000644 0001750 0001750 00000000226 11655120726 026140 0 ustar jamespage jamespage /**
* Internal implementation classes for efficient handling of
* of symbols in JSON (field names in Objects)
*/
package org.codehaus.jackson.sym;
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/Name1.java 0000644 0001750 0001750 00000002026 11655120726 024555 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
/**
* Specialized implementation of PName: can be used for short Strings
* that consists of at most 4 bytes. Usually this means short
* ascii-only names.
*
* The reason for such specialized classes is mostly space efficiency;
* and to a lesser degree performance. Both are achieved for short
* Strings by avoiding another level of indirection (via quad arrays)
*/
public final class Name1
extends Name
{
final static Name1 sEmptyName = new Name1("", 0, 0);
final int mQuad;
Name1(String name, int hash, int quad)
{
super(name, hash);
mQuad = quad;
}
final static Name1 getEmptyName() { return sEmptyName; }
@Override
public boolean equals(int quad)
{
return (quad == mQuad);
}
@Override
public boolean equals(int quad1, int quad2)
{
return (quad1 == mQuad) && (quad2 == 0);
}
@Override
public boolean equals(int[] quads, int qlen)
{
return (qlen == 1 && quads[0] == mQuad);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/BytesToNameCanonicalizer.java 0000644 0001750 0001750 00000075215 11655120726 030522 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
import java.util.Arrays;
import org.codehaus.jackson.util.InternCache;
/**
* This class is basically a caching symbol table implementation used for
* canonicalizing {@link Name}s, constructed directly from a byte-based
* input source.
*
* @author Tatu Saloranta
*/
public final class BytesToNameCanonicalizer
{
protected static final int DEFAULT_TABLE_SIZE = 64;
/**
* Let's not expand symbol tables past some maximum size;
* this should protected against OOMEs caused by large documents
* with uniquer (~= random) names.
*
* @since 1.5
*/
protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem
/**
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k;
* this corresponds to 64k main hash index. This should allow for enough distinct
* names for almost any case.
*/
final static int MAX_ENTRIES_FOR_REUSE = 6000;
final static int MIN_HASH_SIZE = 16;
final static int INITIAL_COLLISION_LEN = 32;
/**
* Bucket index is 8 bits, and value 0 is reserved to represent
* 'empty' status.
*/
final static int LAST_VALID_BUCKET = 0xFE;
/*
/**********************************************************
/* Linkage, needed for merging symbol tables
/**********************************************************
*/
final BytesToNameCanonicalizer _parent;
/*
/**********************************************************
/* Main table state
/**********************************************************
*/
/**
* Whether canonial symbol Strings are to be intern()ed before added
* to the table or not
*/
final boolean _intern;
// // // First, global information
/**
* Total number of Names in the symbol table
*/
private int _count;
// // // Then information regarding primary hash array and its
// // // matching Name array
/**
* Mask used to truncate 32-bit hash value to current hash array
* size; essentially, hash array size - 1 (since hash array sizes
* are 2^N).
*/
private int _mainHashMask;
/**
* Array of 2^N size, which contains combination
* of 24-bits of hash (0 to indicate 'empty' slot),
* and 8-bit collision bucket index (0 to indicate empty
* collision bucket chain; otherwise subtract one from index)
*/
private int[] _mainHash;
/**
* Array that contains Name
instances matching
* entries in _mainHash
. Contains nulls for unused
* entries.
*/
private Name[] _mainNames;
// // // Then the collision/spill-over area info
/**
* Array of heads of collision bucket chains; size dynamically
*/
private Bucket[] _collList;
/**
* Total number of Names in collision buckets (included in
* _count
along with primary entries)
*/
private int _collCount;
/**
* Index of the first unused collision bucket entry (== size of
* the used portion of collision list): less than
* or equal to 0xFF (255), since max number of entries is 255
* (8-bit, minus 0 used as 'empty' marker)
*/
private int _collEnd;
// // // Info regarding pending rehashing...
/**
* This flag is set if, after adding a new entry, it is deemed
* that a rehash is warranted if any more entries are to be added.
*/
private transient boolean _needRehash;
/*
/**********************************************************
/* Sharing, versioning
/**********************************************************
*/
// // // Which of the buffers may be shared (and are copy-on-write)?
/**
* Flag that indicates whether underlying data structures for
* the main hash area are shared or not. If they are, then they
* need to be handled in copy-on-write way, i.e. if they need
* to be modified, a copy needs to be made first; at this point
* it will not be shared any more, and can be modified.
*
* This flag needs to be checked both when adding new main entries,
* and when adding new collision list queues (i.e. creating a new
* collision list head entry)
*/
private boolean _mainHashShared;
private boolean _mainNamesShared;
/**
* Flag that indicates whether underlying data structures for
* the collision list are shared or not. If they are, then they
* need to be handled in copy-on-write way, i.e. if they need
* to be modified, a copy needs to be made first; at this point
* it will not be shared any more, and can be modified.
*
* This flag needs to be checked when adding new collision entries.
*/
private boolean _collListShared;
/*
/**********************************************************
/* Construction, merging
/**********************************************************
*/
public static BytesToNameCanonicalizer createRoot()
{
return new BytesToNameCanonicalizer(DEFAULT_TABLE_SIZE, true);
}
/**
* @param intern Whether canonical symbol Strings should be interned
* or not
*/
public synchronized BytesToNameCanonicalizer makeChild(boolean canonicalize,
boolean intern)
{
return new BytesToNameCanonicalizer(this, intern);
}
/**
* Method called by the using code to indicate it is done
* with this instance. This lets instance merge accumulated
* changes into parent (if need be), safely and efficiently,
* and without calling code having to know about parent
* information
*/
public void release()
{
if (maybeDirty() && _parent != null) {
_parent.mergeChild(this);
/* Let's also mark this instance as dirty, so that just in
* case release was too early, there's no corruption
* of possibly shared data.
*/
markAsShared();
}
}
private BytesToNameCanonicalizer(int hashSize, boolean intern)
{
_parent = null;
_intern = intern;
/* Sanity check: let's now allow hash sizes below certain
* min. value
*/
if (hashSize < MIN_HASH_SIZE) {
hashSize = MIN_HASH_SIZE;
} else {
/* Also; size must be 2^N; otherwise hash algorithm won't
* work... so let's just pad it up, if so
*/
if ((hashSize & (hashSize - 1)) != 0) { // only true if it's 2^N
int curr = MIN_HASH_SIZE;
while (curr < hashSize) {
curr += curr;
}
hashSize = curr;
}
}
initTables(hashSize);
}
/**
* Constructor used when creating a child instance
*/
private BytesToNameCanonicalizer(BytesToNameCanonicalizer parent, boolean intern)
{
_parent = parent;
_intern = intern;
// First, let's copy the state as is:
_count = parent._count;
_mainHashMask = parent._mainHashMask;
_mainHash = parent._mainHash;
_mainNames = parent._mainNames;
_collList = parent._collList;
_collCount = parent._collCount;
_collEnd = parent._collEnd;
_needRehash = false;
// And consider all shared, so far:
_mainHashShared = true;
_mainNamesShared = true;
_collListShared = true;
}
private void initTables(int hashSize)
{
_count = 0;
_mainHash = new int[hashSize];
_mainNames = new Name[hashSize];
_mainHashShared = false;
_mainNamesShared = false;
_mainHashMask = hashSize - 1;
_collListShared = true; // just since it'll need to be allocated
_collList = null;
_collEnd = 0;
_needRehash = false;
}
private synchronized void mergeChild(BytesToNameCanonicalizer child)
{
// Only makes sense if child has more entries
int childCount = child._count;
if (childCount <= _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 (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:
initTables(DEFAULT_TABLE_SIZE);
} else {
_count = child._count;
_mainHash = child._mainHash;
_mainNames = child._mainNames;
_mainHashShared = true; // shouldn't matter for parent
_mainNamesShared = true; // - "" -
_mainHashMask = child._mainHashMask;
_collList = child._collList;
_collCount = child._collCount;
_collEnd = child._collEnd;
}
}
private void markAsShared()
{
_mainHashShared = true;
_mainNamesShared = true;
_collListShared = true;
}
/*
/**********************************************************
/* API, accessors
/**********************************************************
*/
public int size() { return _count; }
/**
* Method called to check to quickly see if a child symbol table
* may have gotten additional entries. Used for checking to see
* if a child table should be merged into shared table.
*/
public boolean maybeDirty()
{
return !_mainHashShared;
}
public static Name getEmptyName()
{
return Name1.getEmptyName();
}
/**
* Finds and returns name matching the specified symbol, if such
* name already exists in the table.
* If not, will return null.
*
* Note: separate methods to optimize common case of
* short element/attribute names (4 or less ascii characters)
*
* @param firstQuad int32 containing first 4 bytes of the name;
* if the whole name less than 4 bytes, padded with zero bytes
* in front (zero MSBs, ie. right aligned)
*
* @return Name matching the symbol passed (or constructed for
* it)
*/
public Name findName(int firstQuad)
{
int hash = calcHash(firstQuad);
int ix = (hash & _mainHashMask);
int val = _mainHash[ix];
/* High 24 bits of the value are low 24 bits of hash (low 8 bits
* are bucket index)... match?
*/
if ((((val >> 8) ^ hash) << 8) == 0) { // match
// Ok, but do we have an actual match?
Name name = _mainNames[ix];
if (name == null) { // main slot empty; can't find
return null;
}
if (name.equals(firstQuad)) {
return name;
}
} else if (val == 0) { // empty slot? no match
return null;
}
// Maybe a spill-over?
val &= 0xFF;
if (val > 0) { // 0 means 'empty'
val -= 1; // to convert from 1-based to 0...
Bucket bucket = _collList[val];
if (bucket != null) {
return bucket.find(hash, firstQuad, 0);
}
}
// Nope, no match whatsoever
return null;
}
/**
* Finds and returns name matching the specified symbol, if such
* name already exists in the table.
* If not, will return null.
*
* Note: separate methods to optimize common case of relatively
* short element/attribute names (8 or less ascii characters)
*
* @param firstQuad int32 containing first 4 bytes of the name.
* @param secondQuad int32 containing bytes 5 through 8 of the
* name; if less than 8 bytes, padded with up to 3 zero bytes
* in front (zero MSBs, ie. right aligned)
*
* @return Name matching the symbol passed (or constructed for
* it)
*/
public Name findName(int firstQuad, int secondQuad)
{
int hash = calcHash(firstQuad, secondQuad);
int ix = (hash & _mainHashMask);
int val = _mainHash[ix];
/* High 24 bits of the value are low 24 bits of hash (low 8 bits
* are bucket index)... match?
*/
if ((((val >> 8) ^ hash) << 8) == 0) { // match
// Ok, but do we have an actual match?
Name name = _mainNames[ix];
if (name == null) { // main slot empty; can't find
return null;
}
if (name.equals(firstQuad, secondQuad)) {
return name;
}
} else if (val == 0) { // empty slot? no match
return null;
}
// Maybe a spill-over?
val &= 0xFF;
if (val > 0) { // 0 means 'empty'
val -= 1; // to convert from 1-based to 0...
Bucket bucket = _collList[val];
if (bucket != null) {
return bucket.find(hash, firstQuad, secondQuad);
}
}
// Nope, no match whatsoever
return null;
}
/**
* Finds and returns name matching the specified symbol, if such
* name already exists in the table; or if not, creates name object,
* adds to the table, and returns it.
*
* Note: this is the general purpose method that can be called for
* names of any length. However, if name is less than 9 bytes long,
* it is preferable to call the version optimized for short
* names.
*
* @param quads Array of int32s, each of which contain 4 bytes of
* encoded name
* @param qlen Number of int32s, starting from index 0, in quads
* parameter
*
* @return Name matching the symbol passed (or constructed for it)
*/
public Name findName(int[] quads, int qlen)
{
/* // Not needed, never gets called
if (qlen < 3) { // another sanity check
return findName(quads[0], (qlen < 2) ? 0 : quads[1]);
}
*/
int hash = calcHash(quads, qlen);
// (for rest of comments regarding logic, see method above)
int ix = (hash & _mainHashMask);
int val = _mainHash[ix];
if ((((val >> 8) ^ hash) << 8) == 0) {
Name name = _mainNames[ix];
if (name == null // main slot empty; no collision list then either
|| name.equals(quads, qlen)) { // should be match, let's verify
return name;
}
} else if (val == 0) { // empty slot? no match
return null;
}
val &= 0xFF;
if (val > 0) { // 0 means 'empty'
val -= 1; // to convert from 1-based to 0...
Bucket bucket = _collList[val];
if (bucket != null) {
return bucket.find(hash, quads, qlen);
}
}
return null;
}
/*
/**********************************************************
/* API, mutators
/**********************************************************
*/
/**
* @since 1.6.0
*/
public Name addName(String symbolStr, int q1, int q2)
{
if (_intern) {
symbolStr = InternCache.instance.intern(symbolStr);
}
int hash = (q2 == 0) ? calcHash(q1) : calcHash(q1, q2);
Name symbol = constructName(hash, symbolStr, q1, q2);
_addSymbol(hash, symbol);
return symbol;
}
public Name addName(String symbolStr, int[] quads, int qlen)
{
if (_intern) {
symbolStr = InternCache.instance.intern(symbolStr);
}
int hash = calcHash(quads, qlen);
Name symbol = constructName(hash, symbolStr, quads, qlen);
_addSymbol(hash, symbol);
return symbol;
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
public final static int calcHash(int firstQuad)
{
int hash = firstQuad;
hash ^= (hash >>> 16); // to xor hi- and low- 16-bits
hash ^= (hash >>> 8); // as well as lowest 2 bytes
return hash;
}
public final static int calcHash(int firstQuad, int secondQuad)
{
int hash = (firstQuad * 31) + secondQuad;
// If this was called for single-quad instance:
//int hash = (secondQuad == 0) ? firstQuad : ((firstQuad * 31) + secondQuad);
hash ^= (hash >>> 16); // to xor hi- and low- 16-bits
hash ^= (hash >>> 8); // as well as lowest 2 bytes
return hash;
}
public final static int calcHash(int[] quads, int qlen)
{
// Note: may be called for qlen < 3
int hash = quads[0];
for (int i = 1; i < qlen; ++i) {
hash = (hash * 31) + quads[i];
}
hash ^= (hash >>> 16); // to xor hi- and low- 16-bits
hash ^= (hash >>> 8); // as well as lowest 2 bytes
return hash;
}
/* 26-Nov-2008, tatu: not used currently; if not used in near future,
* let's just delete it.
*/
/*
public static int[] calcQuads(byte[] wordBytes)
{
int blen = wordBytes.length;
int[] result = new int[(blen + 3) / 4];
for (int i = 0; i < blen; ++i) {
int x = wordBytes[i] & 0xFF;
if (++i < blen) {
x = (x << 8) | (wordBytes[i] & 0xFF);
if (++i < blen) {
x = (x << 8) | (wordBytes[i] & 0xFF);
if (++i < blen) {
x = (x << 8) | (wordBytes[i] & 0xFF);
}
}
}
result[i >> 2] = x;
}
return result;
}
*/
/*
/**********************************************************
/* Standard methods
/**********************************************************
*/
/*
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("[BytesToNameCanonicalizer, size: ");
sb.append(_count);
sb.append('/');
sb.append(_mainHash.length);
sb.append(", ");
sb.append(_collCount);
sb.append(" coll; avg length: ");
// Average length: minimum of 1 for all (1 == primary hit);
// and then 1 per each traversal for collisions/buckets
//int maxDist = 1;
int pathCount = _count;
for (int i = 0; i < _collEnd; ++i) {
int spillLen = _collList[i].length();
for (int j = 1; j <= spillLen; ++j) {
pathCount += j;
}
}
double avgLength;
if (_count == 0) {
avgLength = 0.0;
} else {
avgLength = (double) pathCount / (double) _count;
}
// let's round up a bit (two 2 decimal places)
//avgLength -= (avgLength % 0.01);
sb.append(avgLength);
sb.append(']');
return sb.toString();
}
*/
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private void _addSymbol(int hash, Name symbol)
{
if (_mainHashShared) { // always have to modify main entry
unshareMain();
}
// First, do we need to rehash?
if (_needRehash) {
rehash();
}
++_count;
/* Ok, enough about set up: now we need to find the slot to add
* symbol in:
*/
int ix = (hash & _mainHashMask);
if (_mainNames[ix] == null) { // primary empty?
_mainHash[ix] = (hash << 8);
if (_mainNamesShared) {
unshareNames();
}
_mainNames[ix] = symbol;
} else { // nope, it's a collision, need to spill over
/* How about spill-over area... do we already know the bucket
* (is the case if it's not the first collision)
*/
if (_collListShared) {
unshareCollision(); // also allocates if list was null
}
++_collCount;
int entryValue = _mainHash[ix];
int bucket = entryValue & 0xFF;
if (bucket == 0) { // first spill over?
if (_collEnd <= LAST_VALID_BUCKET) { // yup, still unshared bucket
bucket = _collEnd;
++_collEnd;
// need to expand?
if (bucket >= _collList.length) {
expandCollision();
}
} else { // nope, have to share... let's find shortest?
bucket = findBestBucket();
}
// Need to mark the entry... and the spill index is 1-based
_mainHash[ix] = (entryValue & ~0xFF) | (bucket + 1);
} else {
--bucket; // 1-based index in value
}
// And then just need to link the new bucket entry in
_collList[bucket] = new Bucket(symbol, _collList[bucket]);
}
/* Ok. Now, do we need a rehash next time? Need to have at least
* 50% fill rate no matter what:
*/
{
int hashSize = _mainHash.length;
if (_count > (hashSize >> 1)) {
int hashQuarter = (hashSize >> 2);
/* And either strictly above 75% (the usual) or
* just 50%, and collision count >= 25% of total hash size
*/
if (_count > (hashSize - hashQuarter)) {
_needRehash = true;
} else if (_collCount >= hashQuarter) {
_needRehash = true;
}
}
}
}
private void rehash()
{
_needRehash = false;
// Note: since we'll make copies, no need to unshare, can just mark as such:
_mainNamesShared = false;
/* And then we can first deal with the main hash area. Since we
* are expanding linearly (double up), we know there'll be no
* collisions during this phase.
*/
int[] oldMainHash = _mainHash;
int len = oldMainHash.length;
int newLen = len+len;
/* 13-Mar-2010, tatu: Let's guard against OOME that could be caused by
* large documents with unique (or mostly so) names
*/
if (newLen > MAX_TABLE_SIZE) {
nukeSymbols();
return;
}
_mainHash = new int[newLen];
_mainHashMask = (newLen - 1);
Name[] oldNames = _mainNames;
_mainNames = new Name[newLen];
int symbolsSeen = 0; // let's do a sanity check
for (int i = 0; i < len; ++i) {
Name symbol = oldNames[i];
if (symbol != null) {
++symbolsSeen;
int hash = symbol.hashCode();
int ix = (hash & _mainHashMask);
_mainNames[ix] = symbol;
_mainHash[ix] = hash << 8; // will clear spill index
}
}
/* And then the spill area. This may cause collisions, although
* not necessarily as many as there were earlier. Let's allocate
* same amount of space, however
*/
int oldEnd = _collEnd;
if (oldEnd == 0) { // no prior collisions...
return;
}
_collCount = 0;
_collEnd = 0;
_collListShared = false;
Bucket[] oldBuckets = _collList;
_collList = new Bucket[oldBuckets.length];
for (int i = 0; i < oldEnd; ++i) {
for (Bucket curr = oldBuckets[i]; curr != null; curr = curr._next) {
++symbolsSeen;
Name symbol = curr._name;
int hash = symbol.hashCode();
int ix = (hash & _mainHashMask);
int val = _mainHash[ix];
if (_mainNames[ix] == null) { // no primary entry?
_mainHash[ix] = (hash << 8);
_mainNames[ix] = symbol;
} else { // nope, it's a collision, need to spill over
++_collCount;
int bucket = val & 0xFF;
if (bucket == 0) { // first spill over?
if (_collEnd <= LAST_VALID_BUCKET) { // yup, still unshared bucket
bucket = _collEnd;
++_collEnd;
// need to expand?
if (bucket >= _collList.length) {
expandCollision();
}
} else { // nope, have to share... let's find shortest?
bucket = findBestBucket();
}
// Need to mark the entry... and the spill index is 1-based
_mainHash[ix] = (val & ~0xFF) | (bucket + 1);
} else {
--bucket; // 1-based index in value
}
// And then just need to link the new bucket entry in
_collList[bucket] = new Bucket(symbol, _collList[bucket]);
}
} // for (... buckets in the chain ...)
} // for (... list of bucket heads ... )
if (symbolsSeen != _count) { // sanity check
throw new RuntimeException("Internal error: count after rehash "+symbolsSeen+"; should be "+_count);
}
}
/**
* Helper method called to empty all shared symbols, but to leave
* arrays allocated
*/
private void nukeSymbols()
{
_count = 0;
Arrays.fill(_mainHash, 0);
Arrays.fill(_mainNames, null);
Arrays.fill(_collList, null);
_collCount = 0;
_collEnd = 0;
}
/**
* Method called to find the best bucket to spill a Name over to:
* usually the first bucket that has only one entry, but in general
* first one of the buckets with least number of entries
*/
private int findBestBucket()
{
Bucket[] buckets = _collList;
int bestCount = Integer.MAX_VALUE;
int bestIx = -1;
for (int i = 0, len = _collEnd; i < len; ++i) {
int count = buckets[i].length();
if (count < bestCount) {
if (count == 1) { // best possible
return i;
}
bestCount = count;
bestIx = i;
}
}
return bestIx;
}
/**
* Method that needs to be called, if the main hash structure
* is (may be) shared. This happens every time something is added,
* even if addition is to the collision list (since collision list
* index comes from lowest 8 bits of the primary hash entry)
*/
private void unshareMain()
{
int[] old = _mainHash;
int len = _mainHash.length;
_mainHash = new int[len];
System.arraycopy(old, 0, _mainHash, 0, len);
_mainHashShared = false;
}
private void unshareCollision()
{
Bucket[] old = _collList;
if (old == null) {
_collList = new Bucket[INITIAL_COLLISION_LEN];
} else {
int len = old.length;
_collList = new Bucket[len];
System.arraycopy(old, 0, _collList, 0, len);
}
_collListShared = false;
}
private void unshareNames()
{
Name[] old = _mainNames;
int len = old.length;
_mainNames = new Name[len];
System.arraycopy(old, 0, _mainNames, 0, len);
_mainNamesShared = false;
}
private void expandCollision()
{
Bucket[] old = _collList;
int len = old.length;
_collList = new Bucket[len+len];
System.arraycopy(old, 0, _collList, 0, len);
}
/*
/**********************************************************
/* Constructing name objects
/**********************************************************
*/
private static Name constructName(int hash, String name, int q1, int q2)
{
if (q2 == 0) { // one quad only?
return new Name1(name, hash, q1);
}
return new Name2(name, hash, q1, q2);
}
private static Name constructName(int hash, String name, int[] quads, int qlen)
{
if (qlen < 4) { // Need to check for 3 quad one, can do others too
switch (qlen) {
case 1:
return new Name1(name, hash, quads[0]);
case 2:
return new Name2(name, hash, quads[0], quads[1]);
case 3:
return new Name3(name, hash, quads[0], quads[1], quads[2]);
default:
}
}
// Otherwise, need to copy the incoming buffer
int[] buf = new int[qlen];
for (int i = 0; i < qlen; ++i) {
buf[i] = quads[i];
}
return new NameN(name, hash, buf, qlen);
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
final static class Bucket
{
protected final Name _name;
protected final Bucket _next;
Bucket(Name name, Bucket next)
{
_name = name;
_next = next;
}
public int length()
{
int len = 1;
for (Bucket curr = _next; curr != null; curr = curr._next) {
++len;
}
return len;
}
public Name find(int hash, int firstQuad, int secondQuad)
{
if (_name.hashCode() == hash) {
if (_name.equals(firstQuad, secondQuad)) {
return _name;
}
}
for (Bucket curr = _next; curr != null; curr = curr._next) {
Name currName = curr._name;
if (currName.hashCode() == hash) {
if (currName.equals(firstQuad, secondQuad)) {
return currName;
}
}
}
return null;
}
public Name find(int hash, int[] quads, int qlen)
{
if (_name.hashCode() == hash) {
if (_name.equals(quads, qlen)) {
return _name;
}
}
for (Bucket curr = _next; curr != null; curr = curr._next) {
Name currName = curr._name;
if (currName.hashCode() == hash) {
if (currName.equals(quads, qlen)) {
return currName;
}
}
}
return null;
}
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/sym/Name3.java 0000644 0001750 0001750 00000001716 11655120726 024564 0 ustar jamespage jamespage package org.codehaus.jackson.sym;
/**
* Specialized implementation of PName: can be used for short Strings
* that consists of 9 to 12 bytes. It's the longest special purpose
* implementaion; longer ones are expressed using {@link NameN}.
*/
public final class Name3
extends Name
{
final int mQuad1;
final int mQuad2;
final int mQuad3;
Name3(String name, int hash, int q1, int q2, int q3)
{
super(name, hash);
mQuad1 = q1;
mQuad2 = q2;
mQuad3 = q3;
}
// Implies quad length == 1, never matches
@Override
public boolean equals(int quad) { return false; }
// Implies quad length == 2, never matches
@Override
public boolean equals(int quad1, int quad2) { return false; }
@Override
public boolean equals(int[] quads, int qlen)
{
return (qlen == 3)
&& (quads[0] == mQuad1)
&& (quads[1] == mQuad2)
&& (quads[2] == mQuad3);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/Versioned.java 0000644 0001750 0001750 00000001433 11655120726 024743 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* 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.
*
* @since 1.6
*/
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).
*/
public Version version();
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/ 0000755 0001750 0001750 00000000000 11672662540 023756 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JacksonAnnotation.java 0000644 0001750 0001750 00000001214 11655120726 030236 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Meta-annotation (annotations used on other annotations)
* used for marking all annotations that are
* part of Jackson package. Can be used for recognizing all
* Jackson annotations generically, and in future also for
* passing other generic annotation configuration.
*/
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonAnnotation
{
// for now, a pure tag annotation, no parameters
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonIgnoreType.java 0000644 0001750 0001750 00000002067 11655120726 027541 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that indicates that all properties of annotated
* type are to be ignored during serialization and deserialization.
*
* Note: annotation does have boolean 'value' property (which defaults
* to 'true'), so that it is actually possible to override value
* using mix-in annotations.
*
* @since 1.7
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIgnoreType
{
/**
* Optional argument that defines whether this annotation is active
* or not. The only use for value 'false' if for overriding purposes
* (which is not needed often); most likely it is needed for use
* with "mix-in annotations" ("annotation overrides").
* For most cases, however, default value of "true" is just fine
* and should be omitted.
*/
boolean value() default true;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonPropertyOrder.java 0000644 0001750 0001750 00000003044 11655120726 030270 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that can be used to define ordering (possibly partial) to use
* when serializing object properties. Properties included in annotation
* declaration will be serialized first (in defined order), followed by
* any properties not included in the definition.
* Annotation definition will override any implicit orderings (such as
* guarantee that Creator-properties are serialized before non-creator
* properties)
*
* Examples:
*
* // ensure that "id" and "name" are output before other properties
* @
JsonPropertyOrder({ "id", "name" })
* // order any properties that don't have explicit setting using alphabetic order
* @
JsonPropertyOrder(alphabetic=true)
*
*
* This annotation has no effect on deserialization.
*
* @since 1.4
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonPropertyOrder
{
/**
* Order in which properties of annotated object are to be serialized in.
*/
public String[] value() default { };
/**
* Property that defines what to do regarding ordering of properties
* not explicitly included in annotation instance. If set to true,
* they will be alphabetically ordered; if false, order is
* undefined (default setting)
*/
public boolean alphabetic() default false;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonSetter.java 0000644 0001750 0001750 00000002273 11655120726 026721 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define a non-static,
* single-argument method to be used as a "setter" for a logical property
* as an alternative to recommended
* {@link JsonProperty} annotation (which was introduced in version 1.1).
*
* Setter means that when a property with matching name is encountered in
* JSON content, this method will be used to set value of the property.
*
* NOTE: this annotation was briefly deprecated for version 1.5; but has
* since been un-deprecated to both allow for asymmetric naming (possibly
* different name when reading and writing JSON), and more importantly to
* allow multi-argument setter method in future.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonSetter
{
/**
* Optional default argument that defines logical property this
* method is used to modify ("set"); this is the property
* name used in JSON content.
*/
String value() default "";
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonBackReference.java 0000644 0001750 0001750 00000003301 11655120726 030123 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used to indicate that associated property is part of
* two-way linkage between fields; and that its role is "child" (or "back") link.
* Value type of the property must be a bean: it can not be a Collection, Map,
* Array or enumeration.
* Linkage is handled such that the property
* annotated with this annotation is not serialized; and during deserialization,
* its value is set to instance that has the "managed" (forward) link.
*
* All references have logical name to allow handling multiple linkages; typical case
* would be that where nodes have both parent/child and sibling linkages. If so,
* pairs of references should be named differently.
* It is an error for a class to have multiple back references with same name,
* even if types pointed are different.
*
* Note: only methods and fields can be annotated with this annotation: constructor
* arguments should NOT be annotated, as they can not be either managed or back
* references.
*
* @author tatu
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonBackReference
{
/**
* Logical have for the reference property pair; used to link managed and
* back references. Default name can be used if there is just single
* reference pair (for example, node class that just has parent/child linkage,
* consisting of one managed reference and matching back reference)
*/
public String value() default "defaultReference";
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonAnySetter.java 0000644 0001750 0001750 00000001443 11655120726 027367 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define a non-static,
* single-argument method, to be used as a "fallback" handler
* for all otherwise unrecognized properties found from Json content.
* It is similar to {@link javax.xml.bind.annotation.XmlAnyElement}
* in behavior; and can only be used to denote a single property
* per type.
*
* If used, all otherwise unmapped key-value pairs from Json Object
* structs are added to the property (of type Map or bean).
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonAnySetter
{
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonGetter.java 0000644 0001750 0001750 00000002257 11655120726 026707 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define a non-static,
* no-argument value-returning (non-void) method to be used as a "getter"
* for a logical property,
* as an alternative to recommended
* {@link JsonProperty} annotation (which was introduced in version 1.1).
*
* Getter means that when serializing Object instance of class that has
* this method (possibly inherited from a super class), a call is made
* through the method, and return value will be serialized as value of
* the property.
*
* @deprecated Use {@link JsonProperty} instead (deprecated since version 1.5)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
@Deprecated
public @interface JsonGetter
{
/**
* Defines name of the logical property this
* method is used to access ("get"); empty String means that
* name should be derived from the underlying method (using
* standard Bean name detection rules)
*/
String value() default "";
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonIgnore.java 0000644 0001750 0001750 00000003724 11655120726 026700 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation
* that indicates that the annotated method or field is to be ignored by
* introspection-based
* serialization and deserialization functionality. That is, it should
* not be consider a "getter", "setter" or "creator".
*
* For example,
* a "getter" method that would otherwise denote
* a property (like, say, "getValue" to suggest property "value")
* to serialize, would be ignored and no such property would
* be output unless another annotation defines alternative method
* to use.
*
* This annotation works purely on method-by-method (or field-by-field) basis;
* annotation on one method or field does not imply ignoring other methods
* or fields.
* Specifically, marking a "setter" candidate does not change handling
* of matching "getter" method (or vice versa).
*
* Annotation is usually used just a like a marker annotation, that
* is, without explicitly defining 'value' argument (which defaults
* to true
): but argument can be explicitly defined.
* This can be done to override an existing JsonIgnore by explictly
* defining one with 'false' argument.
*
* Annotation is similar to {@link javax.xml.bind.annotation.XmlTransient}
*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIgnore
{
/**
* Optional argument that defines whether this annotation is active
* or not. The only use for value 'false' if for overriding purposes
* (which is not needed often); most likely it is needed for use
* with "mix-in annotations" (aka "annotation overrides").
* For most cases, however, default value of "true" is just fine
* and should be omitted.
*/
boolean value() default true;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonRawValue.java 0000644 0001750 0001750 00000002313 11655120726 027174 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that indicates that the annotated method
* or field should be serialized by including literal String value
* of the property as is, without quoting of characters.
* This can be useful for injecting values already serialized in JSON or
* passing javascript function definitions from server to a javascript client.
*
* Warning: the resulting JSON stream may be invalid depending on your input value.
*
* @since 1.7.0
*/
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonRawValue
{
/**
* Optional argument that defines whether this annotation is active
* or not. The only use for value 'false' if for overriding purposes
* (which is not needed often); most likely it is needed for use
* with "mix-in annotations" (aka "annotation overrides").
* For most cases, however, default value of "true" is just fine
* and should be omitted.
*/
boolean value() default true;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonWriteNullProperties.java 0000644 0001750 0001750 00000002175 11655120726 031456 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that can be used to define whether object properties
* that have null values are to be written out when serializing
* content as JSON. This affects Bean and Map serialization.
*
* Annotation can be used with Classes (all instances of
* given class) and Methods.
*
* Default value for this property is 'true', meaning that null
* properties are written.
*
* @deprecated (since 1.6) Currently recommended annotation to use is
* {@link org.codehaus.jackson.map.annotate.JsonSerialize#include()}
* (with values ALWAYS
or NON_NULL
)
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
@Deprecated
public @interface JsonWriteNullProperties
{
/**
* Whether properties for beans of annotated type will always be
* written (true), or only if not null (false).
*/
boolean value() default true;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonSubTypes.java 0000644 0001750 0001750 00000003050 11655120726 027223 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used with {@link JsonTypeInfo} to indicate sub types of serializable
* polymorphic types, and to associate logical names used within JSON content
* (which is more portable than using physical Java class names).
*
* @since 1.5 (but available to fields, methods and constructor params only since 1.8)
*/
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonSubTypes {
/**
* Subtypes of the annotated type (annotated class, or property value type
* associated with the annotated method). These will be checked recursively
* so that types can be defined by only including direct subtypes.
*/
public Type[] value();
/**
* Definition of a subtype, along with optional name. If name is missing, class
* of the type will be checked for {@link JsonTypeName} annotation; and if that
* is also missing or empty, a default
* name will be constructed by type id mechanism.
* Default name is usually based on class name.
*/
public @interface Type {
/**
* Class of the subtype
*/
public Class> value();
/**
* Logical type name used as the type identifier for the class
*/
public String name() default "";
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonTypeInfo.java 0000644 0001750 0001750 00000022261 11655120726 027207 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.*;
/**
* Annotation used for configuring details of if and how type information is
* used with JSON serialization and deserialization, to preserve information
* about actual class of Object instances. This is necessarily for polymorphic
* types, and may also be needed to link abstract declared types and matching
* concrete implementation.
*
* Some examples of typical annotations:
*
* // Include Java class name ("com.myempl.ImplClass") as JSON property "class"
* @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class")
*
* // Include logical type name (defined in impl classes) as wrapper; 2 annotations
* @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
* @JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class})
*
* Alternatively you can also define fully customized type handling by using
* {@link org.codehaus.jackson.map.annotate.JsonTypeResolver} annotation.
*
* NOTE: originally this annotation was only available to use with types (classes),
* but starting with 1.7, it is also allowed for properties (fields, methods,
* constructor parameters).
*
* When used for properties (fields, methods), there annotation always defines
* to values : specifically, when applied to a Collection
or
* Map
property, it will not apply to container type but
* to contained values. This is identical to how JAXB handles type information
* annotations; and is chosen since it is the dominant use case. There is no
* per-property way to force type information to be included for type of
* container itself.
*
* @see org.codehaus.jackson.map.annotate.JsonTypeResolver
*
* @since 1.5 (but available to fields, methods and constructor params since 1.7)
*
* @author tatu
*/
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeInfo
{
/*
/**********************************************************
/* Value enumerations used for properties
/**********************************************************
*/
/**
* Definition of different type identifiers that can be included in JSON
* during serialization, and used for deserialization.
*/
public enum Id {
/**
* This means that no explicit type metadata is included, and typing is
* purely done using contextual information possibly augmented with other
* annotations.
*
* Note: no {@link org.codehaus.jackson.map.jsontype.TypeIdResolver}
* is constructed if this value is used.
*/
NONE(null),
/**
* Means that fully-qualified Java class name is used as the type identifier.
*/
CLASS("@class"),
/**
* Means that Java class name with minimal path is used as the type identifier.
* Minimal means that only the class name, and that part of preceding Java
* package name is included that is needed to construct fully-qualified name
* given fully-qualified name of the declared supertype; additionally a single
* leading dot ('.') must be used to indicate that partial class name is used.
* For example, for supertype "com.foobar.Base", and concrete type
* "com.foo.Impl", only ".Impl" would be included; and for "com.foo.impl.Impl2"
* only ".impl.Impl2" would be included.
* NOTE : leading dot ('.') MUST be used to denote partial (minimal) name;
* if it is missing, value is assumed to be fully-qualified name. Fully-qualified
* name is used in cases where subtypes are not in same package (or sub-package
* thereof) as base class.
*
* If all related classes are in the same Java package, this option can reduce
* amount of type information overhead, especially for small types.
* However, please note that using this alternative is inherently risky since it
* assumes that the
* supertype can be reliably detected. Given that it is based on declared type
* (since ultimate supertype, java.lang.Object
would not be very
* useful reference point), this may not always work as expected.
*/
MINIMAL_CLASS("@c"),
/**
* Means that logical type name is used as type information; name will then need
* to be separately resolved to actual concrete type (Class).
*/
NAME("@type"),
/**
* Means that typing mechanism uses customized handling, with possibly
* custom configuration. This means that semantics of other properties is
* not defined by Jackson package, but by the custom implementation.
*/
CUSTOM(null)
;
private final String _defaultPropertyName;
private Id(String defProp) {
_defaultPropertyName = defProp;
}
public String getDefaultPropertyName() { return _defaultPropertyName; }
}
/**
* Definition of standard type inclusion mechanisms for type metadata.
* Used for standard metadata types, except for {@link Id#NONE}.
* May or may not be used for custom types ({@link Id#CUSTOM}).
*/
public enum As {
/**
* Inclusion mechanism that uses a single configurable property, included
* along with actual data (POJO properties) as a separate meta-property.
*
* Default choice for inclusion.
*/
PROPERTY,
/**
* Inclusion mechanism that wraps typed JSON value (POJO
* serialized as JSON) in
* a JSON Object that has a single entry,
* where field name is serialized type identifier,
* and value is the actual JSON value.
*
* Note: can only be used if type information can be serialized as
* String. This is true for standard type metadata types, but not
* necessarily for custom types.
*/
WRAPPER_OBJECT,
/**
* Inclusion mechanism that wraps typed JSON value (POJO
* serialized as JSON) in
* a 2-element JSON array: first element is the serialized
* type identifier, and second element the serialized POJO
* as JSON Object.
*/
WRAPPER_ARRAY,
/**
* Inclusion mechanism similar to PROPERTY
, except that
* property is included one-level higher in hierarchy, i.e. as sibling
* property at same level as JSON Object to type.
* Note that this choice can only be used for properties , not
* for types (classes). Trying to use it for classes will result in
* inclusion strategy of basic PROPERTY
instead.
*
* @since 1.9
*/
EXTERNAL_PROPERTY
;
}
/*
/**********************************************************
/* Annotation properties
/**********************************************************
*/
/**
* What kind of type metadata is to be used for serializing and deserializing
* type information for instances of annotated type (and its subtypes
* unless overridden)
*/
public Id use();
/**
* What mechanism is used for including type metadata (if any; for
* {@link Id#NONE} nothing is included). Default
*
* Note that for type metadata type of {@link Id#CUSTOM},
* this setting may or may not have any effect.
*/
public As include() default As.PROPERTY;
/**
* Property names used when type inclusion method ({@link As#PROPERTY}) is used
* (or possibly when using type metadata of type {@link Id#CUSTOM}).
*
* Default property name used if this property is not explicitly defined
* (or is set to empty String) is based on
* type metadata type ({@link #use}) used.
*/
public String property() default "";
/**
* Optional property that can be used to specify default implementation
* class to use if type identifier is either not present, or can not
* be mapped to a registered type (which can occur for ids, but not when
* specifying explicit class to use).
*
* Note that while this property allows specification of the default
* implementation to use, it does not help with structural issues that
* may arise if type information is missing. This means that most often
* this is used with type-name -based resolution, to cover cases
* where new sub-types are added, but base type is not changed to
* reference new sub-types.
*
* @since 1.9
*/
public Class> defaultImpl() default None.class;
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* This marker class that is only to be used with defaultImpl
* annotation property, to indicate that there is no default implementation
* specified.
*
* @since 1.9
*/
public abstract static class None { }
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/package-info.java 0000644 0001750 0001750 00000001466 11655120726 027150 0 ustar jamespage jamespage /**
* Public core annotations, most of which are used to configure how
* Data Mapping/Binding works. Annotations in this package can only
* have dependencies to non-annotation classes in Core package;
* annotations that have dependencies to Mapper classes are included
* in Mapper module (under org.codehaus.jackson.map.annotate
).
* Also contains parameter types (mostly enums) needed by annotations.
*
* In future (version 2.0?), this package will probably be split off
* as a separate jar/module, to allow use of annotations without
* including core module. This would be useful for third party value
* classes that themselves do not depend on Jackson, but may want to
* be annotated to be automatically and conveniently serializable by
* Jackson.
*/
package org.codehaus.jackson.annotate;
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonCreator.java 0000644 0001750 0001750 00000001054 11655120726 027046 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define constructors and factory
* methods as one to use for instantiating new instances of the associated
* class.
*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonCreator
{
// no values, since there's no property
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonTypeName.java 0000644 0001750 0001750 00000001435 11655120726 027174 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used for binding logical name that the annotated class
* has. Used with {@link JsonTypeInfo} (and specifically its
* {@link JsonTypeInfo#use} property) to establish relationship
* between type names and types.
*
* @since 1.5
*
* @author tatu
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeName {
/**
* Logical type name for annotated type. If missing (or defined as Empty String),
* defaults to using non-qualified class name as the type.
*/
public String value() default "";
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonUnwrapped.java 0000644 0001750 0001750 00000004011 11655120726 027410 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used to indicate that a property should be serialized
* "unwrapped"; that is, if it would be serialized as JSON Object, its
* properties are instead included as properties of its containing
* Object. For example, consider case of POJO like:
*
*
* public class Parent {
* public int age;
* public Name name;
* }
* public class Name {
* public String first, last;
* }
*
* which would normally be serialized as follows (assuming @JsonUnwrapped
* had no effect):
*
* {
* "age" : 18,
* "name" : {
* "first" : "Joey",
* "last" : "Sixpack"
* }
* }
*
* can be changed to this:
*
* {
* "age" : 18,
* "first" : "Joey",
* "last" : "Sixpack"
* }
*
* by changing Parent class to:
*
* public class Parent {
* public int age;
* \@JsonUnwrapped
* public Name name;
* }
*
* Annotation can only be added to properties, and not classes, as it is contextual.
*
* Also note that annotation only applies if
*
* Value is serialized as JSON Object
*
* Serialization is done using BeanSerializer
, not a custom serializer
*
* No type information is added; if type information needs to be added, structure can
* not be altered regardless of inclusion strategy; so annotation is basically ignored.
*
*
*
* @since 1.9
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonUnwrapped
{
/**
* Property that is usually only used when overriding (masking) annotations,
* using mix-in annotations. Otherwise default value of 'true' is fine, and
* value need not be explicitly included.
*/
boolean enabled() default true;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonValue.java 0000644 0001750 0001750 00000003454 11655120726 026531 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation similar to
* {@link javax.xml.bind.annotation.XmlValue}
* that indicates that results of the annotated "getter" method
* (which means signature must be that of getters; non-void return
* type, no args) is to be used as the single value to serialize
* for the instance. Usually value will be of a simple scalar type
* (String or Number), but it can be any serializable type (Collection,
* Map or Bean).
*
* At most one method of a Class can be annotated with this annotation;
* if more than one is found, an exception may be thrown.
* Also, if method signature is not compatible with Getters, an exception
* may be thrown.
* Whether exception is thrown or not is an implementation detail (due
* to filtering during introspection, some annotations may be skipped)
* and applications should not rely on specific behavior.
*
* A typical use case is that of annotating toString()
* method so that returned String value is Object's Json serialization.
*
* Boolean argument is only used so that sub-classes can "disable"
* annotation if necessary.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonValue
{
/**
* Optional argument that defines whether this annotation is active
* or not. The only use for value 'false' if for overriding purposes.
* Overriding may be necessary when used
* with "mix-in annotations" (aka "annotation overrides").
* For most cases, however, default value of "true" is just fine
* and should be omitted.
*/
boolean value() default true;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonProperty.java 0000644 0001750 0001750 00000002475 11655120726 027303 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define a non-static
* method as a "setter" or "getter" for a logical property
* (depending on its signature),
* or non-static object field to be used (serialized, deserialized) as
* a logical property.
*
* Default value ("") indicates that the field name is used
* as the property name without any modifications, but it
* can be specified to non-empty value to specify different
* name. Property name refers to name used externally, as
* the field name in Json objects.
*
* NOTE: since version 1.1, annotation has also been applicable
* to fields (not with 1.0).
*
* NOTE: since version 1.2, annotation has also been applicable
* to (constructor) parameters
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonProperty
{
/**
* Defines name of the logical property, i.e. Json object field
* name to use for the property: if empty String (which is the
* default), will use name of the field that is annotated.
*/
String value() default "";
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonAnyGetter.java 0000644 0001750 0001750 00000001520 11655120726 027347 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define a non-static,
* no-argument method or member field as something of a reverse of
* {@link JsonAnySetter} method; basically being used like a
* getter but such that contents of the returned Map (type must be
* {@link java.util.Map}) are serialized as if they were actual properties
* of the bean that contains method/field with this annotations.
* As with {@link JsonAnySetter}, only one property should be annotated
* with this annotation.
*
* @since 1.6
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonAnyGetter
{
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonMethod.java 0000644 0001750 0001750 00000005254 11655120726 026675 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
/**
* Enumeration used to define kinds of methods that annotations like
* {@link JsonAutoDetect} apply to.
*
* In addition to actual method types (GETTER, SETTER, CREATOR; and
* sort-of-method, FIELD), 2 pseudo-types
* are defined for convenience: ALWAYS
and NONE
. These
* can be used to indicate, all or none of available method types (respectively),
* for use by annotations that takes JsonMethod
argument.
*/
public enum JsonMethod
{
/**
* Getters are methods used to get a POJO field value for serialization,
* or, under certain conditions also for de-serialization. Latter
* can be used for effectively setting Collection or Map values
* in absence of setters, iff returned value is not a copy but
* actual value of the logical property.
*
* Since version 1.3, this does NOT include "is getters" (methods
* that return boolean and named 'isXxx' for property 'xxx'); instead,
* {@link #IS_GETTER} is used}.
*/
GETTER,
/**
* Setters are methods used to set a POJO value for deserialization.
*/
SETTER,
/**
* Creators are constructors and (static) factory methods used to
* construct POJO instances for deserialization
*/
CREATOR,
/**
* Field refers to fields of regular Java objects. Although
* they are not really methods, addition of optional field-discovery
* in version 1.1 meant that there was need to enable/disable
* their auto-detection, and this is the place to add it in.
*
* @since 1.1
*/
FIELD,
/**
* "Is getters" are getter-like methods that are named "isXxx"
* (instead of "getXxx" for getters) and return boolean value
* (either primitive, or {@link java.lang.Boolean}).
*
* @since 1.3
*/
IS_GETTER,
/**
* This pseudo-type indicates that none of real types is included
*/
NONE,
/**
* This pseudo-type indicates that all of real types are included
*/
ALL
;
private JsonMethod() { }
public boolean creatorEnabled() {
return (this == CREATOR) || (this == ALL);
}
public boolean getterEnabled() {
return (this == GETTER) || (this == ALL);
}
public boolean isGetterEnabled() {
return (this == IS_GETTER) || (this == ALL);
}
public boolean setterEnabled() {
return (this == SETTER) || (this == ALL);
}
public boolean fieldEnabled() {
return (this == FIELD) || (this == ALL);
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonIgnoreProperties.java 0000644 0001750 0001750 00000003065 11655120726 030753 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that can be used to either suppress serialization of
* properties (during serialization), or ignore processing of
* JSON properties read (during deserialization).
*
* Example:
*
* // to prevent specified fields from being serialized or deserialized
* // (i.e. not include in JSON output; or being set even if they were included)
* \@JsonIgnoreProperties({ "internalId", "secretKey" })
* // To ignore any unknown properties in JSON input without exception:
* \@JsonIgnoreProperties(ignoreUnknown=true)
*
*
* Only applicable to classes, not for properties (getters, setters, fields).
*
* @since 1.4
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIgnoreProperties
{
/**
* Names of properties to ignore.
*/
public String[] value() default { };
/**
* Property that defines whether it is ok to just ignore any
* unrecognized properties during deserialization.
* If true, all properties that are unrecognized -- that is,
* there are no setters or creators that accept them -- are
* ignored without warnings (although handlers for unknown
* properties, if any, will still be called) without
* exception.
*
* Does not have any effect on serialization.
*/
public boolean ignoreUnknown() default false;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonAutoDetect.java 0000644 0001750 0001750 00000011236 11655120726 027513 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
/**
* Class annotation that can be used to define which kinds of Methods
* are to be detected by auto-detection.
* Auto-detection means using name conventions
* and/or signature templates to find methods to use for data binding.
* For example, so-called "getters" can be auto-detected by looking for
* public member methods that return a value, do not take argument,
* and have prefix "get" in their name.
*
* Pseudo-value NONE
means that all auto-detection is disabled
* for the specific class that annotation is applied to (including
* its super-types, but only when resolving that class).
* Pseudo-value ALWAYS
means that auto-detection is enabled
* for all method types for the class in similar way.
*
* The default value is ALWAYS
: that is, by default, auto-detection
* is enabled for all classes unless instructed otherwise.
*
* Starting with version 1.5, it is also possible to use more fine-grained
* definitions, to basically define minimum visibility level needed. Defaults
* are different for different types (getters need to be public; setters can
* have any access modifier, for example).
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonAutoDetect
{
/**
* Enumeration for possible visibility thresholds (minimum visibility)
* that can be used to limit which methods (and fields) are
* auto-detected.
*
* @since 1.5
*/
public enum Visibility {
/**
* Value that means that all kinds of access modifiers are acceptable,
* from private to public.
*/
ANY,
/**
* Value that means that any other access modifier other than 'private'
* is considered auto-detectable.
*/
NON_PRIVATE,
/**
* Value that means access modifiers 'protected' and 'public' are
* auto-detectable (and 'private' and "package access" == no modifiers
* are not)
*/
PROTECTED_AND_PUBLIC,
/**
* Value to indicate that only 'public' access modifier is considered
* auto-detectable.
*/
PUBLIC_ONLY,
/**
* Value that indicates that no access modifiers are auto-detectable:
* this can be used to explicitly disable auto-detection for specified
* types.
*/
NONE,
/**
* Value that indicates that default visibility level (whatever it is,
* depends on context) is to be used. This usually means that inherited
* value (from parent visibility settings) is to be used.
*/
DEFAULT;
public boolean isVisible(Member m) {
switch (this) {
case ANY:
return true;
case NONE:
return false;
case NON_PRIVATE:
return !Modifier.isPrivate(m.getModifiers());
case PROTECTED_AND_PUBLIC:
if (Modifier.isProtected(m.getModifiers())) {
return true;
}
// fall through to public case:
case PUBLIC_ONLY:
return Modifier.isPublic(m.getModifiers());
}
return false;
}
}
/**
* Types of property elements (getters, setters, fields, creators) that
* can be auto-detected.
* NOTE: as of 1.5, it is recommended that instead of defining this property,
* distinct visibility properties are used instead. This because levels
* used with this method are not explicit, but global defaults that differ for different
* methods. As such, this property can be considered deprecated and
* only retained for backwards compatibility.
*/
JsonMethod[] value() default { JsonMethod.ALL };
/**
* Minimum visibility required for auto-detecting regular getter methods.
*
* @since 1.5
*/
Visibility getterVisibility() default Visibility.DEFAULT;
/**
* Minimum visibility required for auto-detecting is-getter methods.
*
* @since 1.5
*/
Visibility isGetterVisibility() default Visibility.DEFAULT;
/**
* Minimum visibility required for auto-detecting setter methods.
*
* @since 1.5
*/
Visibility setterVisibility() default Visibility.DEFAULT;
/**
* Minimum visibility required for auto-detecting Creator methods,
* except for no-argument constructors (which are always detected
* no matter what).
*
* @since 1.5
*/
Visibility creatorVisibility() default Visibility.DEFAULT;
/**
* Minimum visibility required for auto-detecting member fields.
*
* @since 1.5
*/
Visibility fieldVisibility() default Visibility.DEFAULT;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/annotate/JsonManagedReference.java 0000644 0001750 0001750 00000003373 11655120726 030630 0 ustar jamespage jamespage package org.codehaus.jackson.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used to indicate that annotated property is part of
* two-way linkage between fields; and that its role is "parent" (or "forward") link.
* Value type (class) of property must have a single compatible property annotated with
* {@link JsonBackReference}. Linkage is handled such that the property
* annotated with this annotation is handled normally (serialized normally, no
* special handling for deserialization); it is the matching back reference
* that requires special handling
*
* All references have logical name to allow handling multiple linkages; typical case
* would be that where nodes have both parent/child and sibling linkages. If so,
* pairs of references should be named differently.
* It is an error for a class too have multiple managed references with same name,
* even if types pointed are different.
*
* Note: only methods and fields can be annotated with this annotation: constructor
* arguments should NOT be annotated, as they can not be either managed or back
* references.
*
* @author tatu
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonManagedReference
{
/**
* Logical have for the reference property pair; used to link managed and
* back references. Default name can be used if there is just single
* reference pair (for example, node class that just has parent/child linkage,
* consisting of one managed reference and matching back reference)
*/
public String value() default "defaultReference";
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/type/ 0000755 0001750 0001750 00000000000 11672662540 023126 5 ustar jamespage jamespage jackson-src-1.9.2/src/java/org/codehaus/jackson/type/package-info.java 0000644 0001750 0001750 00000000622 11655120726 026311 0 ustar jamespage jamespage /**
* 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 org.codehaus.jackson.type;
jackson-src-1.9.2/src/java/org/codehaus/jackson/type/JavaType.java 0000644 0001750 0001750 00000037247 11655120726 025525 0 ustar jamespage jamespage package org.codehaus.jackson.type;
import java.lang.reflect.Modifier;
/**
* Base class for type token classes used both to contain information
* and as keys for deserializers.
*
* Instances can (only) be constructed by
* {@link org.codehaus.jackson.map.type.TypeFactory}.
*/
public abstract class JavaType
{
/**
* 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 _hashCode;
/**
* 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.
*
* TODO: make final and possible promote to sub-classes
*
* @since 1.3
*/
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.
*
* TODO: make final and possible promote to sub-classes
*
* @since 1.5
*/
protected /*final*/ Object _typeHandler;
/*
/**********************************************************
/* 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)
{
_class = raw;
_hashCode = raw.getName().hashCode() + additionalHash;
_valueHandler = null;
_typeHandler = null;
}
/**
* "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
*
* @since 1.7
*/
public abstract JavaType withTypeHandler(Object h);
/**
* "Copy method" that will construct a new instance that is identical to
* this instance, except that its content type will have specified
* type handler assigned.
*
* @return Newly created type instance
*
* @since 1.7
*/
public abstract JavaType withContentTypeHandler(Object h);
// !!! TODO: in 2.0, change to be abstract method
//public abstract JavaType withValueHandler(Object h);
/**
* @since 1.9
*/
public JavaType withValueHandler(Object h) {
/* 16-Aug-2011, tatu: This is not entirely correct, as we can not
* create new immutable instances. However, sub-classes can,
* so if mapper is version 1.9, things work as expected.
* Otherwise, in 2.0 we can make this method abstract.
*/
setValueHandler(h);
return this;
}
// !!! TODO: in 2.0, change to be abstract method
//public abstract JavaType withContentValueHandler(Object h);
/**
* @since 1.9
*/
public JavaType withContentValueHandler(Object h) {
/* 16-Aug-2011, tatu: This is not entirely correct, as we can not
* create new immutable instances. However, sub-classes can,
* so if mapper is version 1.9, things work as expected.
* Otherwise, in 2.0 we can make this method abstract.
*/
getContentType().setValueHandler(h);
return this;
}
/**
* Method for assigning handler to associate with this type; or
* if null passed, to remove such assignment
*
* @since 1.3
*
* @deprecated Since 1.9, should not be used; instead, use
* withContentTypeHandler
and
* withContentValueHandler
methods.
*/
@Deprecated
public void setValueHandler(Object h) {
// sanity check, should be assigned just once
if (h != null && _valueHandler != null) {
throw new IllegalStateException("Trying to reset value handler for type ["+toString()
+"]; old handler of type "+_valueHandler.getClass().getName()+", new handler of type "+h.getClass().getName());
}
_valueHandler = h;
}
/*
/**********************************************************
/* Type coercion fluent factory methods
/**********************************************************
*/
/**
* Method that can be called to do a "narrowing" conversions; that is,
* to return a type with a raw class that is assignable to the raw
* class of this type. If this is not possible, an
* {@link IllegalArgumentException} is thrown.
* If class is same as the current raw class, instance itself is
* returned.
*/
public JavaType narrowBy(Class> subclass)
{
// First: if same raw class, just return this instance
if (subclass == _class) {
return this;
}
// Otherwise, ensure compatibility
_assertSubclass(subclass, _class);
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;
}
/**
* More efficient version of {@link #narrowBy}, called by
* internal framework in cases where compatibility checks
* are to be skipped.
*
* @since 1.5
*/
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;
}
/**
* Method that can be called to do a "widening" conversions; that is,
* to return a type with a raw class that could be assigned from this
* type.
* If such conversion is not possible, an
* {@link IllegalArgumentException} is thrown.
* If class is same as the current raw class, instance itself is
* returned.
*/
public JavaType widenBy(Class> superclass)
{
// First: if same raw class, just return this instance
if (superclass == _class) {
return this;
}
// Otherwise, ensure compatibility
_assertSubclass(_class, superclass);
return _widen(superclass);
}
protected abstract JavaType _narrow(Class> subclass);
/**
*
* Default implementation is just to call {@link #_narrow}, since
* underlying type construction is usually identical
*/
protected JavaType _widen(Class> superclass) {
return _narrow(superclass);
}
public abstract JavaType narrowContentsBy(Class> contentClass);
/**
* @since 1.8
*/
public abstract JavaType widenContentsBy(Class> contentClass);
/*
/**********************************************************
/* Public API, simple accessors
/**********************************************************
*/
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.
*/
public final boolean hasRawClass(Class> clz) {
return _class == clz;
}
public boolean isAbstract() {
return Modifier.isAbstract(_class.getModifiers());
}
/**
* @since 1.3
*/
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...
*/
if (_class.isPrimitive()) {
return true;
}
return false;
}
public boolean isThrowable() {
return Throwable.class.isAssignableFrom(_class);
}
public boolean isArrayType() { return false; }
public final boolean isEnumType() { return _class.isEnum(); }
public final boolean isInterface() { return _class.isInterface(); }
public final boolean isPrimitive() { return _class.isPrimitive(); }
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.
*/
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)
*
* @since 1.8
*/
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)
*
* @since 1.8
*/
public boolean isMapLikeType() { return false; }
/*
/**********************************************************
/* 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).
*
* @since 1.6
*/
public boolean hasGenericTypes()
{
return containedTypeCount() > 0;
}
/**
* Method for accessing key type for this type, assuming type
* has such a concept (only Map types do)
*/
public JavaType getKeyType() { return null; }
/**
* 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 JavaType getContentType() { return null; }
/**
* Method for checking how many contained types this type
* has. Contained types are usually generic types, so that
* generic Maps have 2 contained types.
*
* @since 1.5
*/
public int containedTypeCount() { return 0; }
/**
* 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)
*
* @since 1.5
*/
public JavaType containedType(int index) { return null; }
/**
* 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)
*
* @since 1.5
*/
public String containedTypeName(int index) { return null; }
/*
/**********************************************************
/* Semi-public API, accessing handlers
/**********************************************************
*/
/**
* Method for accessing value handler associated with this type, if any
*
* @since 1.3
*/
@SuppressWarnings("unchecked")
public T getValueHandler() { return (T) _valueHandler; }
/**
* Method for accessing type handler associated with this type, if any
*
* @since 1.5
*/
@SuppressWarnings("unchecked")
public T getTypeHandler() { return (T) _typeHandler; }
/*
/**********************************************************
/* Support for producing signatures (1.6+)
/**********************************************************
*/
/**
* 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.
*
* @since 1.5
*/
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)
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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.
*
* @since 1.6
*/
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
*
* @since 1.6
*/
public abstract StringBuilder getErasedSignature(StringBuilder sb);
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
protected void _assertSubclass(Class> subclass, Class> superClass)
{
if (!_class.isAssignableFrom(subclass)) {
throw new IllegalArgumentException("Class "+subclass.getName()+" is not assignable to "+_class.getName());
}
}
/*
/**********************************************************
/* 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 _hashCode; }
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/type/TypeReference.java 0000644 0001750 0001750 00000004361 11655120726 026531 0 ustar jamespage jamespage package org.codehaus.jackson.type;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* This class is used to pass full generics type information, and
* avoid problems with type erasure (that basically removes most
* usable type references from runtime Class objects).
* It 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.
*/
public abstract class TypeReference
implements Comparable>
{
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 specifc
* case when we know an actual use case, and thereby suitable
* work arounds for valid case(s) and/or error to throw
* on invalid one(s).
*/
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() { return _type; }
/**
* The only reason we define this method (and require implementation
* of Comparable
) is to prevent constructing a
* reference without type information.
*/
@Override
public int compareTo(TypeReference o) {
// just need an implementation, not a good one... hence:
return 0;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonLocation.java 0000644 0001750 0001750 00000010273 11655120726 025411 0 ustar jamespage jamespage package org.codehaus.jackson;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
/**
* 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
*
* @since 1.3
*/
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
*/
final 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);
}
@JsonCreator
public JsonLocation(@JsonProperty("sourceRef") Object sourceRef,
@JsonProperty("byteOffset") long totalBytes,
@JsonProperty("charOffset") long totalChars,
@JsonProperty("lineNr") int lineNr,
@JsonProperty("columnNr") int columnNr)
{
_sourceRef = sourceRef;
_totalBytes = totalBytes;
_totalChars = totalChars;
_lineNr = lineNr;
_columnNr = columnNr;
}
/**
* Reference to the original resource being read, if one available.
* For example, when a parser has been constructed by passing
* a {@link java.io.File} instance, this method would return
* that File. Will return null if no such reference is available,
* for example when {@link java.io.InputStream} was used to
* construct the parser instance.
*/
public Object getSourceRef() { return _sourceRef; }
/**
* @return Line number of the location (1-based)
*/
public int getLineNr() { return _lineNr; }
/**
* @return Column number of the location (1-based)
*/
public int getColumnNr() { return _columnNr; }
/**
* @return Character offset within underlying stream, reader or writer,
* if available; -1 if not.
*/
public long getCharOffset() { return _totalChars; }
/**
* @return Byte offset within underlying stream, reader or writer,
* if available; -1 if not.
*/
public long getByteOffset()
{
return _totalBytes;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(80);
sb.append("[Source: ");
if (_sourceRef == null) {
sb.append("UNKNOWN");
} else {
sb.append(_sourceRef.toString());
}
sb.append("; line: ");
sb.append(_lineNr);
sb.append(", column: ");
sb.append(_columnNr);
sb.append(']');
return sb.toString();
}
@Override
public int hashCode()
{
int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
hash ^= _lineNr;
hash += _columnNr;
hash ^= (int) _totalChars;
hash += (int) _totalBytes;
return hash;
}
@Override
public boolean equals(Object other)
{
if (other == this) return true;
if (other == null) return false;
if (!(other instanceof JsonLocation)) return false;
JsonLocation otherLoc = (JsonLocation) other;
if (_sourceRef == null) {
if (otherLoc._sourceRef != null) return false;
} else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;
return (_lineNr == otherLoc._lineNr)
&& (_columnNr == otherLoc._columnNr)
&& (_totalChars == otherLoc._totalChars)
&& (getByteOffset() == otherLoc.getByteOffset())
;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/ObjectCodec.java 0000644 0001750 0001750 00000012220 11655120726 025145 0 ustar jamespage jamespage package org.codehaus.jackson;
import java.io.IOException;
import java.util.Iterator;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.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
* {@link org.codehaus.jackson.map.ObjectMapper}.
*/
public abstract class ObjectCodec
{
protected ObjectCodec() { }
/*
/**********************************************************
/* API for de-serialization (JSON-to-Object)
/**********************************************************
*/
/**
* Method to deserialize JSON content into a non-container
* type (it can be an array type, however): typically a bean, array
* or a wrapper type (like {@link java.lang.Boolean}).
*
* Note: this method should NOT be used if the result type is a
* container ({@link java.util.Collection} or {@link java.util.Map}.
* The reason is that due to type erasure, key and value types
* can not be introspected when using this method.
*/
public abstract T readValue(JsonParser jp, Class valueType)
throws IOException, JsonProcessingException;
/**
* Method to deserialize JSON content into a Java type, reference
* to which is passed as argument. Type is passed using so-called
* "super type token"
* and specifically needs to be used if the root type is a
* parameterized (generic) container type.
*/
public abstract T readValue(JsonParser jp, TypeReference> valueTypeRef)
throws IOException, JsonProcessingException;
/**
* Method to deserialize JSON content 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).
*/
public abstract T readValue(JsonParser jp, JavaType valueType)
throws IOException, JsonProcessingException;
/**
* 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).
*/
public abstract JsonNode readTree(JsonParser jp)
throws IOException, JsonProcessingException;
/**
* Method for reading sequence of Objects from parser stream,
* all with same specified value type.
*
* @since 1.9
*/
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.
*
* @since 1.9
*/
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.
*
* @since 1.9
*/
public abstract Iterator readValues(JsonParser jp, JavaType 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;
/**
* Method to serialize given Json Tree, using generator
* provided.
*/
public abstract void writeTree(JsonGenerator jgen, JsonNode rootNode)
throws IOException, JsonProcessingException;
/*
/**********************************************************
/* API for Tree Model handling
/**********************************************************
*/
/**
* Method for construct root level Object nodes
* for Tree Model instances.
*
* @since 1.2
*/
public abstract JsonNode createObjectNode();
/**
* Method for construct root level Array nodes
* for Tree Model instances.
*
* @since 1.2
*/
public abstract JsonNode createArrayNode();
/**
* Method for constructing a {@link JsonParser} for reading
* contents of a JSON tree, as if it was external serialized
* JSON content.
*
* @since 1.3
*/
public abstract JsonParser treeAsTokens(JsonNode n);
/**
* Convenience method for converting given JSON tree into instance of specified
* value type. This is equivalent to first constructing a {@link JsonParser} to
* iterate over contents of the tree, and using that parser for data binding.
*
* @since 1.3
*/
public abstract T treeToValue(JsonNode n, Class valueType)
throws IOException, JsonProcessingException;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/FormatSchema.java 0000644 0001750 0001750 00000002410 11655120726 025352 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* 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.
*
* @since 1.8
*/
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.
*/
public String getSchemaType();
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/JsonGenerator.java 0000644 0001750 0001750 00000125011 11655120726 025564 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.io.CharacterEscapes;
import org.codehaus.jackson.io.SerializedString;
/**
* 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, Versioned
{
/**
* Enumeration that defines all togglable features for generators.
*/
public enum Feature {
/**
* Feature that determines whether generator will automatically
* close underlying output target that is NOT owned by the
* generator.
* If disabled, calling application has to separately
* close the underlying {@link OutputStream} and {@link Writer}
* instances used to create the generator. If enabled, generator
* will handle closing, as long as generator itself gets closed:
* this happens when end-of-input is encountered, or generator
* is closed by a call to {@link JsonGenerator#close}.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_TARGET(true),
/**
* Feature that determines what happens when the generator is
* closed while there are still unmatched
* {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT}
* entries in output content. If enabled, such Array(s) and/or
* Object(s) are automatically closed; if disabled, nothing
* specific is done.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_JSON_CONTENT(true),
/**
* Feature that determines whether JSON Object field names are
* quoted using double-quotes, as specified by JSON specification
* or not. Ability to disable quoting was added to support use
* cases where they are not usually expected, which most commonly
* occurs when used straight from Javascript.
*/
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.
*
* @since 1.3
*/
WRITE_NUMBERS_AS_STRINGS(false),
/**
* Feature that specifies that calls to {@link #flush} will cause
* matching flush()
to underlying {@link OutputStream}
* or {@link Writer}; if disabled this will not be done.
* Main reason to disable this feature is to prevent flushing at
* generator level, if it is not possible to prevent method being
* called by other code (like ObjectMapper
or third
* party libraries).
*
* Feature is enabled by default.
*
* @since 1.7
*/
FLUSH_PASSED_TO_STREAM(true),
/**
* Feature that specifies that all characters beyond 7-bit ASCII
* range (i.e. code points of 128 and above) need to be output
* using format-specific escapes (for JSON, backslash escapes),
* if format uses escaping mechanisms (which is generally true
* for textual formats but not for binary formats).
*
* @since 1.8
*/
ESCAPE_NON_ASCII(false)
;
final boolean _defaultState;
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; }
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, configuration, initialization
/**********************************************************
*/
protected JsonGenerator() { }
/**
* 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
*
* @since 1.8
*/
public void setSchema(FormatSchema schema)
{
throw new UnsupportedOperationException("Generator of type "+getClass().getName()+" does not support schema of type '"
+schema.getSchemaType()+"'");
}
/**
* 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
*
* @since 1.8
*/
public boolean canUseSchema(FormatSchema schema) {
return false;
}
/**
* @since 1.6
*/
@Override
public Version version() {
return Version.unknownVersion();
}
/**
* 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.
*
* @since 1.8
*/
public Object getOutputTarget() {
return null;
}
/*
/**********************************************************
/* Public API, configuration
/**********************************************************
*/
/**
* Method for enabling specified parser features:
* check {@link Feature} for list of available features.
*
* @return Generator itself (this), to allow chaining
*
* @since 1.2
*/
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
*
* @since 1.2
*/
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
*
* @since 1.2
*/
public 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.
*
* @since 1.2
*/
public abstract boolean isEnabled(Feature f);
/**
* 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();
// // // Older deprecated versions
/** @deprecated Use {@link #enable} instead
*/
@Deprecated
public void enableFeature(Feature f) { enable(f); }
/** @deprecated Use {@link #disable} instead
*/
@Deprecated
public void disableFeature(Feature f) { disable(f); }
/** @deprecated Use {@link #configure} instead
*/
@Deprecated
public void setFeature(Feature f, boolean state) { configure(f, state); }
/** @deprecated Use {@link #isEnabled} instead
*/
@Deprecated
public boolean isFeatureEnabled(Feature f) { return isEnabled(f); }
/*
/**********************************************************
/* Configuring generator
/**********************************************************
*/
/**
* Method for setting a custom pretty printer, which is usually
* used to add indentation for improved human readability.
* By default, generator does not do pretty printing.
*
* To use the default pretty printer that comes with core
* Jackson distribution, call {@link #useDefaultPrettyPrinter}
* instead.
*
* @return Generator itself (this), to allow chaining
*/
public JsonGenerator setPrettyPrinter(PrettyPrinter pp) {
_cfgPrettyPrinter = pp;
return this;
}
/**
* Convenience method for enabling pretty-printing using
* the default pretty printer
* ({@link org.codehaus.jackson.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.
*
* @since 1.8
*/
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.
*
* @since 1.8
*/
public CharacterEscapes getCharacterEscapes() {
return null;
}
/**
* Method for defining custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*
* @since 1.8
*/
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) {
return this;
}
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
/**
* Method for writing starting marker of a JSON Array value
* (character '['; plus possible white space decoration
* if pretty-printing is enabled).
*
* Array values can be written in any context where values
* are allowed: meaning everywhere except for when
* a field name is expected.
*/
public abstract void writeStartArray()
throws IOException, JsonGenerationException;
/**
* Method for writing closing marker of a JSON Array value
* (character ']'; plus possible white space decoration
* if pretty-printing is enabled).
*
* Marker can be written if the innermost structured type
* is Array.
*/
public abstract void writeEndArray()
throws IOException, JsonGenerationException;
/**
* Method for writing starting marker of a JSON Object value
* (character '{'; plus possible white space decoration
* if pretty-printing is enabled).
*
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a field name is expected.
*/
public abstract void writeStartObject()
throws IOException, JsonGenerationException;
/**
* Method for writing closing marker of a JSON Object value
* (character '}'; plus possible white space decoration
* if pretty-printing is enabled).
*
* Marker can be written if the innermost structured type
* is Object, and the last written event was either a
* complete value, or START-OBJECT marker (see JSON specification
* for more details).
*/
public abstract void writeEndObject()
throws IOException, JsonGenerationException;
/**
* Method for writing a field name (JSON String surrounded by
* double quotes: syntactically identical to a JSON String value),
* possibly decorated by white space if pretty-printing is enabled.
*
* Field names can only be written in Object context (check out
* JSON specification for details), when field name is expected
* (field names alternate with values).
*/
public abstract void writeFieldName(String name)
throws IOException, JsonGenerationException;
/**
* Method similar to {@link #writeFieldName(String)}, main difference
* being that it may perform better as some of processing (such as
* quoting of certain characters, or encoding into external encoding
* if supported by generator) can be done just once and reused for
* later calls.
*
* Default implementation simple uses unprocessed name container in
* serialized String; implementations are strongly encouraged to make
* use of more efficient methods argument object has.
*
* @since 1.6
*/
public void writeFieldName(SerializedString name)
throws IOException, JsonGenerationException
{
writeFieldName(name.getValue());
}
/**
* 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.
*
* @since 1.7
*/
public void writeFieldName(SerializableString name)
throws IOException, JsonGenerationException
{
writeFieldName(name.getValue());
}
/*
/**********************************************************
/* Public API, write methods, text/String values
/**********************************************************
*/
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
*/
public abstract void writeString(String text)
throws IOException, JsonGenerationException;
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
*/
public abstract void writeString(char[] text, int offset, int len)
throws IOException, JsonGenerationException;
/**
* Method similar to {@link #writeString(String)}, but that takes
* {@link SerializableString} which can make this potentially
* more efficient to call as generator may be able to reuse
* quoted and/or encoded representation.
*
* Default implementation just calls {@link #writeString(String)};
* sub-classes should override it with more efficient implementation
* if possible.
*
* @since 1.7
*/
public void writeString(SerializableString text)
throws IOException, JsonGenerationException
{
writeString(text.getValue());
}
/**
* 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.
*
* @since 1.7
*/
public abstract void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException;
/**
* Method similar to {@link #writeString(String)} but that takes as its input
* a UTF-8 encoded String which has not been escaped using whatever
* escaping scheme data format requires (for JSON that is backslash-escaping
* for control characters and double-quotes; for other formats something else).
* This means that textual JSON backends need to check if value needs
* JSON escaping, but otherwise can just be copied as is to output.
* Also, quoting that data format requires (like double-quotes for JSON) will be added
* around the value if and as necessary.
*
* Note that some backends may choose not to support this method: for
* example, if underlying destination is a {@link java.io.Writer}
* using this method would require UTF-8 decoding.
* In this case
* generator implementation may instead choose to throw a
* {@link UnsupportedOperationException} due to ineffectiveness
* of having to decode input.
*
* @since 1.7
*/
public abstract void writeUTF8String(byte[] text, int offset, int length)
throws IOException, JsonGenerationException;
/*
/**********************************************************
/* Public API, write methods, binary/raw content
/**********************************************************
*/
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(String text)
throws IOException, JsonGenerationException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(String text, int offset, int len)
throws IOException, JsonGenerationException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(char[] text, int offset, int len)
throws IOException, JsonGenerationException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(char c)
throws IOException, JsonGenerationException;
/**
* Method that will force generator to copy
* input text verbatim without any modifications, but assuming
* it must constitute a single legal JSON value (number, string,
* boolean, null, Array or List). Assuming this, proper separators
* are added if and as needed (comma or colon), and generator
* state updated to reflect this.
*/
public abstract void writeRawValue(String text)
throws IOException, JsonGenerationException;
public abstract void writeRawValue(String text, int offset, int len)
throws IOException, JsonGenerationException;
public abstract void writeRawValue(char[] text, int offset, int len)
throws IOException, JsonGenerationException;
/**
* Method that will output given chunk of binary data as base64
* encoded, as a complete String value (surrounded by double quotes).
* This method defaults
*
* Note: because Json Strings can not contain unescaped linefeeds,
* if linefeeds are included (as per last argument), they must be
* escaped. This adds overhead for decoding without improving
* readability.
* Alternatively if linefeeds are not included,
* resulting String value may violate the requirement of base64
* RFC which mandates line-length of 76 characters and use of
* linefeeds. However, all {@link JsonParser} implementations
* are required to accept such "long line base64"; as do
* typical production-level base64 decoders.
*
* @param b64variant Base64 variant to use: defines details such as
* whether padding is used (and if so, using which character);
* what is the maximum line length before adding linefeed,
* and also the underlying alphabet to use.
*/
public abstract void writeBinary(Base64Variant b64variant,
byte[] data, int offset, int len)
throws IOException, JsonGenerationException;
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
*/
public void writeBinary(byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
writeBinary(Base64Variants.getDefaultVariant(), data, offset, len);
}
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but assumes default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). Also
* assumes that whole byte array is to be output.
*/
public void writeBinary(byte[] data)
throws IOException, JsonGenerationException
{
writeBinary(Base64Variants.getDefaultVariant(), data, 0, data.length);
}
/*
/**********************************************************
/* 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.
*/
public abstract void writeNumber(int v)
throws IOException, JsonGenerationException;
/**
* Method for outputting given value as Json number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNumber(long v)
throws IOException, JsonGenerationException;
/**
* Method for outputting given value as Json number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNumber(BigInteger v)
throws IOException, JsonGenerationException;
/**
* Method for outputting indicate Json numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNumber(double d)
throws IOException, JsonGenerationException;
/**
* Method for outputting indicate Json numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNumber(float f)
throws IOException, JsonGenerationException;
/**
* Method for outputting indicate Json numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNumber(BigDecimal dec)
throws IOException, JsonGenerationException;
/**
* Write method that can be used for custom numeric types that can
* not be (easily?) converted to "standard" Java number types.
* Because numbers are not surrounded by double quotes, regular
* {@link #writeString} method can not be used; nor
* {@link #writeRaw} because that does not properly handle
* value separators needed in Array or Object contexts.
*
* Note: because of lack of type safety, some generator
* implementations may not be able to implement this
* method. For example, if a binary json format is used,
* it may require type information for encoding; similarly
* for generator-wrappers around Java objects or Json nodes.
* If implementation does not implement this method,
* it needs to throw {@link UnsupportedOperationException}.
*/
public abstract void writeNumber(String encodedValue)
throws IOException, JsonGenerationException,
UnsupportedOperationException;
/**
* Method for outputting literal Json boolean value (one of
* Strings 'true' and 'false').
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeBoolean(boolean state)
throws IOException, JsonGenerationException;
/**
* Method for outputting literal Json null value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNull()
throws IOException, JsonGenerationException;
/*
/**********************************************************
/* Public API, write methods, serializing Java objects
/**********************************************************
*/
/**
* Method for writing given Java object (POJO) as Json.
* Exactly how the object gets written depends on object
* in question (ad on codec, its configuration); for most
* beans it will result in Json object, but for others Json
* array, or String or numeric value (and for nulls, Json
* null literal.
* NOTE : generator must have its object codec
* set to non-null value; for generators created by a mapping
* factory this is the case, for others not.
*/
public abstract void writeObject(Object pojo)
throws IOException, JsonProcessingException;
/**
* Method for writing given JSON tree (expressed as a tree
* where given JsonNode is the root) using this generator.
* This will generally just call
* {@link #writeObject} with given node, but is added
* for convenience and to make code more explicit in cases
* where it deals specifically with trees.
*/
public abstract void writeTree(JsonNode rootNode)
throws IOException, JsonProcessingException;
/*
/**********************************************************
/* Public API, convenience field write methods
/**********************************************************
*/
/**
* Convenience method for outputting a field entry ("member")
* that has a String value. Equivalent to:
*
* writeFieldName(fieldName);
* writeString(value);
*
*
* Note: many performance-sensitive implementations override this method
*/
public void writeStringField(String fieldName, String value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeString(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has a boolean value. Equivalent to:
*
* writeFieldName(fieldName);
* writeBoolean(value);
*
*/
public final void writeBooleanField(String fieldName, boolean value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeBoolean(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has JSON literal value null. Equivalent to:
*
* writeFieldName(fieldName);
* writeNull();
*
*/
public final void writeNullField(String fieldName)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeNull();
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, int value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, long value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, double value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, float value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, BigDecimal value)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that contains specified data in base64-encoded form.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeBinary(value);
*
*/
public final void writeBinaryField(String fieldName, byte[] data)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeBinary(data);
}
/**
* Convenience method for outputting a field entry ("member")
* (that will contain a JSON Array value), and the START_ARRAY marker.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeStartArray();
*
*
* Note: caller still has to take care to close the array
* (by calling {#link #writeEndArray}) after writing all values
* of the value Array.
*/
public final void writeArrayFieldStart(String fieldName)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeStartArray();
}
/**
* Convenience method for outputting a field entry ("member")
* (that will contain a JSON Object value), and the START_OBJECT marker.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeStartObject();
*
*
* Note: caller still has to take care to close the Object
* (by calling {#link #writeEndObject}) after writing all
* entries of the value Object.
*/
public final void writeObjectFieldStart(String fieldName)
throws IOException, JsonGenerationException
{
writeFieldName(fieldName);
writeStartObject();
}
/**
* Convenience method for outputting a field entry ("member")
* that has contents of specific Java object as its value.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeObject(pojo);
*
*/
public final void writeObjectField(String fieldName, Object pojo)
throws IOException, JsonProcessingException
{
writeFieldName(fieldName);
writeObject(pojo);
}
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
/**
* Method for copying contents of the current event that
* the given parser instance points to.
* Note that the method will not copy any other events,
* such as events contained within Json Array or Object structures.
*
* Calling this method will not advance the given
* parser, although it may cause parser to internally process
* more data (if it lazy loads contents of value events, for example)
*/
public abstract void copyCurrentEvent(JsonParser jp)
throws IOException, JsonProcessingException;
/**
* Method for copying contents of the current event
* and following events that it encloses
* the given parser instance points to.
*
* So what constitutes enclosing? Here is the list of
* events that have associated enclosed events that will
* get copied:
*
* {@link JsonToken#START_OBJECT}:
* all events up to and including matching (closing)
* {@link JsonToken#END_OBJECT} will be copied
*
* {@link JsonToken#START_ARRAY}
* all events up to and including matching (closing)
* {@link JsonToken#END_ARRAY} will be copied
*
* {@link JsonToken#FIELD_NAME} the logical value (which
* can consist of a single scalar value; or a sequence of related
* events for structured types (Json Arrays, Objects)) will
* be copied along with the name itself. So essentially the
* whole field entry (name and value) will be copied.
*
*
*
* After calling this method, parser will point to the
* last event that was copied. This will either be
* the event parser already pointed to (if there were no
* enclosed events), or the last enclosed event copied.
*/
public abstract void copyCurrentStructure(JsonParser jp)
throws IOException, JsonProcessingException;
/*
/**********************************************************
/* Public API, context access
/**********************************************************
*/
/**
* @return Context object that can give information about logical
* position within generated json content.
*/
public abstract JsonStreamContext getOutputContext();
/*
/**********************************************************
/* Public API, buffer handling
/**********************************************************
*/
/**
* Method called to flush any buffered content to the underlying
* target (output stream, writer), and to flush the target itself
* as well.
*/
public abstract void flush() throws IOException;
/**
* Method that can be called to determine whether this generator
* is closed or not. If it is closed, no more output can be done.
*/
public abstract boolean isClosed();
/*
/**********************************************************
/* Closeable implementation
/**********************************************************
*/
/**
* Method called to close this generator, so that no more content
* can be written.
*
* Whether the underlying target (stream, writer) gets closed depends
* on whether this generator either manages the target (i.e. is the
* only one with access to the target -- case if caller passes a
* reference to the resource such as File, but not stream); or
* has feature {@link Feature#AUTO_CLOSE_TARGET} enabled.
* If either of above is true, the target is also closed. Otherwise
* (not managing, feature not enabled), target is not closed.
*/
@Override
public abstract void close()
throws IOException;
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/Base64Variant.java 0000644 0001750 0001750 00000032712 11655120726 025362 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson;
import java.util.Arrays;
/**
* Abstract base class used to define specific details of which
* variant of Base64 encoding/decoding is to be used. Although there is
* somewhat standard basic version (so-called "MIME Base64"), other variants
* exists, see Base64 Wikipedia entry for details.
*
* @author Tatu Saloranta
*
* @since 0.9.3
*/
public final class Base64Variant
{
/**
* 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 int[] _asciiToBase64 = new int[128];
/**
* Encoding table used for base 64 decoding when output is done
* as characters.
*/
private final char[] _base64ToAsciiC = new char[64];
/**
* Alternative encoding table used for base 64 decoding when output is done
* as ascii bytes.
*/
private final byte[] _base64ToAsciiB = new byte[64];
/*
/**********************************************************
/* Other configuration
/**********************************************************
*/
/**
* Symbolic name of variant; used for diagnostics/debugging.
*/
final String _name;
/**
* Whether this variant uses padding or not.
*/
final boolean _usesPadding;
/**
* Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not).
*/
final 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.
*/
final 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;
}
/*
/**********************************************************
/* Public accessors
/**********************************************************
*/
public String getName() { return _name; }
public boolean usesPadding() { return _usesPadding; }
public boolean usesPaddingChar(char c) { return c == _paddingChar; }
public boolean usesPaddingChar(int ch) { return ch == (int) _paddingChar; }
public char getPaddingChar() { return _paddingChar; }
public byte getPaddingByte() { return (byte)_paddingChar; }
public int getMaxLineLength() { return _maxLineLength; }
/*
/**********************************************************
/* Decoding support
/**********************************************************
*/
/**
* @return 6-bit decoded value, if valid character;
*/
public int decodeBase64Char(char c)
{
int ch = (int) c;
return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID;
}
public int decodeBase64Char(int ch)
{
return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID;
}
public int decodeBase64Byte(byte b)
{
int ch = (int) b;
return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID;
}
/*
/**********************************************************
/* Encoding support
/**********************************************************
*/
public char encodeBase64BitsAsChar(int value)
{
/* Let's assume caller has done necessary checks; this
* method must be fast and inlinable
*/
return _base64ToAsciiC[value];
}
/**
* Method that encodes given right-aligned (LSB) 24-bit value
* into 4 base64 characters, stored in given result buffer.
*/
public int encodeBase64Chunk(int b24, char[] buffer, int ptr)
{
buffer[ptr++] = _base64ToAsciiC[(b24 >> 18) & 0x3F];
buffer[ptr++] = _base64ToAsciiC[(b24 >> 12) & 0x3F];
buffer[ptr++] = _base64ToAsciiC[(b24 >> 6) & 0x3F];
buffer[ptr++] = _base64ToAsciiC[b24 & 0x3F];
return ptr;
}
public void encodeBase64Chunk(StringBuilder sb, int b24)
{
sb.append(_base64ToAsciiC[(b24 >> 18) & 0x3F]);
sb.append(_base64ToAsciiC[(b24 >> 12) & 0x3F]);
sb.append(_base64ToAsciiC[(b24 >> 6) & 0x3F]);
sb.append(_base64ToAsciiC[b24 & 0x3F]);
}
/**
* Method that outputs partial chunk (which only encodes one
* or two bytes of data). Data given is still aligned same as if
* it as full data; that is, missing data is at the "right end"
* (LSB) of int.
*
* @param outputBytes Number of encoded bytes included (either 1 or 2)
*/
public int encodeBase64Partial(int bits, int outputBytes, char[] buffer, int outPtr)
{
buffer[outPtr++] = _base64ToAsciiC[(bits >> 18) & 0x3F];
buffer[outPtr++] = _base64ToAsciiC[(bits >> 12) & 0x3F];
if (_usesPadding) {
buffer[outPtr++] = (outputBytes == 2) ?
_base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar;
buffer[outPtr++] = _paddingChar;
} else {
if (outputBytes == 2) {
buffer[outPtr++] = _base64ToAsciiC[(bits >> 6) & 0x3F];
}
}
return outPtr;
}
public void encodeBase64Partial(StringBuilder sb, int bits, int outputBytes)
{
sb.append(_base64ToAsciiC[(bits >> 18) & 0x3F]);
sb.append(_base64ToAsciiC[(bits >> 12) & 0x3F]);
if (_usesPadding) {
sb.append((outputBytes == 2) ?
_base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar);
sb.append(_paddingChar);
} else {
if (outputBytes == 2) {
sb.append(_base64ToAsciiC[(bits >> 6) & 0x3F]);
}
}
}
public byte encodeBase64BitsAsByte(int value)
{
// As with above, assuming it is 6-bit value
return _base64ToAsciiB[value];
}
/**
* Method that encodes given right-aligned (LSB) 24-bit value
* into 4 base64 bytes (ascii), stored in given result buffer.
*/
public int encodeBase64Chunk(int b24, byte[] buffer, int ptr)
{
buffer[ptr++] = _base64ToAsciiB[(b24 >> 18) & 0x3F];
buffer[ptr++] = _base64ToAsciiB[(b24 >> 12) & 0x3F];
buffer[ptr++] = _base64ToAsciiB[(b24 >> 6) & 0x3F];
buffer[ptr++] = _base64ToAsciiB[b24 & 0x3F];
return ptr;
}
/**
* Method that outputs partial chunk (which only encodes one
* or two bytes of data). Data given is still aligned same as if
* it as full data; that is, missing data is at the "right end"
* (LSB) of int.
*
* @param outputBytes Number of encoded bytes included (either 1 or 2)
*/
public int encodeBase64Partial(int bits, int outputBytes, byte[] buffer, int outPtr)
{
buffer[outPtr++] = _base64ToAsciiB[(bits >> 18) & 0x3F];
buffer[outPtr++] = _base64ToAsciiB[(bits >> 12) & 0x3F];
if (_usesPadding) {
byte pb = (byte) _paddingChar;
buffer[outPtr++] = (outputBytes == 2) ?
_base64ToAsciiB[(bits >> 6) & 0x3F] : pb;
buffer[outPtr++] = pb;
} else {
if (outputBytes == 2) {
buffer[outPtr++] = _base64ToAsciiB[(bits >> 6) & 0x3F];
}
}
return outPtr;
}
/**
* Convenience method for converting given byte array as base64 encoded
* String using this variant's settings.
* Resulting value is "raw", that is, not enclosed in double-quotes.
*
* @param input Byte array to encode
*
* @since 1.6
*/
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
*
* @since 1.6
*/
public String encode(byte[] input, boolean addQuotes)
{
int inputEnd = input.length;
StringBuilder sb;
{
// let's approximate... 33% overhead, ~= 3/8 (0.375)
int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3);
sb = new StringBuilder(outputLen);
}
if (addQuotes) {
sb.append('"');
}
int chunksBeforeLF = getMaxLineLength() >> 2;
// Ok, first we loop through all full triplets of data:
int inputPtr = 0;
int safeInputEnd = inputEnd-3; // to get only full triplets
while (inputPtr <= safeInputEnd) {
// First, mash 3 bytes into lsb of 32-bit int
int b24 = ((int) input[inputPtr++]) << 8;
b24 |= ((int) input[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
encodeBase64Chunk(sb, b24);
if (--chunksBeforeLF <= 0) {
// note: must quote in JSON value, so not really useful...
sb.append('\\');
sb.append('n');
chunksBeforeLF = getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
if (inputLeft > 0) { // yes, but do we have room for output?
int b24 = ((int) input[inputPtr++]) << 16;
if (inputLeft == 2) {
b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
}
encodeBase64Partial(sb, b24, inputLeft);
}
if (addQuotes) {
sb.append('"');
}
return sb.toString();
}
/*
/**********************************************************
/* other methods
/**********************************************************
*/
@Override
public String toString() { return _name; }
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/Base64Variants.java 0000644 0001750 0001750 00000007261 11655120726 025546 0 ustar jamespage jamespage /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*
* Licensed under the License specified in file LICENSE, included with
* the source code and binary code bundles.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.jackson;
/**
* Container for commonly used Base64 variants.
*
* @author Tatu Saloranta
*/
public final class Base64Variants
{
final static String STD_BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* This variant is what most people would think of "the standard"
* Base64 encoding.
*
* See wikipedia Base64 entry for details.
*
* Note that although this can be thought of as the standard variant,
* it is not the default for Jackson: no-linefeeds alternative
* is because of JSON requirement of escaping all linefeeds.
*/
public final static Base64Variant MIME;
static {
MIME = new Base64Variant("MIME", STD_BASE64_ALPHABET, true, '=', 76);
}
/**
* Slightly non-standard modification of {@link #MIME} which does not
* use linefeeds (max line length set to infinite). Useful when linefeeds
* wouldn't work well (possibly in attributes), or for minor space savings
* (save 1 linefeed per 76 data chars, ie. ~1.4% savings).
*/
public final static Base64Variant MIME_NO_LINEFEEDS;
static {
MIME_NO_LINEFEEDS = new Base64Variant(MIME, "MIME-NO-LINEFEEDS", Integer.MAX_VALUE);
}
/**
* This variant is the one that predates {@link #MIME}: it is otherwise
* identical, except that it mandates shorter line length.
*/
public final static Base64Variant PEM = new Base64Variant(MIME, "PEM", true, '=', 64);
/**
* This non-standard variant is usually used when encoded data needs to be
* passed via URLs (such as part of GET request). It differs from the
* base {@link #MIME} variant in multiple ways.
* First, no padding is used: this also means that it generally can not
* be written in multiple separate but adjacent chunks (which would not
* be the usual use case in any case). Also, no linefeeds are used (max
* line length set to infinite). And finally, two characters (plus and
* slash) that would need quoting in URLs are replaced with more
* optimal alternatives (hyphen and underscore, respectively).
*/
public final static Base64Variant MODIFIED_FOR_URL;
static {
StringBuffer sb = new StringBuffer(STD_BASE64_ALPHABET);
// Replace plus with hyphen, slash with underscore (and no padding)
sb.setCharAt(sb.indexOf("+"), '-');
sb.setCharAt(sb.indexOf("/"), '_');
/* And finally, let's not split lines either, wouldn't work too
* well with URLs
*/
MODIFIED_FOR_URL = new Base64Variant("MODIFIED-FOR-URL", sb.toString(), false, Base64Variant.PADDING_CHAR_NONE, Integer.MAX_VALUE);
}
/**
* Method used to get the default variant ("MIME_NO_LINEFEEDS") for cases
* where caller does not explicitly specify the variant.
* We will prefer no-linefeed version because linefeeds in JSON values
* must be escaped, making linefeed-containing variants sub-optimal.
*/
public static Base64Variant getDefaultVariant() {
return MIME_NO_LINEFEEDS;
}
}
jackson-src-1.9.2/src/java/org/codehaus/jackson/Version.java 0000644 0001750 0001750 00000005051 11655120726 024432 0 ustar jamespage jamespage package org.codehaus.jackson;
/**
* Object that encapsulates version information of a component,
* and is return by {@link Versioned#version}.
*
* @since 1.6
*/
public class Version
implements Comparable
{
private final static Version UNKNOWN_VERSION = new Version(0, 0, 0, null);
protected final int _majorVersion;
protected final int _minorVersion;
protected final int _patchLevel;
/**
* Additional information for snapshot versions; null for non-snapshot
* (release) versions.
*/
protected final String _snapshotInfo;
public Version(int major, int minor, int patchLevel,
String snapshotInfo)
{
_majorVersion = major;
_minorVersion = minor;
_patchLevel = patchLevel;
_snapshotInfo = snapshotInfo;
}
/**
* Method returns canonical "not known" version, which is used as version
* in cases where actual version information is not known (instead of null).
*/
public static Version unknownVersion() { return UNKNOWN_VERSION; }
public boolean isUknownVersion() { return (this == UNKNOWN_VERSION); }
public boolean isSnapshot() { return (_snapshotInfo != null && _snapshotInfo.length() > 0); }
public int getMajorVersion() { return _majorVersion; }
public int getMinorVersion() { return _minorVersion; }
public int getPatchLevel() { return _patchLevel; }
@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 _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);
}
@Override
public int compareTo(Version other)
{
int diff = _majorVersion - other._majorVersion;
if (diff == 0) {
diff = _minorVersion - other._minorVersion;
if (diff == 0) {
diff = _patchLevel - other._patchLevel;
}
}
return diff;
}
}
jackson-src-1.9.2/src/VERSION.txt 0000644 0001750 0001750 00000000012 11655120726 017065 0 ustar jamespage jamespage @VERSION@
jackson-src-1.9.2/src/mapper/ 0000755 0001750 0001750 00000000000 11655120726 016472 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/ 0000755 0001750 0001750 00000000000 11655120726 017413 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/ 0000755 0001750 0001750 00000000000 11655120726 020202 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/codehaus/ 0000755 0001750 0001750 00000000000 11655120726 021775 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/ 0000755 0001750 0001750 00000000000 11655120726 023425 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/schema/ 0000755 0001750 0001750 00000000000 11672662540 024671 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/schema/JsonSchema.java 0000644 0001750 0001750 00000004211 11655120726 027560 0 ustar jamespage jamespage package org.codehaus.jackson.schema;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonValue;
import org.codehaus.jackson.node.ObjectNode;
import org.codehaus.jackson.node.JsonNodeFactory;
/**
* A {@link org.codehaus.jackson.JsonNode} that represents a JSON-Schema instance.
*
* @author Ryan Heaton
* @see JSON Schema
*/
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 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;
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/schema/package-info.java 0000644 0001750 0001750 00000000260 11655120726 030052 0 ustar jamespage jamespage /**
* Classes needed for JSON schema support (currently just ability
* to generate schemas using serialization part of data mapping)
*/
package org.codehaus.jackson.schema;
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/schema/JsonSerializableSchema.java 0000644 0001750 0001750 00000002760 11655120726 032116 0 ustar jamespage jamespage package org.codehaus.jackson.schema;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import org.codehaus.jackson.annotate.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
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonSerializableSchema
{
/**
* The schema type for this JsonSerializable instance.
* Possible values: "string", "number", "boolean", "object", "array", "null", "any"
*
* @return The schema type for this JsonSerializable instance.
*/
String schemaType() default "any";
/**
* If the schema type is "object", the node that defines the properties of the object.
*
* @return The node representing the schema properties, or "##irrelevant" if irrelevant.
*/
String schemaObjectPropertiesDefinition() default "##irrelevant";
/**
* If the schema type if "array", the node that defines the schema for the items in the array.
*
* @return The schema for the items in the array, or "##irrelevant" if irrelevant.
*/
String schemaItemDefinition() default "##irrelevant";
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/schema/SchemaAware.java 0000644 0001750 0001750 00000001326 11655120726 027712 0 ustar jamespage jamespage package org.codehaus.jackson.schema;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.JsonMappingException;
import java.lang.reflect.Type;
/**
* Marker interface for schema-aware serializers.
*
* @author Ryan Heaton
*/
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.
*/
JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException;
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/ 0000755 0001750 0001750 00000000000 11672662540 024356 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/NullNode.java 0000644 0001750 0001750 00000002306 11655120726 026736 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.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 JsonToken asToken() { return JsonToken.VALUE_NULL; }
@Override
public boolean isNull() { return true; }
@Override
public String asText() {
return "null";
}
@Override
public int asInt(int defaultValue) {
return 0;
}
@Override
public long asLong(long defaultValue) {
return 0L;
}
@Override
public double asDouble(double defaultValue) {
return 0.0;
}
@Override
public final void serialize(JsonGenerator jg, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jg.writeNull();
}
@Override
public boolean equals(Object o)
{
return (o == this);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/NumericNode.java 0000644 0001750 0001750 00000003112 11655120726 027422 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.JsonParser;
/**
* Intermediate value node used for numeric nodes.
*/
public abstract class NumericNode
extends ValueNode
{
protected NumericNode() { }
@Override
public final boolean isNumber() { return true; }
// // // Let's re-abstract so sub-classes handle them
@Override
public abstract JsonParser.NumberType getNumberType();
@Override
public abstract Number getNumberValue();
@Override
public abstract int getIntValue();
@Override
public abstract long getLongValue();
@Override
public abstract double getDoubleValue();
@Override
public abstract BigDecimal getDecimalValue();
@Override
public abstract BigInteger getBigIntegerValue();
/*
/**********************************************************
/* General type coercions
/**********************************************************
*/
@Override
public abstract String asText();
@Override
public int asInt() {
return getIntValue();
}
@Override
public int asInt(int defaultValue) {
return getIntValue();
}
@Override
public long asLong() {
return getLongValue();
}
@Override
public long asLong(long defaultValue) {
return getLongValue();
}
@Override
public double asDouble() {
return getDoubleValue();
}
@Override
public double asDouble(double defaultValue) {
return getDoubleValue();
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/TextNode.java 0000644 0001750 0001750 00000022672 11655120726 026760 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.io.NumberInput;
import org.codehaus.jackson.util.ByteArrayBuilder;
import org.codehaus.jackson.util.CharTypes;
/**
* Value node that contains a text value.
*/
public final class TextNode
extends ValueNode
{
final static int INT_SPACE = ' ';
final static TextNode EMPTY_STRING_NODE = new TextNode("");
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 JsonToken asToken() { return JsonToken.VALUE_STRING; }
/**
* Yes indeed it is textual
*/
@Override
public boolean isTextual() { return true; }
@Override
public String getTextValue() {
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
{
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_SPACE);
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[] getBinaryValue() throws IOException
{
return getBinaryValue(Base64Variants.getDefaultVariant());
}
/*
/**********************************************************
/* General type coercions
/**********************************************************
*/
@Override
public String asText() {
return _value;
}
// note: neither fast nor elegant, but these work for now:
@Override
public boolean asBoolean(boolean defaultValue) {
if (_value != null) {
if ("true".equals(_value.trim())) {
return true;
}
}
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, JsonProcessingException
{
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.getClass() != getClass()) { // final class, can do this
return false;
}
return ((TextNode) o)._value.equals(_value);
}
@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 <= INT_SPACE) {
base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
} else if (b64variant.usesPaddingChar(ch)) {
base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
} else {
base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
}
if (msg != null) {
base = base + ": " + msg;
}
throw new JsonParseException(base, JsonLocation.NA);
}
protected void _reportBase64EOF()
throws JsonParseException
{
throw new JsonParseException("Unexpected end-of-String when base64 content", JsonLocation.NA);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/BooleanNode.java 0000644 0001750 0001750 00000004051 11655120726 027402 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
/**
* This concrete value class is used to contain boolean (true / false)
* values. Only two instances are ever created, to minimize memory
* usage
*/
public final class BooleanNode
extends ValueNode
{
// // Just need two instances...
public final static BooleanNode TRUE = new BooleanNode();
public final static BooleanNode FALSE = new BooleanNode();
private BooleanNode() { }
public static BooleanNode getTrue() { return TRUE; }
public static BooleanNode getFalse() { return FALSE; }
public static BooleanNode valueOf(boolean b) { return b ? TRUE : FALSE; }
// Interesting... two choices...
@Override public JsonToken asToken() {
return (this == TRUE) ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
}
@Override
public boolean isBoolean() { return true; }
@Override
public boolean getBooleanValue() {
return (this == TRUE);
}
@Override
public String asText() {
return (this == TRUE) ? "true" : "false";
}
@Override
public boolean asBoolean() {
return (this == TRUE);
}
@Override
public boolean asBoolean(boolean defaultValue) {
return (this == TRUE);
}
@Override
public int asInt(int defaultValue) {
return (this == TRUE) ? 1 : 0;
}
@Override
public long asLong(long defaultValue) {
return (this == TRUE) ? 1L : 0L;
}
@Override
public double asDouble(double defaultValue) {
return (this == TRUE) ? 1.0 : 0.0;
}
@Override
public final void serialize(JsonGenerator jg, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jg.writeBoolean(this == TRUE);
}
@Override
public boolean equals(Object o)
{
/* Since there are only ever two instances in existence
* can do identity comparison
*/
return (o == this);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/IntNode.java 0000644 0001750 0001750 00000006100 11655120726 026552 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.NumberOutput;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Numeric node that contains simple 32-bit integer values.
*/
public final 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
*/
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 getNumberType() { return JsonParser.NumberType.INT; }
/*
/**********************************************************
/* Overrridden JsonNode methods
/**********************************************************
*/
@Override
public boolean isIntegralNumber() { return true; }
@Override
public boolean isInt() { return true; }
@Override
public Number getNumberValue() {
return Integer.valueOf(_value);
}
@Override
public int getIntValue() { return _value; }
@Override
public long getLongValue() { return (long) _value; }
@Override
public double getDoubleValue() { return (double) _value; }
@Override
public BigDecimal getDecimalValue() { return BigDecimal.valueOf(_value); }
@Override
public BigInteger getBigIntegerValue() { 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.getClass() != getClass()) { // final class, can do this
return false;
}
return ((IntNode) o)._value == _value;
}
@Override
public int hashCode() { return _value; }
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/JsonNodeFactory.java 0000644 0001750 0001750 00000016444 11655120726 030275 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 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
{
/**
* 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 = new JsonNodeFactory();
protected JsonNodeFactory() { }
/*
/**********************************************************
/* Factory methods for literal values
/**********************************************************
*/
/**
* Factory method for getting an instance of JSON boolean value
* (either literal 'true' or 'false')
*/
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)
*/
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
*/
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}.
*
* @since 1.9
*/
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
*/
public NumericNode numberNode(short 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}.
*
* @since 1.9
*/
public ValueNode numberNode(Short value) {
return (value == null) ? nullNode() : IntNode.valueOf(value.shortValue());
}
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given 32-bit integer value
*/
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}.
*
* @since 1.9
*/
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
*/
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}.
*
* @since 1.9
*/
public ValueNode numberNode(Long value) {
return (value == null) ? nullNode() : LongNode.valueOf(value.longValue());
}
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given unlimited range integer value
*/
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
*/
public NumericNode numberNode(float v) { return DoubleNode.valueOf((double) 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}.
*
* @since 1.9
*/
public ValueNode numberNode(Float value) {
return (value == null) ? nullNode() : DoubleNode.valueOf(value.doubleValue());
}
/**
* Factory method for getting an instance of JSON numeric value
* that expresses given 64-bit floating point value
*/
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}.
*
* @since 1.9
*/
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
*/
public NumericNode numberNode(BigDecimal v) { return DecimalNode.valueOf(v); }
/*
/**********************************************************
/* Factory methods for textual values
/**********************************************************
*/
/**
* Factory method for constructing a node that represents JSON
* String value
*/
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
*/
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
*/
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
*/
public ArrayNode arrayNode() { return new ArrayNode(this); }
/**
* Factory method for constructing an empty JSON Object ("struct") node
*/
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.
*/
public POJONode POJONode(Object pojo) { return new POJONode(pojo); }
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/ObjectNode.java 0000644 0001750 0001750 00000047442 11655120726 027244 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.TypeSerializer;
/**
* Node that maps to JSON Object structures in JSON content.
*/
public class ObjectNode
extends ContainerNode
{
protected LinkedHashMap _children = null;
public ObjectNode(JsonNodeFactory nc) { super(nc); }
/*
/**********************************************************
/* Implementation of core JsonNode API
/**********************************************************
*/
@Override public JsonToken asToken() { return JsonToken.START_OBJECT; }
@Override
public boolean isObject() { return true; }
@Override
public int size() {
return (_children == null) ? 0 : _children.size();
}
@Override
public Iterator getElements()
{
return (_children == null) ? NoNodesIterator.instance() : _children.values().iterator();
}
@Override
public JsonNode get(int index) { return null; }
@Override
public JsonNode get(String fieldName)
{
if (_children != null) {
return _children.get(fieldName);
}
return null;
}
@Override
public Iterator getFieldNames()
{
return (_children == null) ? NoStringsIterator.instance() : _children.keySet().iterator();
}
@Override
public JsonNode path(int index)
{
return MissingNode.getInstance();
}
@Override
public JsonNode path(String fieldName)
{
if (_children != null) {
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> getFields()
{
if (_children == null) {
return NoFieldsIterator.instance;
}
return _children.entrySet().iterator();
}
@Override
public ObjectNode with(String propertyName)
{
if (_children == null) {
_children = new LinkedHashMap();
} else {
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;
}
/*
/**********************************************************
/* Public API, finding value nodes
/**********************************************************
*/
@Override
public JsonNode findValue(String fieldName)
{
if (_children != null) {
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)
{
if (_children != null) {
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)
{
if (_children != null) {
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)
{
if (_children != null) {
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)
{
if (_children != null) {
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 final void serialize(JsonGenerator jg, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jg.writeStartObject();
if (_children != null) {
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);
if (_children != null) {
for (Map.Entry en : _children.entrySet()) {
jg.writeFieldName(en.getKey());
((BaseJsonNode) en.getValue()).serialize(jg, provider);
}
}
typeSer.writeTypeSuffixForObject(this, jg);
}
/*
/**********************************************************
/* 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.
*/
public JsonNode put(String fieldName, JsonNode value)
{
if (value == null) { // let's not store 'raw' nulls but nodes
value = nullNode();
}
return _put(fieldName, value);
}
/**
* Method for removing field entry from this ObjectNode.
* Will return value of the field, if such field existed;
* null if not.
*/
public JsonNode remove(String fieldName)
{
if (_children != null) {
return _children.remove(fieldName);
}
return null;
}
/**
* Method for removing specified field properties out of
* this ObjectNode.
*
* @param fieldNames Names of fields to remove
*
* @return This ObjectNode after removing entries
*
* @since 1.6
*/
public ObjectNode remove(Collection fieldNames)
{
if (_children != null) {
for (String fieldName : fieldNames) {
_children.remove(fieldName);
}
}
return this;
}
/**
* Method for removing all field properties, such that this
* ObjectNode will contain no properties after call.
*/
@Override
public ObjectNode removeAll()
{
_children = null;
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 (to allow chaining)
*
* @since 1.3
*/
public JsonNode putAll(Map properties)
{
if (_children == null) {
_children = new LinkedHashMap(properties);
} else {
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 (to allow chaining)
*
* @since 1.3
*/
public JsonNode putAll(ObjectNode other)
{
int len = other.size();
if (len > 0) {
if (_children == null) {
_children = new LinkedHashMap(len);
}
other.putContentsTo(_children);
}
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 ObjectNode (to allow call chaining)
*
* @since 1.6
*/
public ObjectNode retain(Collection fieldNames)
{
if (_children != null) {
Iterator> entries = _children.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = entries.next();
if (!fieldNames.contains(entry.getKey())) {
entries.remove();
}
}
}
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 ObjectNode (to allow call chaining)
*
* @since 1.6
*/
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.
*
* @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.
*
* @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;
}
public void putPOJO(String fieldName, Object pojo)
{
_put(fieldName, POJONode(pojo));
}
public void putNull(String fieldName)
{
_put(fieldName, nullNode());
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void put(String fieldName, int v) { _put(fieldName, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void put(String fieldName, Integer value) {
if (value == null) {
_put(fieldName, nullNode());
} else {
_put(fieldName, numberNode(value.intValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void put(String fieldName, long v) { _put(fieldName, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void put(String fieldName, Long value) {
if (value == null) {
_put(fieldName, nullNode());
} else {
_put(fieldName, numberNode(value.longValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void put(String fieldName, float v) { _put(fieldName, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void put(String fieldName, Float value) {
if (value == null) {
_put(fieldName, nullNode());
} else {
_put(fieldName, numberNode(value.floatValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void put(String fieldName, double v) { _put(fieldName, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void put(String fieldName, Double value) {
if (value == null) {
_put(fieldName, nullNode());
} else {
_put(fieldName, numberNode(value.doubleValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void put(String fieldName, BigDecimal v) {
if (v == null) {
putNull(fieldName);
} else {
_put(fieldName, numberNode(v));
}
}
/**
* Method for setting value of a field to specified String value.
*/
public void put(String fieldName, String v) {
if (v == null) {
putNull(fieldName);
} else {
_put(fieldName, textNode(v));
}
}
/**
* Method for setting value of a field to specified String value.
*/
public void put(String fieldName, boolean v) { _put(fieldName, booleanNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void put(String fieldName, Boolean value) {
if (value == null) {
_put(fieldName, nullNode());
} else {
_put(fieldName, booleanNode(value.booleanValue()));
}
}
/**
* Method for setting value of a field to specified binary value
*/
public void put(String fieldName, byte[] v) {
if (v == null) {
_put(fieldName, nullNode());
} else {
_put(fieldName, binaryNode(v));
}
}
/*
/**********************************************************
/* Package methods (for other node classes to use)
/**********************************************************
*/
/**
* @since 1.6
*/
protected void putContentsTo(Map dst)
{
if (_children != null) {
for (Map.Entry en : _children.entrySet()) {
dst.put(en.getKey(), en.getValue());
}
}
}
/*
/**********************************************************
/* Standard methods
/**********************************************************
*/
@Override
public boolean equals(Object o)
{
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) {
return false;
}
ObjectNode other = (ObjectNode) o;
if (other.size() != size()) {
return false;
}
if (_children != null) {
for (Map.Entry en : _children.entrySet()) {
String key = en.getKey();
JsonNode value = en.getValue();
JsonNode otherValue = other.get(key);
if (otherValue == null || !otherValue.equals(value)) {
return false;
}
}
}
return true;
}
@Override
public int hashCode()
{
return (_children == null) ? -1 : _children.hashCode();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(32 + (size() << 4));
sb.append("{");
if (_children != null) {
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
/**********************************************************
*/
private final JsonNode _put(String fieldName, JsonNode value)
{
if (_children == null) {
_children = new LinkedHashMap();
}
return _children.put(fieldName, value);
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* For efficiency, let's share the "no fields" iterator...
*/
protected static class NoFieldsIterator
implements Iterator>
{
final static NoFieldsIterator instance = new NoFieldsIterator();
private NoFieldsIterator() { }
@Override
public boolean hasNext() { return false; }
@Override
public Map.Entry next() { throw new NoSuchElementException(); }
@Override
public void remove() { // or IllegalOperationException?
throw new IllegalStateException();
}
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/ContainerNode.java 0000644 0001750 0001750 00000013434 11655120726 027752 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonToken;
/**
* This intermediate base class is used for all container nodes,
* specifically, array and object nodes.
*/
public abstract class ContainerNode
extends BaseJsonNode
{
/**
* We will keep a reference to the Object (usually TreeMapper)
* that can construct instances of nodes to add to this container
* node.
*/
JsonNodeFactory _nodeFactory;
protected ContainerNode(JsonNodeFactory nc)
{
_nodeFactory = nc;
}
@Override
public boolean isContainerNode() { return true; }
@Override
public abstract JsonToken asToken();
/* NOTE: must have separate implementations since semantics have
* slight difference; deprecated method must return null,
* new method empty string.
*/
@SuppressWarnings("deprecation")
@Override
public String getValueAsText() { return null; }
@Override
public String asText() { return ""; }
/*
/**********************************************************
/* Find methods; made abstract again to ensure implementation
/**********************************************************
*/
@Override
public abstract JsonNode findValue(String fieldName);
@Override
public abstract ObjectNode findParent(String fieldName);
@Override
public abstract List findValues(String fieldName, List foundSoFar);
@Override
public abstract List findParents(String fieldName, List foundSoFar);
@Override
public abstract List findValuesAsText(String fieldName, List foundSoFar);
/*
/**********************************************************
/* 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);
/*
/**********************************************************
/* NodeCreator implementation, just dispatch to
/* the real creator
/**********************************************************
*/
/**
* Factory method that constructs and returns an empty {@link ArrayNode}
* Construction is done using registered {@link JsonNodeFactory}.
*/
public final ArrayNode arrayNode() { return _nodeFactory.arrayNode(); }
/**
* Factory method that constructs and returns an empty {@link ObjectNode}
* Construction is done using registered {@link JsonNodeFactory}.
*/
public final ObjectNode objectNode() { return _nodeFactory.objectNode(); }
public final NullNode nullNode() { return _nodeFactory.nullNode(); }
public final BooleanNode booleanNode(boolean v) { return _nodeFactory.booleanNode(v); }
public final NumericNode numberNode(byte v) { return _nodeFactory.numberNode(v); }
public final NumericNode numberNode(short v) { return _nodeFactory.numberNode(v); }
public final NumericNode numberNode(int v) { return _nodeFactory.numberNode(v); }
public final NumericNode numberNode(long v) { return _nodeFactory.numberNode(v); }
public final NumericNode numberNode(float v) { return _nodeFactory.numberNode(v); }
public final NumericNode numberNode(double v) { return _nodeFactory.numberNode(v); }
public final NumericNode numberNode(BigDecimal v) { return (_nodeFactory.numberNode(v)); }
public final TextNode textNode(String text) { return _nodeFactory.textNode(text); }
public final BinaryNode binaryNode(byte[] data) { return _nodeFactory.binaryNode(data); }
public final BinaryNode binaryNode(byte[] data, int offset, int length) { return _nodeFactory.binaryNode(data, offset, length); }
public final POJONode POJONode(Object pojo) { return _nodeFactory.POJONode(pojo); }
/*
/**********************************************************
/* Common mutators
/**********************************************************
*/
/**
* Method for removing all children container has (if any)
*
* @return Container node itself (to allow method call chaining)
*
* @since 1.3
*/
public abstract ContainerNode removeAll();
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
protected static class NoNodesIterator
implements Iterator
{
final static NoNodesIterator instance = new NoNodesIterator();
private NoNodesIterator() { }
public static NoNodesIterator instance() { return instance; }
@Override
public boolean hasNext() { return false; }
@Override
public JsonNode next() { throw new NoSuchElementException(); }
@Override
public void remove() {
// could as well throw IllegalOperationException?
throw new IllegalStateException();
}
}
protected static class NoStringsIterator
implements Iterator
{
final static NoStringsIterator instance = new NoStringsIterator();
private NoStringsIterator() { }
public static NoStringsIterator instance() { return instance; }
@Override
public boolean hasNext() { return false; }
@Override
public String next() { throw new NoSuchElementException(); }
@Override
public void remove() {
// could as well throw IllegalOperationException?
throw new IllegalStateException();
}
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/LongNode.java 0000644 0001750 0001750 00000004554 11655120726 026732 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.NumberOutput;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Numeric node that contains simple 64-bit integer values.
*/
public final class LongNode
extends NumericNode
{
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 getNumberType() { return JsonParser.NumberType.LONG; }
@Override
public boolean isIntegralNumber() { return true; }
@Override
public boolean isLong() { return true; }
@Override
public Number getNumberValue() {
return Long.valueOf(_value);
}
@Override
public int getIntValue() { return (int) _value; }
@Override
public long getLongValue() { return _value; }
@Override
public double getDoubleValue() { return (double) _value; }
@Override
public BigDecimal getDecimalValue() { return BigDecimal.valueOf(_value); }
@Override
public BigInteger getBigIntegerValue() { 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.getClass() != getClass()) { // final class, can do this
return false;
}
return ((LongNode) o)._value == _value;
}
@Override
public int hashCode() {
return ((int) _value) ^ (int) (_value >> 32);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/NodeCursor.java 0000644 0001750 0001750 00000013744 11655120726 027311 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.util.*;
import org.codehaus.jackson.*;
/**
* 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.
*/
final NodeCursor _parent;
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 abstract String getCurrentName();
/*
/**********************************************************
/* 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 Array(n, this);
}
if (n.isObject()) {
return new Object(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 RootValue
extends NodeCursor
{
JsonNode _node;
protected boolean _done = false;
public RootValue(JsonNode n, NodeCursor p) {
super(JsonStreamContext.TYPE_ROOT, p);
_node = n;
}
@Override
public String getCurrentName() { return null; }
@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 Array
extends NodeCursor
{
Iterator _contents;
JsonNode _currentNode;
public Array(JsonNode n, NodeCursor p) {
super(JsonStreamContext.TYPE_ARRAY, p);
_contents = n.getElements();
}
@Override
public String getCurrentName() { return null; }
@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 Object
extends NodeCursor
{
Iterator> _contents;
Map.Entry _current;
boolean _needEntry;
public Object(JsonNode n, NodeCursor p)
{
super(JsonStreamContext.TYPE_OBJECT, p);
_contents = ((ObjectNode) n).getFields();
_needEntry = true;
}
@Override
public String getCurrentName() {
return (_current == null) ? null : _current.getKey();
}
@Override
public JsonToken nextToken()
{
// Need a new entry?
if (_needEntry) {
if (!_contents.hasNext()) {
_current = null;
return null;
}
_needEntry = false;
_current = _contents.next();
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;
}
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/ArrayNode.java 0000644 0001750 0001750 00000046272 11655120726 027114 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.TypeSerializer;
/**
* Node class that represents Arrays mapped from Json content.
*/
public final class ArrayNode
extends ContainerNode
{
protected ArrayList _children;
public ArrayNode(JsonNodeFactory nc) { super(nc); }
/*
/**********************************************************
/* Implementation of core JsonNode API
/**********************************************************
*/
@Override public JsonToken asToken() { return JsonToken.START_ARRAY; }
@Override
public boolean isArray() { return true; }
@Override
public int size()
{
return (_children == null) ? 0 : _children.size();
}
@Override
public Iterator getElements()
{
return (_children == null) ? NoNodesIterator.instance() : _children.iterator();
}
@Override
public JsonNode get(int index)
{
if (index >= 0 && (_children != null) && 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 && (_children != null) && index < _children.size()) {
return _children.get(index);
}
return MissingNode.getInstance();
}
/*
/**********************************************************
/* Public API, serialization
/**********************************************************
*/
@Override
public final void serialize(JsonGenerator jg, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jg.writeStartArray();
if (_children != null) {
for (JsonNode n : _children) {
/* 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)n).serialize(jg, provider);
}
}
jg.writeEndArray();
}
@Override
public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
typeSer.writeTypePrefixForArray(this, jg);
if (_children != null) {
for (JsonNode n : _children) {
((BaseJsonNode)n).serialize(jg, provider);
}
}
typeSer.writeTypeSuffixForArray(this, jg);
}
/*
/**********************************************************
/* Public API, finding value nodes
/**********************************************************
*/
@Override
public JsonNode findValue(String fieldName)
{
if (_children != null) {
for (JsonNode node : _children) {
JsonNode value = node.findValue(fieldName);
if (value != null) {
return value;
}
}
}
return null;
}
@Override
public List findValues(String fieldName, List foundSoFar)
{
if (_children != null) {
for (JsonNode node : _children) {
foundSoFar = node.findValues(fieldName, foundSoFar);
}
}
return foundSoFar;
}
@Override
public List findValuesAsText(String fieldName, List foundSoFar)
{
if (_children != null) {
for (JsonNode node : _children) {
foundSoFar = node.findValuesAsText(fieldName, foundSoFar);
}
}
return foundSoFar;
}
@Override
public ObjectNode findParent(String fieldName)
{
if (_children != null) {
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)
{
if (_children != null) {
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();
}
return _set(index, value);
}
public void add(JsonNode value)
{
if (value == null) { // let's not store 'raw' nulls but nodes
value = nullNode();
}
_add(value);
}
/**
* 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)
*
* @since 1.3
*/
public JsonNode addAll(ArrayNode other)
{
int len = other.size();
if (len > 0) {
if (_children == null) {
_children = new ArrayList(len+2);
}
other.addContentsTo(_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)
*
* @since 1.3
*/
public JsonNode addAll(Collection nodes)
{
int len = nodes.size();
if (len > 0) {
if (_children == null) {
_children = new ArrayList(nodes);
} else {
_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.
*/
public void insert(int index, JsonNode value)
{
if (value == null) {
value = nullNode();
}
_insert(index, value);
}
/**
* Method for removing an entry from this ArrayNode.
* Will return value of the entry at specified index, if entry existed;
* null if not.
*/
public JsonNode remove(int index)
{
if (index >= 0 && (_children != null) && index < _children.size()) {
return _children.remove(index);
}
return null;
}
@Override
public ArrayNode removeAll()
{
_children = null;
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.
*/
public void addPOJO(Object value)
{
if (value == null) {
addNull();
} else {
_add(POJONode(value));
}
}
public void addNull()
{
_add(nullNode());
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void add(int v) { _add(numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void add(Integer value) {
if (value == null) {
addNull();
} else {
_add(numberNode(value.intValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void add(long v) { _add(numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void add(Long value) {
if (value == null) {
addNull();
} else {
_add(numberNode(value.longValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void add(float v) { _add(numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void add(Float value) {
if (value == null) {
addNull();
} else {
_add(numberNode(value.floatValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void add(double v) { _add(numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void add(Double value) {
if (value == null) {
addNull();
} else {
_add(numberNode(value.doubleValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void add(BigDecimal v) {
if (v == null) {
addNull();
} else {
_add(numberNode(v));
}
}
/**
* Method for setting value of a field to specified String value.
*/
public void add(String v) {
if (v == null) {
addNull();
} else {
_add(textNode(v));
}
}
/**
* Method for setting value of a field to specified String value.
*/
public void add(boolean v) { _add(booleanNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void add(Boolean value) {
if (value == null) {
addNull();
} else {
_add(booleanNode(value.booleanValue()));
}
}
/**
* Method for setting value of a field to specified binary value
*/
public void add(byte[] v) {
if (v == null) {
addNull();
} else {
_add(binaryNode(v));
}
}
public ArrayNode insertArray(int index)
{
ArrayNode n = arrayNode();
_insert(index, 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 insertObject(int index)
{
ObjectNode n = objectNode();
_insert(index, n);
return n;
}
/**
* Method that will construct a POJONode and add it at the end
* of this array node.
*/
public void insertPOJO(int index, Object value)
{
if (value == null) {
insertNull(index);
} else {
_insert(index, POJONode(value));
}
}
public void insertNull(int index)
{
_insert(index, nullNode());
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void insert(int index, int v) { _insert(index, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void insert(int index, Integer value) {
if (value == null) {
insertNull(index);
} else {
_insert(index, numberNode(value.intValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void insert(int index, long v) { _insert(index, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void insert(int index, Long value) {
if (value == null) {
insertNull(index);
} else {
_insert(index, numberNode(value.longValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void insert(int index, float v) { _insert(index, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void insert(int index, Float value) {
if (value == null) {
insertNull(index);
} else {
_insert(index, numberNode(value.floatValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void insert(int index, double v) { _insert(index, numberNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void insert(int index, Double value) {
if (value == null) {
insertNull(index);
} else {
_insert(index, numberNode(value.doubleValue()));
}
}
/**
* Method for setting value of a field to specified numeric value.
*/
public void insert(int index, BigDecimal v) {
if (v == null) {
insertNull(index);
} else {
_insert(index, numberNode(v));
}
}
/**
* Method for setting value of a field to specified String value.
*/
public void insert(int index, String v) {
if (v == null) {
insertNull(index);
} else {
_insert(index, textNode(v));
}
}
/**
* Method for setting value of a field to specified String value.
*/
public void insert(int index, boolean v) { _insert(index, booleanNode(v)); }
/**
* Alternative method that we need to avoid bumping into NPE issues
* with auto-unboxing.
*
* @since 1.9
*/
public void insert(int index, Boolean value) {
if (value == null) {
insertNull(index);
} else {
_insert(index, booleanNode(value.booleanValue()));
}
}
/**
* Method for setting value of a field to specified binary value
*/
public void insert(int index, byte[] v) {
if (v == null) {
insertNull(index);
} else {
_insert(index, binaryNode(v));
}
}
/*
/**********************************************************
/* Package methods (for other node classes to use)
/**********************************************************
*/
/**
* @since 1.6
*/
protected void addContentsTo(List dst)
{
if (_children != null) {
for (JsonNode n : _children) {
dst.add(n);
}
}
}
/*
/**********************************************************
/* Standard methods
/**********************************************************
*/
@Override
public boolean equals(Object o)
{
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) { // final class, can do this
return false;
}
ArrayNode other = (ArrayNode) o;
if (_children == null || _children.size() == 0) {
return other.size() == 0;
}
return other._sameChildren(_children);
}
@Override
public int hashCode()
{
int hash;
if (_children == null) {
hash = 1;
} else {
hash = _children.size();
for (JsonNode n : _children) {
if (n != null) {
hash ^= n.hashCode();
}
}
}
return hash;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(16 + (size() << 4));
sb.append('[');
if (_children != null) {
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
/**********************************************************
*/
public JsonNode _set(int index, JsonNode value)
{
if (_children == null || index < 0 || index >= _children.size()) {
throw new IndexOutOfBoundsException("Illegal index "+index+", array size "+size());
}
return _children.set(index, value);
}
private void _add(JsonNode node)
{
if (_children == null) {
_children = new ArrayList();
}
_children.add(node);
}
private void _insert(int index, JsonNode node)
{
if (_children == null) {
_children = new ArrayList();
_children.add(node);
return;
}
if (index < 0) {
_children.add(0, node);
} else if (index >= _children.size()) {
_children.add(node);
} else {
_children.add(index, node);
}
}
/**
* Note: this method gets called iff otherChildren
* is non-empty
*/
private boolean _sameChildren(ArrayList otherChildren)
{
int len = otherChildren.size();
if (this.size() != len) { // important: call size() to handle case of null list...
return false;
}
for (int i = 0; i < len; ++i) {
if (!_children.get(i).equals(otherChildren.get(i))) {
return false;
}
}
return true;
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/TreeTraversingParser.java 0000644 0001750 0001750 00000025306 11655120726 031344 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.impl.JsonParserMinimalBase;
/**
* 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.
*
* @author tatu
*
* @since 1.3
*/
public class TreeTraversingParser extends JsonParserMinimalBase
{
/*
/**********************************************************
/* 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.Array(n, null);
} else if (n.isObject()) {
_nextToken = JsonToken.START_OBJECT;
_nodeCursor = new NodeCursor.Object(n, null);
} else { // value node
_nodeCursor = new NodeCursor.RootValue(n, null);
}
}
@Override
public void setCodec(ObjectCodec c) {
_objectCodec = c;
}
@Override
public ObjectCodec getCodec() {
return _objectCodec;
}
/*
/**********************************************************
/* 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 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().getTextValue();
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return String.valueOf(currentNode().getNumberValue());
case VALUE_EMBEDDED_OBJECT:
JsonNode n = currentNode();
if (n != null && n.isBinary()) {
// this will convert it to base64
return n.asText();
}
}
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.getNumberType();
}
@Override
public BigInteger getBigIntegerValue() throws IOException, JsonParseException
{
return currentNumericNode().getBigIntegerValue();
}
@Override
public BigDecimal getDecimalValue() throws IOException, JsonParseException {
return currentNumericNode().getDecimalValue();
}
@Override
public double getDoubleValue() throws IOException, JsonParseException {
return currentNumericNode().getDoubleValue();
}
@Override
public float getFloatValue() throws IOException, JsonParseException {
return (float) currentNumericNode().getDoubleValue();
}
@Override
public long getLongValue() throws IOException, JsonParseException {
return currentNumericNode().getLongValue();
}
@Override
public int getIntValue() throws IOException, JsonParseException {
return currentNumericNode().getIntValue();
}
@Override
public Number getNumberValue() throws IOException, JsonParseException {
return currentNumericNode().getNumberValue();
}
@Override
public Object getEmbeddedObject() {
if (!_closed) {
JsonNode n = currentNode();
if (n != null && n.isPojo()) {
return ((POJONode) n).getPojo();
}
}
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.getBinaryValue();
// (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;
}
/*
/**********************************************************
/* 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
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/package-info.java 0000644 0001750 0001750 00000000507 11655120726 027543 0 ustar jamespage jamespage /**
* Contains concrete {@link org.codehaus.jackson.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 org.codehaus.jackson.node;
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/DoubleNode.java 0000644 0001750 0001750 00000005272 11655120726 027243 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.NumberOutput;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Numeric node that contains 64-bit ("double precision")
* floating point values simple 32-bit integer values.
*/
public final 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 getNumberType() { return JsonParser.NumberType.DOUBLE; }
/*
/**********************************************************
/* Overrridden JsonNode methods
/**********************************************************
*/
@Override
public boolean isFloatingPointNumber() { return true; }
@Override
public boolean isDouble() { return true; }
@Override
public Number getNumberValue() {
return Double.valueOf(_value);
}
@Override
public int getIntValue() { return (int) _value; }
@Override
public long getLongValue() { return (long) _value; }
@Override
public double getDoubleValue() { return _value; }
@Override
public BigDecimal getDecimalValue() { return BigDecimal.valueOf(_value); }
@Override
public BigInteger getBigIntegerValue() {
return getDecimalValue().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.getClass() != getClass()) { // final class, can do this
return false;
}
return ((DoubleNode) o)._value == _value;
}
@Override
public int hashCode()
{
// same as hashCode Double.class uses
long l = Double.doubleToLongBits(_value);
return ((int) l) ^ (int) (l >> 32);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/POJONode.java 0000644 0001750 0001750 00000006537 11655120726 026605 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Value node that contains a wrapped POJO, to be serialized as
* a JSON constructed through data mapping (usually done by
* calling {@link org.codehaus.jackson.map.ObjectMapper}).
*/
public final class POJONode
extends ValueNode
{
protected final Object _value;
public POJONode(Object v) { _value = v; }
/*
/**********************************************************
/* Base class overrides
/**********************************************************
*/
@Override public JsonToken asToken() { return JsonToken.VALUE_EMBEDDED_OBJECT; }
@Override
public boolean isPojo() { return true; }
/*
/**********************************************************
/* General type coercions
/**********************************************************
*/
@Override
public String asText() {
return (_value == null) ? "null" : _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 jg, SerializerProvider provider)
throws IOException, JsonProcessingException
{
if (_value == null) {
jg.writeNull();
} else {
jg.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.getClass() != getClass()) { // final class, can do this
return false;
}
POJONode other = (POJONode) o;
if (_value == null) {
return other._value == null;
}
return _value.equals(other._value);
}
@Override
public int hashCode() { return _value.hashCode(); }
@Override
public String toString()
{
return String.valueOf(_value);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/MissingNode.java 0000644 0001750 0001750 00000005317 11655120726 027442 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.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 BaseJsonNode
{
private final static MissingNode instance = new MissingNode();
private MissingNode() { }
public static MissingNode getInstance() { return instance; }
@Override public JsonToken asToken() { return JsonToken.NOT_AVAILABLE; }
@Override
public boolean isMissingNode() { return true; }
@Override
public String asText() { return null; }
@Override
public int asInt(int defaultValue) {
return 0;
}
@Override
public long asLong(long defaultValue) {
return 0L;
}
@Override
public double asDouble(double defaultValue) {
return 0.0;
}
@Override
public JsonNode path(String fieldName) { return this; }
@Override
public JsonNode path(int index) { return this; }
@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.
*/
jg.writeNull();
}
@Override
public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
jg.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 "";
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/DecimalNode.java 0000644 0001750 0001750 00000004767 11655120726 027377 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Numeric node that contains values that do not fit in simple
* integer (int, long) or floating point (double) values.
*/
public final class DecimalNode
extends NumericNode
{
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 getNumberType() { return JsonParser.NumberType.BIG_DECIMAL; }
/*
/**********************************************************
/* Overrridden JsonNode methods
/**********************************************************
*/
@Override
public boolean isFloatingPointNumber() { return true; }
@Override
public boolean isBigDecimal() { return true; }
@Override
public Number getNumberValue() { return _value; }
@Override
public int getIntValue() { return _value.intValue(); }
@Override
public long getLongValue() { return _value.longValue(); }
@Override
public BigInteger getBigIntegerValue() { return _value.toBigInteger(); }
@Override
public double getDoubleValue() { return _value.doubleValue(); }
@Override
public BigDecimal getDecimalValue() { return _value; }
@Override
public String asText() {
return _value.toString();
}
@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.getClass() != getClass()) { // final class, can do this
return false;
}
return ((DecimalNode) o)._value.equals(_value);
}
@Override
public int hashCode() { return _value.hashCode(); }
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/BinaryNode.java 0000644 0001750 0001750 00000005635 11655120726 027260 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.util.Arrays;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Value node that contains Base64 encoded binary value, which will be
* output and stored as Json String value.
*/
public final class BinaryNode
extends ValueNode
{
final static BinaryNode EMPTY_BINARY_NODE = new BinaryNode(new byte[0]);
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 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;
}
@Override
public boolean isBinary() { return true; }
/**
*
* 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[] getBinaryValue() { 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(_data);
}
@Override
public boolean equals(Object o)
{
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) { // final class, can do this
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);
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/ValueNode.java 0000644 0001750 0001750 00000003247 11655120726 027105 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.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
public boolean isValueNode() { return true; }
@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);
}
/*
/**********************************************************************
/* Public API, path handling
/**********************************************************************
*/
@Override
public JsonNode path(String fieldName) { return MissingNode.getInstance(); }
@Override
public JsonNode path(int index) { return MissingNode.getInstance(); }
/*
/**********************************************************************
/* Base impls for standard methods
/**********************************************************************
*/
@Override
public String toString() { return asText(); }
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/BaseJsonNode.java 0000644 0001750 0001750 00000006525 11655120726 027537 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.util.List;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.JsonSerializableWithType;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.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 JsonSerializableWithType}.
* This simplifies object mapping
* aspects a bit, as no external serializers are needed.
*/
public abstract class BaseJsonNode
extends JsonNode
implements JsonSerializableWithType
{
protected BaseJsonNode() { }
/*
/**********************************************************
/* Basic definitions for non-container types
/**********************************************************
*/
@Override
public JsonNode findValue(String fieldName) {
return null;
}
@Override
public final JsonNode findPath(String fieldName)
{
JsonNode value = findValue(fieldName);
if (value == null) {
return MissingNode.getInstance();
}
return value;
}
// note: co-variant return type
@Override
public ObjectNode findParent(String fieldName) {
return null;
}
@Override
public List findValues(String fieldName, List foundSoFar) {
return foundSoFar;
}
@Override
public List findValuesAsText(String fieldName, List foundSoFar) {
return foundSoFar;
}
@Override
public List findParents(String fieldName, List foundSoFar) {
return foundSoFar;
}
/*
/**********************************************************
/* Support for traversal-as-stream
/**********************************************************
*/
@Override
public JsonParser traverse() {
return new TreeTraversingParser(this);
}
/**
* 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)
*
* @since 1.3
*/
@Override
public abstract JsonToken asToken();
/**
* @since 1.3
*/
@Override
public JsonParser.NumberType getNumberType() {
// most types non-numeric, so:
return null;
}
/*
/**********************************************************
/* JsonSerializable
/**********************************************************
*/
/**
* Method called to serialize node instances using given generator.
*/
@SuppressWarnings("deprecation")
@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;
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/node/BigIntegerNode.java 0000644 0001750 0001750 00000005117 11655120726 030046 0 ustar jamespage jamespage package org.codehaus.jackson.node;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.SerializerProvider;
/**
* Numeric node that contains simple 64-bit integer values.
*/
public final class BigIntegerNode
extends NumericNode
{
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 getNumberType() { return JsonParser.NumberType.BIG_INTEGER; }
@Override
public boolean isIntegralNumber() { return true; }
@Override
public boolean isBigInteger() { return true; }
@Override
public Number getNumberValue() {
return _value;
}
@Override
public int getIntValue() { return _value.intValue(); }
@Override
public long getLongValue() { return _value.longValue(); }
@Override
public BigInteger getBigIntegerValue() { return _value; }
@Override
public double getDoubleValue() { return _value.doubleValue(); }
@Override
public BigDecimal getDecimalValue() { 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.getClass() != getClass()) { // final class, can do this
return false;
}
return ((BigIntegerNode) o)._value == _value;
}
@Override
public int hashCode() {
return _value.hashCode();
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/map/ 0000755 0001750 0001750 00000000000 11672662540 024206 5 ustar jamespage jamespage jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/map/ResolvableSerializer.java 0000644 0001750 0001750 00000001615 11655120726 031200 0 ustar jamespage jamespage package org.codehaus.jackson.map;
/**
* 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.
*/
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).
*
* @param provider Provider that has constructed serializer this method
* is called on.
*/
public abstract void resolve(SerializerProvider provider)
throws JsonMappingException;
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/map/AnnotationIntrospector.java 0000644 0001750 0001750 00000152423 11655120726 031602 0 ustar jamespage jamespage package org.codehaus.jackson.map;
import java.lang.annotation.Annotation;
import java.util.*;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.deser.ValueInstantiator;
import org.codehaus.jackson.map.introspect.*;
import org.codehaus.jackson.map.jsontype.NamedType;
import org.codehaus.jackson.map.jsontype.TypeResolverBuilder;
/**
* 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.
*
* 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.
*/
public abstract class AnnotationIntrospector
{
/*
/**********************************************************
/* Helper types
/**********************************************************
*/
/**
* Value type used with managed and back references; contains type and
* logic name, used to link related references
*
* @since 1.6
*/
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 org.codehaus.jackson.annotate.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 org.codehaus.jackson.annotate.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.
*
* @since 1.3
*/
public static AnnotationIntrospector nopInstance() {
return NopAnnotationIntrospector.instance;
}
public static AnnotationIntrospector pair(AnnotationIntrospector a1, AnnotationIntrospector a2) {
return new Pair(a1, a2);
}
/*
/**********************************************************
/* Access to possibly chained introspectors (1.7)
/**********************************************************
*/
/**
* 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;
}
/*
/**********************************************************
/* Generic annotation properties, lookup
/**********************************************************
*/
/**
* Method called by framework to determine whether given annotation
* is handled by this introspector.
*/
public abstract boolean isHandled(Annotation ann);
/*
/**********************************************************
/* General class annotations
/**********************************************************
*/
/**
* Method that checks whether specified class has annotations
* that indicate that it is (or is not) cachable. Exact
* semantics depend on type of class annotated and using
* class (factory or provider).
*
* Currently only used
* with deserializers, to determine whether provider
* should cache instances, and if no annotations are found,
* assumes non-cachable instances.
*
* @return True, if class is considered cachable within context,
* False if not, and null if introspector does not care either
* way.
*/
public Boolean findCachability(AnnotatedClass ac) {
return null;
}
/**
* 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.
*
* @since 1.3
*/
public abstract String findRootName(AnnotatedClass ac);
/**
* 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.
*
* @since 1.4
*/
public abstract String[] findPropertiesToIgnore(AnnotatedClass ac);
/**
* Method for checking whether an annotation indicates that all unknown properties
*
* @since 1.4
*/
public abstract Boolean findIgnoreUnknownProperties(AnnotatedClass ac);
/**
* 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')
*
* @since 1.7
*/
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(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).
*
* @since 1.5
*/
public VisibilityChecker> findAutoDetectVisibility(AnnotatedClass ac, VisibilityChecker> checker) {
return checker;
}
/*
/**********************************************************
/* Class annotations for Polymorphic type handling (1.5+)
/**********************************************************
*/
/**
* 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
*
* @since 1.5 -- although changed in 1.8 to pass configuration object
*/
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
*
* @since 1.5 -- although changed in 1.8 to pass configuration object
*/
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
*
* @since 1.5 -- although changed in 1.8 to pass configuration object
*/
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
*
* @since 1.5
*/
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
*
* @since 1.5
*/
public String findTypeName(AnnotatedClass ac) {
return null;
}
/*
/**********************************************************
/* General member (field, method/constructor) annotations
/**********************************************************
*/
/**
* Note: defined as non-abstract to reduce fragility between
* versions.
*
* @since 1.6
*/
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)
*
* @since 1.9
*/
public Boolean shouldUnwrapProperty(AnnotatedMember member) {
return null;
}
/**
* Method called to check whether given property is marked to
* be ignored; but NOT to determine if it should necessarily
* be ignored, since that may depend on other factors.
*
* Default implementation calls existing 'isIgnored' methods
* such as {@link #isIgnorableField(AnnotatedField)} and
* {@link #isIgnorableMethod(AnnotatedMethod)}.
*
* @since 1.9
*/
public boolean hasIgnoreMarker(AnnotatedMember m)
{
/* For maximum backwards compatibility, we better call
* existing methods.
*/
/* TODO: For 2.0, replace with simple 'return false;'
*/
if (m instanceof AnnotatedMethod) {
return isIgnorableMethod((AnnotatedMethod) m);
}
if (m instanceof AnnotatedField) {
return isIgnorableField((AnnotatedField) m);
}
if (m instanceof AnnotatedConstructor) {
return isIgnorableConstructor((AnnotatedConstructor) 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;
}
/*
/**********************************************************
/* General method annotations
/**********************************************************
*/
/**
* Method for checking whether there is an annotation that
* indicates that given method should be ignored for all
* operations (serialization, deserialization).
*
* Note that this method should ONLY return true for such
* explicit ignoral cases; and not if method just happens not to
* be visible for annotation processor.
*
* @return True, if an annotation is found to indicate that the
* method should be ignored; false if not.
*/
public abstract boolean isIgnorableMethod(AnnotatedMethod m);
/**
* @since 1.2
*/
public abstract boolean isIgnorableConstructor(AnnotatedConstructor c);
/*
/**********************************************************
/* General field annotations
/**********************************************************
*/
/**
* Method for checking whether there is an annotation that
* indicates that given field should be ignored for all
* operations (serialization, deserialization).
*
* @return True, if an annotation is found to indicate that the
* field should be ignored; false if not.
*/
public abstract boolean isIgnorableField(AnnotatedField f);
/*
/**********************************************************
/* 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
); if value of different
* type is returned, a runtime exception may be thrown by caller.
*
* Note: this variant was briefly deprecated for 1.7; should not be
*/
public abstract Object findSerializer(Annotated am);
/**
* 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
); if value of different
* type is returned, a runtime exception may be thrown by caller.
*
* @since 1.8
*/
public Class extends JsonSerializer>> 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
); if value of different
* type is returned, a runtime exception may be thrown by caller.
*
* @since 1.8
*/
public Class extends JsonSerializer>> findContentSerializer(Annotated am) {
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
*
* @return Enumerated value indicating which properties to include
* in serialization
*/
public JsonSerialize.Inclusion findSerializationInclusion(Annotated a, JsonSerialize.Inclusion defValue) {
return defValue;
}
/**
* 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
*/
public abstract Class> findSerializationType(Annotated a);
/**
* 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
*/
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
*/
public Class> findSerializationContentType(Annotated am, JavaType baseType) {
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.
*
* @since 1.2
*
* @return Typing mode to use, if annotation is found; null otherwise
*/
public abstract JsonSerialize.Typing findSerializationTyping(Annotated a);
/**
* Method for checking if annotated serializable property (represented by
* field or getter method) has definitions for views it is to be included
* in. If null is returned, no view definitions exist and property is always
* included; 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 serializable property (field or getter method)
* @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 abstract Class>[] findSerializationViews(Annotated a);
/*
/**********************************************************
/* Serialization: class annotations
/**********************************************************
*/
/**
* Method for accessing defined property serialization order (which may be
* partial). May return null if no ordering is defined.
*
* @since 1.4
*/
public abstract String[] findSerializationPropertyOrder(AnnotatedClass ac);
/**
* Method for checking whether an annotation indicates that serialized properties
* for which no explicit is defined should be alphabetically (lexicograpically)
* ordered
*
* @since 1.4
*/
public abstract Boolean findSerializationSortAlphabetically(AnnotatedClass ac);
/*
/**********************************************************
/* Serialization: method annotations
/**********************************************************
*/
/**
* Method for checking whether given method has an annotation
* that suggests property name associated with method that
* may be a "getter". Should return null if no annotation
* is found; otherwise a non-null String.
* If non-null value is returned, it is used as the property
* name, except for empty String ("") which is taken to mean
* "use standard bean name detection if applicable;
* method name if not".
*/
public abstract String findGettablePropertyName(AnnotatedMethod am);
/**
* 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 abstract boolean hasAsValueAnnotation(AnnotatedMethod am);
/**
* 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 abstract String findEnumValue(Enum> value);
/*
/**********************************************************
/* Serialization: field annotations
/**********************************************************
*/
/**
* Method for checking whether given member field represent
* a serializable logical property; and if so, returns the
* name of that property.
* Should return null if no annotation is found (indicating it
* is not a serializable field); otherwise a non-null String.
* If non-null value is returned, it is used as the property
* name, except for empty String ("") which is taken to mean
* "use the field name as is".
*/
public abstract String findSerializablePropertyName(AnnotatedField af);
/*
/**********************************************************
/* 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
); if value of different
* type is returned, a runtime exception may be thrown by caller.
*
* Note: this variant was briefly deprecated for 1.7; but it turns out
* we really should not try to push BeanProperty through at this point
*/
public abstract Object findDeserializer(Annotated am);
/**
* 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
); if value of different
* type is returned, a runtime exception may be thrown by caller.
*
* @since 1.3
*/
public abstract Class extends KeyDeserializer> findKeyDeserializer(Annotated am);
/**
* 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
); if value of different
* type is returned, a runtime exception may be thrown by caller.
*
* @since 1.3
*/
public abstract Class extends JsonDeserializer>> findContentDeserializer(Annotated am);
/**
* Method for accessing annotated type definition that a
* method can have, to be used as the type for serialization
* instead of the runtime type.
* Type must be a 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
* @param propName Logical property name of the property that uses
* type, if known; null for types not associated with property
*
* @return Class to use for deserialization instead of declared type
*/
public abstract Class> findDeserializationType(Annotated am, JavaType baseType,
String propName);
/**
* 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
* @param propName Logical property name of the property that uses
* type, if known; null for types not associated with property
*
* @return Class specifying more specific type to use instead of
* declared type, if annotation found; null if not
*/
public abstract Class> findDeserializationKeyType(Annotated am, JavaType baseKeyType,
String propName);
/**
* 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
* @param propName Logical property name of the property that uses
* type, if known; null for types not associated with property
*
* @return Class specifying more specific type to use instead of
* declared type, if annotation found; null if not
*/
public abstract Class> findDeserializationContentType(Annotated am, JavaType baseContentType,
String propName);
/*
/**********************************************************
/* 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.
*
* @since 1.9
*/
public Object findValueInstantiator(AnnotatedClass ac) {
return null;
}
/*
/**********************************************************
/* Deserialization: method annotations
/**********************************************************
*/
/**
* Method for checking whether given method has an annotation
* that suggests property name associated with method that
* may be a "setter". Should return null if no annotation
* is found; otherwise a non-null String.
* If non-null value is returned, it is used as the property
* name, except for empty String ("") which is taken to mean
* "use standard bean name detection if applicable;
* method name if not".
*/
public abstract String findSettablePropertyName(AnnotatedMethod am);
/**
* 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
*
* @since 1.6
*/
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;
}
/*
/**********************************************************
/* Deserialization: field annotations
/**********************************************************
*/
/**
* Method for checking whether given member field represent
* a deserializable logical property; and if so, returns the
* name of that property.
* Should return null if no annotation is found (indicating it
* is not a deserializable field); otherwise a non-null String.
* If non-null value is returned, it is used as the property
* name, except for empty String ("") which is taken to mean
* "use the field name as is".
*/
public abstract String findDeserializablePropertyName(AnnotatedField af);
/*
/**********************************************************
/* Deserialization: parameter annotations (for
/* creator method parameters)
/**********************************************************
*/
/**
* Method for checking whether given set of annotations indicates
* property name for associated parameter.
* No actual parameter object can be passed since JDK offers no
* representation; just annotations.
*/
public abstract String findPropertyNameForParam(AnnotatedParameter param);
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* 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 (available
* since version 1.1).
*/
public static class Pair
extends AnnotationIntrospector
{
protected final AnnotationIntrospector _primary, _secondary;
public Pair(AnnotationIntrospector p,
AnnotationIntrospector s)
{
_primary = p;
_secondary = s;
}
/**
* 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)
*
* @since 1.7
*/
public static AnnotationIntrospector create(AnnotationIntrospector primary,
AnnotationIntrospector secondary)
{
if (primary == null) {
return secondary;
}
if (secondary == null) {
return primary;
}
return new Pair(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 isHandled(Annotation ann)
{
return _primary.isHandled(ann) || _secondary.isHandled(ann);
}
/*
/******************************************************
/* General class annotations
/******************************************************
*/
@Override
public Boolean findCachability(AnnotatedClass ac)
{
Boolean result = _primary.findCachability(ac);
if (result == null) {
result = _secondary.findCachability(ac);
}
return result;
}
@Override
public String findRootName(AnnotatedClass ac)
{
String name1 = _primary.findRootName(ac);
if (name1 == null) {
return _secondary.findRootName(ac);
} else if (name1.length() > 0) {
return name1;
}
// name1 is empty; how about secondary?
String name2 = _secondary.findRootName(ac);
return (name2 == null) ? name1 : name2;
}
@Override
public String[] findPropertiesToIgnore(AnnotatedClass ac)
{
String[] result = _primary.findPropertiesToIgnore(ac);
if (result == null) {
result = _secondary.findPropertiesToIgnore(ac);
}
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(AnnotatedClass ac)
{
Object id = _primary.findFilterId(ac);
if (id == null) {
id = _secondary.findFilterId(ac);
}
return id;
}
/*
/******************************************************
/* 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 ref = _primary.findReferenceType(member);
if (ref == null) {
ref = _secondary.findReferenceType(member);
}
return ref;
}
@Override
public Boolean shouldUnwrapProperty(AnnotatedMember member)
{
Boolean value = _primary.shouldUnwrapProperty(member);
if (value == null) {
value = _secondary.shouldUnwrapProperty(member);
}
return value;
}
@Override
public Object findInjectableValueId(AnnotatedMember m)
{
Object value = _primary.findInjectableValueId(m);
if (value == null) {
value = _secondary.findInjectableValueId(m);
}
return value;
}
@Override
public boolean hasIgnoreMarker(AnnotatedMember m) {
return _primary.hasIgnoreMarker(m) || _secondary.hasIgnoreMarker(m);
}
// // // General method annotations
@Override
public boolean isIgnorableMethod(AnnotatedMethod m) {
return _primary.isIgnorableMethod(m) || _secondary.isIgnorableMethod(m);
}
@Override
public boolean isIgnorableConstructor(AnnotatedConstructor c) {
return _primary.isIgnorableConstructor(c) || _secondary.isIgnorableConstructor(c);
}
// // // General field annotations
@Override
public boolean isIgnorableField(AnnotatedField f)
{
return _primary.isIgnorableField(f) || _secondary.isIgnorableField(f);
}
// // // Serialization: general annotations
@Override
public Object findSerializer(Annotated am)
{
Object result = _primary.findSerializer(am);
if (result == null) {
result = _secondary.findSerializer(am);
}
return result;
}
@Override
public Class extends JsonSerializer>> findKeySerializer(Annotated a)
{
Class extends JsonSerializer>> result = _primary.findKeySerializer(a);
if (result == null || result == JsonSerializer.None.class) {
result = _secondary.findKeySerializer(a);
}
return result;
}
@Override
public Class extends JsonSerializer>> findContentSerializer(Annotated a)
{
Class extends JsonSerializer>> result = _primary.findContentSerializer(a);
if (result == null || result == JsonSerializer.None.class) {
result = _secondary.findContentSerializer(a);
}
return result;
}
@Override
public JsonSerialize.Inclusion findSerializationInclusion(Annotated a,
JsonSerialize.Inclusion defValue)
{
/* This is bit trickier: need to combine results in a meaningful
* way. Seems like it should be a disjoint; that is, most
* restrictive value should be returned.
* For enumerations, comparison is done by indexes, which
* works: largest value is the last one, which is the most
* restrictive value as well.
*/
/* 09-Mar-2010, tatu: Actually, as per [JACKSON-256], it is probably better to just
* use strict overriding. Simpler, easier to understand.
*/
// note: call secondary first, to give lower priority
defValue = _secondary.findSerializationInclusion(a, defValue);
defValue = _primary.findSerializationInclusion(a, defValue);
return defValue;
}
@Override
public Class> findSerializationType(Annotated a)
{
Class> result = _primary.findSerializationType(a);
if (result == null) {
result = _secondary.findSerializationType(a);
}
return result;
}
@Override
public Class> findSerializationKeyType(Annotated am, JavaType baseType)
{
Class> result = _primary.findSerializationKeyType(am, baseType);
if (result == null) {
result = _secondary.findSerializationKeyType(am, baseType);
}
return result;
}
@Override
public Class> findSerializationContentType(Annotated am, JavaType baseType)
{
Class> result = _primary.findSerializationContentType(am, baseType);
if (result == null) {
result = _secondary.findSerializationContentType(am, baseType);
}
return result;
}
@Override
public JsonSerialize.Typing findSerializationTyping(Annotated a)
{
JsonSerialize.Typing result = _primary.findSerializationTyping(a);
if (result == null) {
result = _secondary.findSerializationTyping(a);
}
return result;
}
@Override
public Class>[] findSerializationViews(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.findSerializationViews(a);
if (result == null) {
result = _secondary.findSerializationViews(a);
}
return result;
}
// // // Serialization: class annotations
@Override
public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
String[] result = _primary.findSerializationPropertyOrder(ac);
if (result == null) {
result = _secondary.findSerializationPropertyOrder(ac);
}
return result;
}
/**
* Method for checking whether an annotation indicates that serialized properties
* for which no explicit is defined should be alphabetically (lexicograpically)
* ordered
*/
@Override
public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
Boolean result = _primary.findSerializationSortAlphabetically(ac);
if (result == null) {
result = _secondary.findSerializationSortAlphabetically(ac);
}
return result;
}
// // // Serialization: method annotations
@Override
public String findGettablePropertyName(AnnotatedMethod am)
{
String result = _primary.findGettablePropertyName(am);
if (result == null) {
result = _secondary.findGettablePropertyName(am);
} else if (result.length() == 0) {
/* Empty String is a default; can be overridden by
* more explicit answer from secondary entry
*/
String str2 = _secondary.findGettablePropertyName(am);
if (str2 != null) {
result = str2;
}
}
return result;
}
@Override
public boolean hasAsValueAnnotation(AnnotatedMethod am)
{
return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am);
}
@Override
public String findEnumValue(Enum> value)
{
String result = _primary.findEnumValue(value);
if (result == null) {
result = _secondary.findEnumValue(value);
}
return result;
}
// // // Serialization: field annotations
@Override
public String findSerializablePropertyName(AnnotatedField af)
{
String result = _primary.findSerializablePropertyName(af);
if (result == null) {
result = _secondary.findSerializablePropertyName(af);
} else if (result.length() == 0) {
/* Empty String is a default; can be overridden by
* more explicit answer from secondary entry
*/
String str2 = _secondary.findSerializablePropertyName(af);
if (str2 != null) {
result = str2;
}
}
return result;
}
// // // Deserialization: general annotations
@Override
public Object findDeserializer(Annotated am)
{
Object result = _primary.findDeserializer(am);
if (result == null) {
result = _secondary.findDeserializer(am);
}
return result;
}
@Override
public Class extends KeyDeserializer> findKeyDeserializer(Annotated am)
{
Class extends KeyDeserializer> result = _primary.findKeyDeserializer(am);
if (result == null || result == KeyDeserializer.None.class) {
result = _secondary.findKeyDeserializer(am);
}
return result;
}
@Override
public Class extends JsonDeserializer>> findContentDeserializer(Annotated am)
{
Class extends JsonDeserializer>> result = _primary.findContentDeserializer(am);
if (result == null || result == JsonDeserializer.None.class) {
result = _secondary.findContentDeserializer(am);
}
return result;
}
@Override
public Class> findDeserializationType(Annotated am, JavaType baseType,
String propName)
{
Class> result = _primary.findDeserializationType(am, baseType, propName);
if (result == null) {
result = _secondary.findDeserializationType(am, baseType, propName);
}
return result;
}
@Override
public Class> findDeserializationKeyType(Annotated am, JavaType baseKeyType,
String propName)
{
Class> result = _primary.findDeserializationKeyType(am, baseKeyType, propName);
if (result == null) {
result = _secondary.findDeserializationKeyType(am, baseKeyType, propName);
}
return result;
}
@Override
public Class> findDeserializationContentType(Annotated am, JavaType baseContentType,
String propName)
{
Class> result = _primary.findDeserializationContentType(am, baseContentType, propName);
if (result == null) {
result = _secondary.findDeserializationContentType(am, baseContentType, propName);
}
return result;
}
// // // Deserialization: class annotations
@Override
public Object findValueInstantiator(AnnotatedClass ac)
{
Object result = _primary.findValueInstantiator(ac);
if (result == null) {
result = _secondary.findValueInstantiator(ac);
}
return result;
}
// // // Deserialization: method annotations
@Override
public String findSettablePropertyName(AnnotatedMethod am)
{
String result = _primary.findSettablePropertyName(am);
if (result == null) {
result = _secondary.findSettablePropertyName(am);
} else if (result.length() == 0) {
/* Empty String is a default; can be overridden by
* more explicit answer from secondary entry
*/
String str2 = _secondary.findSettablePropertyName(am);
if (str2 != null) {
result = str2;
}
}
return result;
}
@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);
}
// // // Deserialization: field annotations
@Override
public String findDeserializablePropertyName(AnnotatedField af)
{
String result = _primary.findDeserializablePropertyName(af);
if (result == null) {
result = _secondary.findDeserializablePropertyName(af);
} else if (result.length() == 0) {
/* Empty String is a default; can be overridden by
* more explicit answer from secondary entry
*/
String str2 = _secondary.findDeserializablePropertyName(af);
if (str2 != null) {
result = str2;
}
}
return result;
}
// // // Deserialization: parameter annotations (for creators)
@Override
public String findPropertyNameForParam(AnnotatedParameter param)
{
String result = _primary.findPropertyNameForParam(param);
if (result == null) {
result = _secondary.findPropertyNameForParam(param);
}
return result;
}
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/map/SerializationConfig.java 0000644 0001750 0001750 00000114131 11655120726 031011 0 ustar jamespage jamespage package org.codehaus.jackson.map;
import java.text.DateFormat;
import java.util.*;
import org.codehaus.jackson.annotate.*;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; // for javadocs
import org.codehaus.jackson.map.introspect.Annotated;
import org.codehaus.jackson.map.introspect.AnnotatedClass;
import org.codehaus.jackson.map.introspect.VisibilityChecker;
import org.codehaus.jackson.map.jsontype.SubtypeResolver;
import org.codehaus.jackson.map.jsontype.TypeResolverBuilder;
import org.codehaus.jackson.map.ser.FilterProvider;
import org.codehaus.jackson.map.type.ClassKey;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.map.util.ClassUtil;
import org.codehaus.jackson.type.JavaType;
/**
* Object that contains baseline configuration for serialization
* process. An instance is owned by {@link ObjectMapper}, which makes
* a copy that is passed during serialization process to
* {@link SerializerProvider} and {@link SerializerFactory}.
*
* Note: although configuration settings can be changed at any time
* (for factories and instances), they are not guaranteed to have
* effect if called after constructing relevant mapper or serializer
* instance. This because some objects may be configured, constructed and
* cached first time they are needed.
*
* As of version 1.9, the goal is to make this class eventually immutable.
* Because of this, existing methods that allow changing state of this
* instance are deprecated in favor of methods that create new instances
* with different configuration ("fluent factories")
*/
public class SerializationConfig
extends MapperConfig.Impl
{
/**
* Enumeration that defines togglable features that guide
* the serialization feature.
*/
public enum Feature implements MapperConfig.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.
*
* @since 1.2
*/
USE_ANNOTATIONS(true),
/**
* Feature that determines whether regualr "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 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.
*
* @since 1.1
*/
AUTO_DETECT_FIELDS(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.
*
* Feature is enabled by default.
*/
CAN_OVERRIDE_ACCESS_MODIFIERS(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 for backwards compatibility
* reasons.
*
* @since 1.9
*/
REQUIRE_SETTERS_FOR_GETTERS(false),
/*
/******************************************************
/* Generic output features
/******************************************************
*/
/**
* Feature that determines the default settings of whether Bean
* properties with null values are to be written out.
*
* Feature is enabled by default (null properties written).
*
* Note too that there is annotation
* {@link org.codehaus.jackson.annotate.JsonWriteNullProperties}
* that can be used for more granular control (annotates bean
* classes or individual property access methods).
*
* @deprecated As of 1.1, use {@link SerializationConfig#setSerializationInclusion}
* instead
*/
@Deprecated
WRITE_NULL_PROPERTIES(true),
/**
* Feature that determines whether the type detection for
* serialization should be using actual dynamic runtime type,
* or declared static type.
* Default value is false, to use dynamic runtime type.
*
* This global default value can be overridden at class, method
* or field level by using {@link JsonSerialize#typing} annotation
* property
*/
USE_STATIC_TYPING(false),
/**
* Feature that determines whether properties that have no view
* annotations are included in JSON serialization views (see
* {@link org.codehaus.jackson.map.annotate.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 org.codehaus.jackson.map.annotate.JsonView} annotation.
*
* @since 1.5
*/
DEFAULT_VIEW_INCLUSION(true),
/**
* 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.
*
* Default setting is false, meaning root value is not wrapped.
*
* @since 1.7
*/
WRAP_ROOT_VALUE(false),
/**
* Feature that allows enabling (or disabling) indentation
* for the underlying generator, using the default pretty
* printer (see
* {@link org.codehaus.jackson.JsonGenerator#useDefaultPrettyPrinter}
* for details).
*
* Note that this only affects cases where
* {@link org.codehaus.jackson.JsonGenerator}
* is constructed implicitly by ObjectMapper: if explicit
* generator is passed, its configuration is not changed.
*
* Also note that if you want to configure details of indentation,
* you need to directly configure the generator: there is a
* method to use any PrettyPrinter
instance.
* This feature will only allow using the default implementation.
*/
INDENT_OUTPUT(false),
/**
* 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 org.codehaus.jackson.annotate.JsonPropertyOrder#alphabetic()}
* works); if disabled, order is unspecified (based on what JDK gives
* us, which may be declaration order, but not guaranteed).
*
* Note that this is just the default behavior, and can be overridden by
* explicit overrides in classes.
*
* @since 1.8
*/
SORT_PROPERTIES_ALPHABETICALLY(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.
*
* @since 1.4
*/
FAIL_ON_EMPTY_BEANS(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, and is similar in behavior
* to default prior to 1.7.
*
* @since 1.7
*/
WRAP_EXCEPTIONS(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.
*
* @since 1.6 (see [JACKSON-282 for details])
*/
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).
*
* @since 1.6 (see [JACKSON-401 for details])
*/
FLUSH_AFTER_WRITE_VALUE(true),
/*
/******************************************************
/* Data type - specific serialization configuration
/******************************************************
*/
/**
* Feature that determines whether {@link java.util.Date} 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 #getDateFormat}.
*
* 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}.
*/
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.
*
* @since 1.9
*/
WRITE_DATE_KEYS_AS_TIMESTAMPS(false),
/**
* 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).
*
* @since 1.6 (see [JACKSON-289 for details])
*/
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.
* Since pre-1.6 method was to use Enum name, this is the default.
*
* Note: this feature should usually have same value
* as {@link DeserializationConfig.Feature#READ_ENUMS_USING_TO_STRING}.
*
* For further details, check out [JACKSON-212]
*
* @since 1.6
*/
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.
*
* @since 1.9
*/
WRITE_ENUMS_USING_INDEX(false),
/**
* Feature that determines whether Map entries with null values are
* to be serialized (true) or not (false).
*
* For further details, check out [JACKSON-314]
*
* @since 1.6
*/
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.
*
* @since 1.9
*/
WRITE_EMPTY_JSON_ARRAYS(true)
;
final boolean _defaultState;
private Feature(boolean defaultState) {
_defaultState = defaultState;
}
@Override
public boolean enabledByDefault() { return _defaultState; }
@Override
public int getMask() { return (1 << ordinal()); }
}
/*
/**********************************************************
/* Configuration settings
/**********************************************************
*/
/**
* 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.
*
* Defaults to null for backwards compatibility; if left as null,
* will check
* deprecated {@link Feature#WRITE_NULL_PROPERTIES}
* to choose between {@link Inclusion#ALWAYS}
* and {@link Inclusion#NON_NULL}.
*/
protected JsonSerialize.Inclusion _serializationInclusion = null;
/**
* View to use for filtering out properties to serialize.
* Null if none (will also be assigned null if Object.class
* is defined), meaning that all properties are to be included.
*/
protected Class> _serializationView;
/**
* Object used for resolving filter ids to filter instances.
* Non-null if explicitly defined; null by default.
*
* @since 1.7
*/
protected FilterProvider _filterProvider;
/*
/**********************************************************
/* Life-cycle, constructors
/**********************************************************
*/
/**
* Constructor used by ObjectMapper to create default configuration object instance.
*/
public SerializationConfig(ClassIntrospector extends BeanDescription> intr,
AnnotationIntrospector annIntr, VisibilityChecker> vc,
SubtypeResolver subtypeResolver, PropertyNamingStrategy propertyNamingStrategy,
TypeFactory typeFactory, HandlerInstantiator handlerInstantiator)
{
super(intr, annIntr, vc, subtypeResolver, propertyNamingStrategy, typeFactory, handlerInstantiator,
collectFeatureDefaults(SerializationConfig.Feature.class));
_filterProvider = null;
}
/**
* @since 1.8
*/
protected SerializationConfig(SerializationConfig src) {
this(src, src._base);
}
/**
* Constructor used to make a private copy of specific mix-in definitions.
*
* @since 1.8
*/
protected SerializationConfig(SerializationConfig src,
HashMap> mixins, SubtypeResolver str)
{
this(src, src._base);
_mixInAnnotations = mixins;
_subtypeResolver = str;
}
/**
* @since 1.8
*/
protected SerializationConfig(SerializationConfig src, MapperConfig.Base base)
{
super(src, base, src._subtypeResolver);
_serializationInclusion = src._serializationInclusion;
_serializationView = src._serializationView;
_filterProvider = src._filterProvider;
}
/**
* @since 1.8
*/
protected SerializationConfig(SerializationConfig src, FilterProvider filters)
{
super(src);
_serializationInclusion = src._serializationInclusion;
_serializationView = src._serializationView;
_filterProvider = filters;
}
/**
* @since 1.8
*/
protected SerializationConfig(SerializationConfig src, Class> view)
{
super(src);
_serializationInclusion = src._serializationInclusion;
_serializationView = view;
_filterProvider = src._filterProvider;
}
/**
* @since 1.9
*/
protected SerializationConfig(SerializationConfig src, JsonSerialize.Inclusion incl)
{
super(src);
_serializationInclusion = incl;
// And for some level of backwards compatibility, also...
if (incl == JsonSerialize.Inclusion.NON_NULL) {
_featureFlags &= ~Feature.WRITE_NULL_PROPERTIES.getMask();
} else {
_featureFlags |= Feature.WRITE_NULL_PROPERTIES.getMask();
}
_serializationView = src._serializationView;
_filterProvider = src._filterProvider;
}
/**
* @since 1.9
*/
protected SerializationConfig(SerializationConfig src, int features)
{
super(src, features);
_serializationInclusion = src._serializationInclusion;
_serializationView = src._serializationView;
_filterProvider = src._filterProvider;
}
/*
/**********************************************************
/* Life-cycle, factory methods from MapperConfig
/**********************************************************
*/
@Override
public SerializationConfig withClassIntrospector(ClassIntrospector extends BeanDescription> ci) {
return new SerializationConfig(this, _base.withClassIntrospector(ci));
}
@Override
public SerializationConfig withAnnotationIntrospector(AnnotationIntrospector ai) {
return new SerializationConfig(this, _base.withAnnotationIntrospector(ai));
}
@Override
public SerializationConfig withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
return new SerializationConfig(this, _base.withInsertedAnnotationIntrospector(ai));
}
@Override
public SerializationConfig withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
return new SerializationConfig(this, _base.withAppendedAnnotationIntrospector(ai));
}
@Override
public SerializationConfig withVisibilityChecker(VisibilityChecker> vc) {
return new SerializationConfig(this, _base.withVisibilityChecker(vc));
}
@Override
public SerializationConfig withVisibility(JsonMethod forMethod, JsonAutoDetect.Visibility visibility) {
return new SerializationConfig(this, _base.withVisibility(forMethod, visibility));
}
@Override
public SerializationConfig withTypeResolverBuilder(TypeResolverBuilder> trb) {
return new SerializationConfig(this, _base.withTypeResolverBuilder(trb));
}
@Override
public SerializationConfig withSubtypeResolver(SubtypeResolver str) {
SerializationConfig cfg = new SerializationConfig(this);
cfg._subtypeResolver = str;
return cfg;
}
@Override
public SerializationConfig withPropertyNamingStrategy(PropertyNamingStrategy pns) {
return new SerializationConfig(this, _base.withPropertyNamingStrategy(pns));
}
@Override
public SerializationConfig withTypeFactory(TypeFactory tf) {
return new SerializationConfig(this, _base.withTypeFactory(tf));
}
/**
* In addition to constructing instance with specified date format,
* will enable or disable Feature.WRITE_DATES_AS_TIMESTAMPS
* (enable if format set as null; disable if non-null)
*/
@Override
public SerializationConfig withDateFormat(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(Feature.WRITE_DATES_AS_TIMESTAMPS);
} else {
cfg = cfg.without(Feature.WRITE_DATES_AS_TIMESTAMPS);
}
return cfg;
}
@Override
public SerializationConfig withHandlerInstantiator(HandlerInstantiator hi) {
return new SerializationConfig(this, _base.withHandlerInstantiator(hi));
}
/*
/**********************************************************
/* Life-cycle, SerializationConfig specific factory methods
/**********************************************************
*/
/**
* @since 1.7
*/
public SerializationConfig withFilters(FilterProvider filterProvider) {
return new SerializationConfig(this, filterProvider);
}
/**
* @since 1.8
*/
public SerializationConfig withView(Class> view) {
return new SerializationConfig(this, view);
}
/**
* @since 1.9
*/
public SerializationConfig withSerializationInclusion(JsonSerialize.Inclusion incl) {
return new SerializationConfig(this, incl);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*
* @since 1.9
*/
@Override
public SerializationConfig with(Feature... features)
{
int flags = _featureFlags;
for (Feature f : features) {
flags |= f.getMask();
}
return new SerializationConfig(this, flags);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features disabled.
*
* @since 1.9
*/
@Override
public SerializationConfig without(Feature... features)
{
int flags = _featureFlags;
for (Feature f : features) {
flags &= ~f.getMask();
}
return new SerializationConfig(this, flags);
}
/*
/**********************************************************
/* MapperConfig implementation/overrides
/**********************************************************
*/
/**
* Method that checks class annotations that the argument Object has,
* and modifies settings of this configuration object accordingly,
* similar to how those annotations would affect actual value classes
* annotated with them, but with global scope. Note that not all
* annotations have global significance, and thus only subset of
* Jackson annotations will have any effect.
*
* Serialization annotations that are known to have effect are:
*
* {@link JsonWriteNullProperties}
* {@link JsonAutoDetect}
* {@link JsonSerialize#typing}
*
*
* @param cls Class of which class annotations to use
* for changing configuration settings
*
* @deprecated Since 1.9, it is preferably to explicitly configure
* instances; this method also modifies existing instance which is
* against immutable design goals of this class.
*/
@SuppressWarnings("deprecation")
@Deprecated
@Override
public void fromAnnotations(Class> cls)
{
/* 10-Jul-2009, tatu: Should be able to just pass null as
* 'MixInResolver'; no mix-ins set at this point
* 29-Jul-2009, tatu: Also, we do NOT ignore annotations here, even
* if Feature.USE_ANNOTATIONS was disabled, since caller
* specifically requested annotations to be added with this call
*/
AnnotationIntrospector ai = getAnnotationIntrospector();
AnnotatedClass ac = AnnotatedClass.construct(cls, ai, null);
_base = _base.withVisibilityChecker(ai.findAutoDetectVisibility(ac,
getDefaultVisibilityChecker()));
// How about writing null property values?
JsonSerialize.Inclusion incl = ai.findSerializationInclusion(ac, null);
if (incl != _serializationInclusion) {
setSerializationInclusion(incl);
}
JsonSerialize.Typing typing = ai.findSerializationTyping(ac);
if (typing != null) {
set(Feature.USE_STATIC_TYPING, (typing == JsonSerialize.Typing.STATIC));
}
}
@Override
public SerializationConfig createUnshared(SubtypeResolver subtypeResolver)
{
HashMap> mixins = _mixInAnnotations;
_mixInAnnotationsShared = true;
return new SerializationConfig(this, mixins, subtypeResolver);
}
@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(Feature.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.
*
* Note: part of {@link MapperConfig} since 1.7
*/
@SuppressWarnings("unchecked")
@Override
public T introspectClassAnnotations(JavaType type) {
return (T) 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.
*
* Note: part of {@link MapperConfig} since 1.7
*/
@SuppressWarnings("unchecked")
@Override
public T introspectDirectClassAnnotations(JavaType type) {
return (T) getClassIntrospector().forDirectClassAnnotations(this, type, this);
}
@Override
public boolean isAnnotationProcessingEnabled() {
return isEnabled(SerializationConfig.Feature.USE_ANNOTATIONS);
}
@Override
public boolean canOverrideAccessModifiers() {
return isEnabled(Feature.CAN_OVERRIDE_ACCESS_MODIFIERS);
}
@Override
public boolean shouldSortPropertiesAlphabetically() {
return isEnabled(Feature.SORT_PROPERTIES_ALPHABETICALLY);
}
@Override
public VisibilityChecker> getDefaultVisibilityChecker()
{
VisibilityChecker> vchecker = super.getDefaultVisibilityChecker();
if (!isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)) {
vchecker = vchecker.withGetterVisibility(Visibility.NONE);
}
// then global overrides (disabling)
if (!isEnabled(SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS)) {
vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
}
if (!isEnabled(SerializationConfig.Feature.AUTO_DETECT_FIELDS)) {
vchecker = vchecker.withFieldVisibility(Visibility.NONE);
}
return vchecker;
}
/*
/**********************************************************
/* MapperConfig overrides for 1.8 backwards compatibility
/**********************************************************
*/
/* NOTE: these are overloads we MUST have, but that were missing
* from 1.9.0 and 1.9.1. Type erasure can bite in the ass...
*
* NOTE: will remove either these variants, or base class one, in 2.0.
*/
/**
* Alias for {@link MapperConfig#isEnabled(org.codehaus.jackson.map.MapperConfig.ConfigFeature)}.
*
* @since 1.0 However, note that version 1.9.0 and 1.9.1 accidentally missed
* this overloaded variant
*/
public boolean isEnabled(SerializationConfig.Feature f) {
return (_featureFlags & f.getMask()) != 0;
}
/**
* @deprecated Since 1.9, it is preferable to use {@link #with} instead;
* this method is deprecated as it modifies current instance instead of
* creating a new one (as the goal is to make this class immutable)
*/
@Deprecated
@Override
public void enable(SerializationConfig.Feature f) {
super.enable(f);
}
/**
* @deprecated Since 1.9, it is preferable to use {@link #without} instead;
* this method is deprecated as it modifies current instance instead of
* creating a new one (as the goal is to make this class immutable)
*/
@Deprecated
@Override
public void disable(SerializationConfig.Feature f) {
super.disable(f);
}
/**
* @deprecated Since 1.9, it is preferable to use {@link #without} and {@link #with} instead;
* this method is deprecated as it modifies current instance instead of
* creating a new one (as the goal is to make this class immutable)
*/
@Deprecated
@Override
public void set(SerializationConfig.Feature f, boolean state) {
super.set(f, state);
}
/*
/**********************************************************
/* Configuration: other
/**********************************************************
*/
/**
* Method for checking which serialization view is being used,
* if any; null if none.
*
* @since 1.4
*/
public Class> getSerializationView() { return _serializationView; }
public JsonSerialize.Inclusion getSerializationInclusion()
{
if (_serializationInclusion != null) {
return _serializationInclusion;
}
return isEnabled(Feature.WRITE_NULL_PROPERTIES) ?
JsonSerialize.Inclusion.ALWAYS : JsonSerialize.Inclusion.NON_NULL;
}
/**
* Method that will define global setting of which
* bean/map properties are to be included in serialization.
* Can be overridden by class annotations (overriding
* settings to use for instances of that class) and
* method/field annotations (overriding settings for the value
* bean for that getter method or field)
*
* @deprecated since 1.9 should either use {@link #withSerializationInclusion}
* to construct new instance, or configure through {@link ObjectMapper}
*/
@SuppressWarnings("deprecation")
@Deprecated
public void setSerializationInclusion(JsonSerialize.Inclusion props)
{
_serializationInclusion = props;
// And for some level of backwards compatibility, also...
if (props == JsonSerialize.Inclusion.NON_NULL) {
disable(Feature.WRITE_NULL_PROPERTIES);
} else {
enable(Feature.WRITE_NULL_PROPERTIES);
}
}
/**
* 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})
*
* @since 1.7
*/
public FilterProvider getFilterProvider() {
return _filterProvider;
}
/*
/**********************************************************
/* 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);
}
/*
/**********************************************************
/* Extended API: serializer instantiation
/**********************************************************
*/
@SuppressWarnings("unchecked")
public JsonSerializer serializerInstance(Annotated annotated, Class extends JsonSerializer>> serClass)
{
HandlerInstantiator hi = getHandlerInstantiator();
if (hi != null) {
JsonSerializer> ser = hi.serializerInstance(this, annotated, serClass);
if (ser != null) {
return (JsonSerializer) ser;
}
}
return (JsonSerializer) ClassUtil.createInstance(serClass, canOverrideAccessModifiers());
}
/*
/**********************************************************
/* Deprecated methods
/**********************************************************
*/
/**
* One thing to note is that this will set {@link Feature#WRITE_DATES_AS_TIMESTAMPS}
* to false (if null format set), or true (if non-null format)
*
* @deprecated Since 1.8, use {@link #withDateFormat} instead.
*/
@SuppressWarnings("deprecation")
@Override
@Deprecated
public final void setDateFormat(DateFormat df) {
super.setDateFormat(df);
set(Feature.WRITE_DATES_AS_TIMESTAMPS, (df == null));
}
/**
* Method for checking which serialization view is being used,
* if any; null if none.
*
* @since 1.4
*
* @deprecated Since 1.8, use {@link #withView} instead
*/
@Deprecated
public void setSerializationView(Class> view)
{
_serializationView = view;
}
/*
/**********************************************************
/* Debug support
/**********************************************************
*/
@Override public String toString()
{
return "[SerializationConfig: flags=0x"+Integer.toHexString(_featureFlags)+"]";
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/map/SerializerFactory.java 0000644 0001750 0001750 00000015447 11655120726 030521 0 ustar jamespage jamespage package org.codehaus.jackson.map;
import org.codehaus.jackson.map.ser.BeanSerializerModifier;
import org.codehaus.jackson.type.JavaType;
/**
* Abstract class that defines API used by {@link SerializerProvider}
* to obtain actual
* {@link JsonSerializer} instances from multiple distinct factories.
*/
public abstract class SerializerFactory
{
/*
/**********************************************************
/* Helper class to contain configuration settings
/**********************************************************
*/
/**
* Configuration settings container class for bean serializer factory.
*
* @since 1.7
*/
public abstract static class Config
{
/**
* Method for creating a new instance with additional serializer provider.
*/
public abstract Config withAdditionalSerializers(Serializers additional);
/**
* @since 1.8
*/
public abstract Config withAdditionalKeySerializers(Serializers additional);
/**
* Method for creating a new instance with additional bean serializer modifier.
*/
public abstract Config withSerializerModifier(BeanSerializerModifier modifier);
public abstract boolean hasSerializers();
public abstract boolean hasKeySerializers();
public abstract boolean hasSerializerModifiers();
public abstract Iterable serializers();
public abstract Iterable keySerializers();
public abstract Iterable serializerModifiers();
}
/*
/**********************************************************
/* Additional configuration
/**********************************************************
*/
/**
* @since 1.7
*/
public abstract Config getConfig();
/**
* 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 org.codehaus.jackson.map.ser.BeanSerializerFactory} for more details.
*
* @since 1.7
*/
public abstract SerializerFactory withConfig(Config config);
/**
* Convenience method for creating a new factory instance with additional serializer
* provider; equivalent to calling
*
* withConfig(getConfig().withAdditionalSerializers(additional));
*
*
* @since 1.7
*/
public final SerializerFactory withAdditionalSerializers(Serializers additional) {
return withConfig(getConfig().withAdditionalSerializers(additional));
}
/**
* @since 1.8
*/
public final SerializerFactory withAdditionalKeySerializers(Serializers additional) {
return withConfig(getConfig().withAdditionalKeySerializers(additional));
}
/**
* Convenience method for creating a new factory instance with additional bean
* serializer modifier; equivalent to calling
*
* withConfig(getConfig().withSerializerModifier(modifier));
*
*
* @since 1.7
*/
public final SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) {
return withConfig(getConfig().withSerializerModifier(modifier));
}
/*
/**********************************************************
/* Basic SerializerFactory API:
/**********************************************************
*/
/**
* Method called to create (or, for immutable serializers, reuse) a serializer for given type.
*/
public abstract JsonSerializer createSerializer(SerializationConfig config, JavaType baseType,
BeanProperty property)
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.
*
* @since 1.7
*/
public abstract TypeSerializer createTypeSerializer(SerializationConfig config, JavaType baseType,
BeanProperty property)
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 config Serialization configuration in use
* @param baseType Declared type for Map keys
* @param property Property that contains Map being serialized; null when serializing root Map value.
*
* @return Serializer to use, if factory knows it; null if not (in which case default serializer
* is to be used)
*
* @since 1.8
*/
public abstract JsonSerializer createKeySerializer(SerializationConfig config, JavaType baseType,
BeanProperty property)
throws JsonMappingException;
/*
/**********************************************************
/* Deprecated (as of 1.7) SerializerFactory API:
/**********************************************************
*/
/**
* Deprecated version of accessor for type id serializer: as of 1.7 one needs
* to instead call version that passes property information through.
*
* @since 1.5
*
* @deprecated Since 1.7, call variant with more arguments
*/
@Deprecated
public final JsonSerializer createSerializer(JavaType type, SerializationConfig config) {
try {
return createSerializer(config, type, null);
} catch (JsonMappingException e) { // not optimal but:
throw new RuntimeJsonMappingException(e);
}
}
/**
* Deprecated version of accessor for type id serializer: as of 1.7 one needs
* to instead call version that passes property information through.
*
* @since 1.5
*
* @deprecated Since 1.7, call variant with more arguments
*/
@Deprecated
public final TypeSerializer createTypeSerializer(JavaType baseType, SerializationConfig config) {
try {
return createTypeSerializer(config, baseType, null);
} catch (JsonMappingException e) { // not optimal but:
throw new RuntimeException(e);
}
}
}
jackson-src-1.9.2/src/mapper/java/org/codehaus/jackson/map/MapperConfig.java 0000644 0001750 0001750 00000121245 11655120726 027424 0 ustar jamespage jamespage package org.codehaus.jackson.map;
import java.text.DateFormat;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.introspect.Annotated;
import org.codehaus.jackson.map.introspect.VisibilityChecker;
import org.codehaus.jackson.map.jsontype.SubtypeResolver;
import org.codehaus.jackson.map.jsontype.TypeIdResolver;
import org.codehaus.jackson.map.jsontype.TypeResolverBuilder;
import org.codehaus.jackson.map.jsontype.impl.StdSubtypeResolver;
import org.codehaus.jackson.map.type.ClassKey;
import org.codehaus.jackson.map.type.TypeBindings;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.map.util.ClassUtil;
import org.codehaus.jackson.map.util.StdDateFormat;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
/**
* Interface that defines functionality accessible through both
* serialization and deserialization configuration objects;
* accessors to mode-independent configuration settings
* and such.
*
* As of version 1.9, the goal is to make this class eventually immutable.
* Because of this, existing methods that allow changing state of this
* instance are deprecated in favor of methods that create new instances
* with different configuration ("fluent factories").
* One major remaining issue is that of handling mix-in annotations, which
* still represent bit of mutable state.
*
* @since 1.2 -- major change in 1.8, changed from interface to
* abstract class
*/
public abstract class MapperConfig>
implements ClassIntrospector.MixInResolver
{
/*
/**********************************************************
/* Constants, default values
/**********************************************************
*/
/**
* This is the default {@link DateFormat} used unless overridden by
* custom implementation.
*/
protected final static DateFormat DEFAULT_DATE_FORMAT = StdDateFormat.instance;
/*
/**********************************************************
/* Simple immutable basic settings
/**********************************************************
*/
/**
* Immutable container object for simple configuration settings.
*
* Note: ideally this would be final, but until we can eliminate
* mutators, must keep it mutable.
*/
protected Base _base;
/*
/**********************************************************
/* 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 1.2
*/
protected HashMap> _mixInAnnotations;
/**
* Flag used to detect when a copy if mix-in annotations is
* needed: set when current copy is shared, cleared when a
* fresh copy is made
*
* @since 1.2
*/
protected boolean _mixInAnnotationsShared;
/*
/**********************************************************
/* "Late bound" settings
/**********************************************************
*/
/**
* Registered concrete subtypes that can be used instead of (or
* in addition to) ones declared using annotations.
* Unlike most other settings, it is not configured as early
* as it is set, but rather only when a non-shared instance
* is constructed by ObjectMapper
(or -Reader
* or -Writer)
*
* Note: this is the only property left as non-final, to allow
* lazy construction of the instance as necessary.
*
* @since 1.6
*/
protected SubtypeResolver _subtypeResolver;
/*
/**********************************************************
/* Life-cycle: constructors
/**********************************************************
*/
protected MapperConfig(ClassIntrospector extends BeanDescription> ci, AnnotationIntrospector ai,
VisibilityChecker> vc, SubtypeResolver str, PropertyNamingStrategy pns, TypeFactory tf,
HandlerInstantiator hi)
{
_base = new Base(ci, ai, vc, pns, tf, null, DEFAULT_DATE_FORMAT, hi);
_subtypeResolver = str;
// by default, assumed to be shared; only cleared when explicit copy is made
_mixInAnnotationsShared = true;
}
/**
* Simple copy constructor
*
* @since 1.8
*/
protected MapperConfig(MapperConfig