package com.fasterxml.jackson.databind.util;

import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.TreeMap;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserMinimalBase;
import com.fasterxml.jackson.core.json.JsonWriteContext;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.databind.*;

Utility class used for efficient storage of JsonToken sequences, needed for temporary buffering. Space efficient for different sequence lengths (especially so for smaller ones; but not significantly less efficient for larger), highly efficient for linear iteration and appending. Implemented as segmented/chunked linked list of tokens; only modifications are via appends.

Note that before version 2.0, this class was located in the "core" bundle, not data-binding; but since it was only used by data binding, was moved here to reduce size of core package

/** * 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. *<p> * Note that before version 2.0, this class was located in the "core" * bundle, not data-binding; but since it was only used by data binding, * was moved here to reduce size of core package */
public class TokenBuffer /* Won't use JsonGeneratorBase, to minimize overhead for validity * checking */ extends JsonGenerator { protected final static int DEFAULT_GENERATOR_FEATURES = JsonGenerator.Feature.collectDefaults(); /* /********************************************************** /* Configuration /********************************************************** */
Object codec to use for stream-based object conversion through parser/generator interfaces. If null, such methods cannot be used.
/** * Object codec to use for stream-based object * conversion through parser/generator interfaces. If null, * such methods cannot be used. */
protected ObjectCodec _objectCodec;
Parse context from "parent" parser (one from which content to buffer is read, if specified). Used, if available, when reading content, to present full context as if content was read from the original parser: this is useful in error reporting and sometimes processing as well.
/** * Parse context from "parent" parser (one from which content to buffer is read, * if specified). Used, if available, when reading content, to present full * context as if content was read from the original parser: this is useful * in error reporting and sometimes processing as well. */
protected JsonStreamContext _parentContext;
Bit flag composed of bits that indicate which Features are enabled.

NOTE: most features have no effect on this class

/** * Bit flag composed of bits that indicate which * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s * are enabled. *<p> * NOTE: most features have no effect on this class */
protected int _generatorFeatures; protected boolean _closed;
Since:2.3
/** * @since 2.3 */
protected boolean _hasNativeTypeIds;
Since:2.3
/** * @since 2.3 */
protected boolean _hasNativeObjectIds;
Since:2.3
/** * @since 2.3 */
protected boolean _mayHaveNativeIds;
Flag set during construction, if use of BigDecimal is to be forced on all floating-point values.
Since:2.7
/** * Flag set during construction, if use of {@link BigDecimal} is to be forced * on all floating-point values. * * @since 2.7 */
protected boolean _forceBigDecimal; /* /********************************************************** /* Token buffering state /********************************************************** */
First segment, for contents this buffer has
/** * First segment, for contents this buffer has */
protected Segment _first;
Last segment of this buffer, one that is used for appending more tokens
/** * Last segment of this buffer, one that is used * for appending more tokens */
protected Segment _last;
Offset within last segment,
/** * Offset within last segment, */
protected int _appendAt;
If native type ids supported, this is the id for following value (or first token of one) to be written.
/** * If native type ids supported, this is the id for following * value (or first token of one) to be written. */
protected Object _typeId;
If native object ids supported, this is the id for following value (or first token of one) to be written.
/** * If native object ids supported, this is the id for following * value (or first token of one) to be written. */
protected Object _objectId;
Do we currently have a native type or object id buffered?
/** * Do we currently have a native type or object id buffered? */
protected boolean _hasNativeId = false; /* /********************************************************** /* Output state /********************************************************** */ protected JsonWriteContext _writeContext; /* /********************************************************** /* Life-cycle /********************************************************** */
Params:
  • codec – Object codec to use for stream-based object conversion through parser/generator interfaces. If null, such methods cannot be used.
  • hasNativeIds – Whether resulting JsonParser (if created) is considered to support native type and object ids
/** * @param codec Object codec to use for stream-based object * conversion through parser/generator interfaces. If null, * such methods cannot be used. * @param hasNativeIds Whether resulting {@link JsonParser} (if created) * is considered to support native type and object ids */
public TokenBuffer(ObjectCodec codec, boolean hasNativeIds) { _objectCodec = codec; _generatorFeatures = DEFAULT_GENERATOR_FEATURES; _writeContext = JsonWriteContext.createRootContext(null); // at first we have just one segment _first = _last = new Segment(); _appendAt = 0; _hasNativeTypeIds = hasNativeIds; _hasNativeObjectIds = hasNativeIds; _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; }
Since:2.3
/** * @since 2.3 */
public TokenBuffer(JsonParser p) { this(p, null); }
Since:2.7
/** * @since 2.7 */
public TokenBuffer(JsonParser p, DeserializationContext ctxt) { _objectCodec = p.getCodec(); _parentContext = p.getParsingContext(); _generatorFeatures = DEFAULT_GENERATOR_FEATURES; _writeContext = JsonWriteContext.createRootContext(null); // at first we have just one segment _first = _last = new Segment(); _appendAt = 0; _hasNativeTypeIds = p.canReadTypeId(); _hasNativeObjectIds = p.canReadObjectId(); _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; _forceBigDecimal = (ctxt == null) ? false : ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); }
Convenience method, equivalent to:
TokenBuffer b = new TokenBuffer(p);
b.copyCurrentStructure(p);
return b;
Since:2.9
/** * Convenience method, equivalent to: *<pre> * TokenBuffer b = new TokenBuffer(p); * b.copyCurrentStructure(p); * return b; *</pre> * * @since 2.9 */
public static TokenBuffer asCopyOfValue(JsonParser p) throws IOException { TokenBuffer b = new TokenBuffer(p); b.copyCurrentStructure(p); return b; }
Method that allows explicitly specifying parent parse context to associate with contents of this buffer. Usually context is assigned at construction, based on given parser; but it is not always available, and may not contain intended context.
Since:2.9
/** * Method that allows explicitly specifying parent parse context to associate * with contents of this buffer. Usually context is assigned at construction, * based on given parser; but it is not always available, and may not contain * intended context. * * @since 2.9 */
public TokenBuffer overrideParentContext(JsonStreamContext ctxt) { _parentContext = ctxt; return this; }
Since:2.7
/** * @since 2.7 */
public TokenBuffer forceUseOfBigDecimal(boolean b) { _forceBigDecimal = b; return this; } @Override public Version version() { return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; }
Method used to create a 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.

Returns:Parser that can be used for reading contents stored in this buffer
/** * Method used to create a {@link JsonParser} that can read contents * stored in this buffer. Will use default <code>_objectCodec</code> for * object conversions. *<p> * 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); }
Same as:
 JsonParser p = asParser();
 p.nextToken();
 return p;
Since:2.9
/** * Same as: *<pre> * JsonParser p = asParser(); * p.nextToken(); * return p; *</pre> * * @since 2.9 */
public JsonParser asParserOnFirstToken() throws IOException { JsonParser p = asParser(_objectCodec); p.nextToken(); return p; }
Method used to create a 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.

Params:
  • codec – Object codec to use for stream-based object conversion through parser/generator interfaces. If null, such methods cannot be used.
Returns:Parser that can be used for reading contents stored in this buffer
/** * Method used to create a {@link JsonParser} that can read contents * stored in this buffer. *<p> * 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 cannot be used. * * @return Parser that can be used for reading contents stored in this buffer */
public JsonParser asParser(ObjectCodec codec) { return new Parser(_first, codec, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); }
Params:
  • src – Parser to use for accessing source information like location, configured 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(), _hasNativeTypeIds, _hasNativeObjectIds, _parentContext); p.setLocation(src.getTokenLocation()); return p; } /* /********************************************************** /* Additional accessors /********************************************************** */ public JsonToken firstToken() { // no need to null check; never create without `_first` return _first.type(0); } /* /********************************************************** /* Other custom methods not needed for implementing interfaces /********************************************************** */
Helper method that will append contents of given buffer into this buffer. Not particularly optimized; can be made faster if there is need.
Returns:This buffer
/** * Helper method that will append contents of given buffer into this * buffer. * Not particularly optimized; can be made faster if there is need. * * @return This buffer */
@SuppressWarnings("resource") public TokenBuffer append(TokenBuffer other) throws IOException { // Important? If source has native ids, need to store if (!_hasNativeTypeIds) { _hasNativeTypeIds = other.canWriteTypeId(); } if (!_hasNativeObjectIds) { _hasNativeObjectIds = other.canWriteObjectId(); } _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds; JsonParser p = other.asParser(); while (p.nextToken() != null) { copyCurrentStructure(p); } return this; }
Helper method that will write all contents of this buffer using given JsonGenerator.

Note: this method would be enough to implement JsonSerializer for TokenBuffer type; but we cannot have upwards references (from core to mapper package); and as such we also cannot take second argument.

/** * Helper method that will write all contents of this buffer * using given {@link JsonGenerator}. *<p> * Note: this method would be enough to implement * <code>JsonSerializer</code> for <code>TokenBuffer</code> type; * but we cannot have upwards * references (from core to mapper package); and as such we also * cannot take second argument. */
public void serialize(JsonGenerator gen) throws IOException { Segment segment = _first; int ptr = -1; final boolean checkIds = _mayHaveNativeIds; boolean hasIds = checkIds && (segment.hasIds()); while (true) { if (++ptr >= Segment.TOKENS_PER_SEGMENT) { ptr = 0; segment = segment.next(); if (segment == null) break; hasIds = checkIds && (segment.hasIds()); } JsonToken t = segment.type(ptr); if (t == null) break; if (hasIds) { Object id = segment.findObjectId(ptr); if (id != null) { gen.writeObjectId(id); } id = segment.findTypeId(ptr); if (id != null) { gen.writeTypeId(id); } } // Note: copied from 'copyCurrentEvent'... switch (t) { case START_OBJECT: gen.writeStartObject(); break; case END_OBJECT: gen.writeEndObject(); break; case START_ARRAY: gen.writeStartArray(); break; case END_ARRAY: gen.writeEndArray(); break; case FIELD_NAME: { // 13-Dec-2010, tatu: Maybe we should start using different type tokens to reduce casting? Object ob = segment.get(ptr); if (ob instanceof SerializableString) { gen.writeFieldName((SerializableString) ob); } else { gen.writeFieldName((String) ob); } } break; case VALUE_STRING: { Object ob = segment.get(ptr); if (ob instanceof SerializableString) { gen.writeString((SerializableString) ob); } else { gen.writeString((String) ob); } } break; case VALUE_NUMBER_INT: { Object n = segment.get(ptr); if (n instanceof Integer) { gen.writeNumber((Integer) n); } else if (n instanceof BigInteger) { gen.writeNumber((BigInteger) n); } else if (n instanceof Long) { gen.writeNumber((Long) n); } else if (n instanceof Short) { gen.writeNumber((Short) n); } else { gen.writeNumber(((Number) n).intValue()); } } break; case VALUE_NUMBER_FLOAT: { Object n = segment.get(ptr); if (n instanceof Double) { gen.writeNumber(((Double) n).doubleValue()); } else if (n instanceof BigDecimal) { gen.writeNumber((BigDecimal) n); } else if (n instanceof Float) { gen.writeNumber(((Float) n).floatValue()); } else if (n == null) { gen.writeNull(); } else if (n instanceof String) { gen.writeNumber((String) n); } else { throw new JsonGenerationException(String.format( "Unrecognized value type for VALUE_NUMBER_FLOAT: %s, cannot serialize", n.getClass().getName()), gen); } } break; case VALUE_TRUE: gen.writeBoolean(true); break; case VALUE_FALSE: gen.writeBoolean(false); break; case VALUE_NULL: gen.writeNull(); break; case VALUE_EMBEDDED_OBJECT: { Object value = segment.get(ptr); // 01-Sep-2016, tatu: as per [databind#1361], should use `writeEmbeddedObject()`; // however, may need to consider alternatives for some well-known types // first if (value instanceof RawValue) { ((RawValue) value).serialize(gen); } else if (value instanceof JsonSerializable) { gen.writeObject(value); } else { gen.writeEmbeddedObject(value); } } break; default: throw new RuntimeException("Internal error: should never end up through this code path"); } } }
Helper method used by standard deserializer.
Since:2.3
/** * Helper method used by standard deserializer. * * @since 2.3 */
public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { if (p.getCurrentTokenId() != JsonToken.FIELD_NAME.id()) { copyCurrentStructure(p); return this; } /* 28-Oct-2014, tatu: As per [databind#592], need to support a special case of starting from * FIELD_NAME, which is taken to mean that we are missing START_OBJECT, but need * to assume one did exist. */ JsonToken t; writeStartObject(); do { copyCurrentStructure(p); } while ((t = p.nextToken()) == JsonToken.FIELD_NAME); if (t != JsonToken.END_OBJECT) { ctxt.reportWrongTokenException(TokenBuffer.class, JsonToken.END_OBJECT, "Expected END_OBJECT after copying contents of a JsonParser into TokenBuffer, got "+t); // never gets here } writeEndObject(); return this; } @Override @SuppressWarnings("resource") public String toString() { // Let's print up to 100 first tokens... final int MAX_COUNT = 100; StringBuilder sb = new StringBuilder(); sb.append("[TokenBuffer: "); /* sb.append("NativeTypeIds=").append(_hasNativeTypeIds).append(","); sb.append("NativeObjectIds=").append(_hasNativeObjectIds).append(","); */ JsonParser jp = asParser(); int count = 0; final boolean hasNativeIds = _hasNativeTypeIds || _hasNativeObjectIds; while (true) { JsonToken t; try { t = jp.nextToken(); if (t == null) break; if (hasNativeIds) { _appendNativeIds(sb); } if (count < MAX_COUNT) { if (count > 0) { sb.append(", "); } sb.append(t.toString()); if (t == JsonToken.FIELD_NAME) { sb.append('('); sb.append(jp.getCurrentName()); sb.append(')'); } } } catch (IOException ioe) { // should never occur throw new IllegalStateException(ioe); } ++count; } if (count >= MAX_COUNT) { sb.append(" ... (truncated ").append(count-MAX_COUNT).append(" entries)"); } sb.append(']'); return sb.toString(); } private final void _appendNativeIds(StringBuilder sb) { Object objectId = _last.findObjectId(_appendAt-1); if (objectId != null) { sb.append("[objectId=").append(String.valueOf(objectId)).append(']'); } Object typeId = _last.findTypeId(_appendAt-1); if (typeId != null) { sb.append("[typeId=").append(String.valueOf(typeId)).append(']'); } } /* /********************************************************** /* JsonGenerator implementation: configuration /********************************************************** */ @Override public JsonGenerator enable(Feature f) { _generatorFeatures |= f.getMask(); return this; } @Override public JsonGenerator disable(Feature f) { _generatorFeatures &= ~f.getMask(); return this; } //public JsonGenerator configure(SerializationFeature f, boolean state) { } @Override public boolean isEnabled(Feature f) { return (_generatorFeatures & f.getMask()) != 0; } @Override public int getFeatureMask() { return _generatorFeatures; } @Override @Deprecated public JsonGenerator setFeatureMask(int mask) { _generatorFeatures = mask; return this; } @Override public JsonGenerator overrideStdFeatures(int values, int mask) { int oldState = getFeatureMask(); _generatorFeatures = (oldState & ~mask) | (values & mask); return this; } @Override public JsonGenerator useDefaultPrettyPrinter() { // No-op: we don't indent return this; } @Override public JsonGenerator setCodec(ObjectCodec oc) { _objectCodec = oc; return this; } @Override public ObjectCodec getCodec() { return _objectCodec; } @Override public final JsonWriteContext getOutputContext() { return _writeContext; } /* /********************************************************** /* JsonGenerator implementation: capability introspection /********************************************************** */
Since we can efficiently store byte[], yes.
/** * Since we can efficiently store <code>byte[]</code>, yes. */
@Override public boolean canWriteBinaryNatively() { return true; } /* /********************************************************** /* JsonGenerator implementation: low-level output handling /********************************************************** */ @Override public void flush() throws IOException { /* NOP */ } @Override public void close() throws IOException { _closed = true; } @Override public boolean isClosed() { return _closed; } /* /********************************************************** /* JsonGenerator implementation: write methods, structural /********************************************************** */ @Override public final void writeStartArray() throws IOException { _writeContext.writeValue(); _append(JsonToken.START_ARRAY); _writeContext = _writeContext.createChildArrayContext(); } @Override public final void writeEndArray() throws IOException { _append(JsonToken.END_ARRAY); // Let's allow unbalanced tho... i.e. not run out of root level, ever JsonWriteContext c = _writeContext.getParent(); if (c != null) { _writeContext = c; } } @Override public final void writeStartObject() throws IOException { _writeContext.writeValue(); _append(JsonToken.START_OBJECT); _writeContext = _writeContext.createChildObjectContext(); } @Override // since 2.8 public void writeStartObject(Object forValue) throws IOException { _writeContext.writeValue(); _append(JsonToken.START_OBJECT); JsonWriteContext ctxt = _writeContext.createChildObjectContext(); _writeContext = ctxt; if (forValue != null) { ctxt.setCurrentValue(forValue); } } @Override public final void writeEndObject() throws IOException { _append(JsonToken.END_OBJECT); // Let's allow unbalanced tho... i.e. not run out of root level, ever JsonWriteContext c = _writeContext.getParent(); if (c != null) { _writeContext = c; } } @Override public final void writeFieldName(String name) throws IOException { _writeContext.writeFieldName(name); _append(JsonToken.FIELD_NAME, name); } @Override public void writeFieldName(SerializableString name) throws IOException { _writeContext.writeFieldName(name.getValue()); _append(JsonToken.FIELD_NAME, name); } /* /********************************************************** /* JsonGenerator implementation: write methods, textual /********************************************************** */ @Override public void writeString(String text) throws IOException { if (text == null) { writeNull(); } else { _appendValue(JsonToken.VALUE_STRING, text); } } @Override public void writeString(char[] text, int offset, int len) throws IOException { writeString(new String(text, offset, len)); } @Override public void writeString(SerializableString text) throws IOException { if (text == null) { writeNull(); } else { _appendValue(JsonToken.VALUE_STRING, text); } } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { // could add support for buffering if we really want it... _reportUnsupportedOperation(); } @Override public void writeUTF8String(byte[] text, int offset, int length) throws IOException { // could add support for buffering if we really want it... _reportUnsupportedOperation(); } @Override public void writeRaw(String text) throws IOException { _reportUnsupportedOperation(); } @Override public void writeRaw(String text, int offset, int len) throws IOException { _reportUnsupportedOperation(); } @Override public void writeRaw(SerializableString text) throws IOException { _reportUnsupportedOperation(); } @Override public void writeRaw(char[] text, int offset, int len) throws IOException { _reportUnsupportedOperation(); } @Override public void writeRaw(char c) throws IOException { _reportUnsupportedOperation(); } @Override public void writeRawValue(String text) throws IOException { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text)); } @Override public void writeRawValue(String text, int offset, int len) throws IOException { if (offset > 0 || len != text.length()) { text = text.substring(offset, offset+len); } _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text)); } @Override public void writeRawValue(char[] text, int offset, int len) throws IOException { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new String(text, offset, len)); } /* /********************************************************** /* JsonGenerator implementation: write methods, primitive types /********************************************************** */ @Override public void writeNumber(short i) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_INT, Short.valueOf(i)); } @Override public void writeNumber(int i) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i)); } @Override public void writeNumber(long l) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l)); } @Override public void writeNumber(double d) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d)); } @Override public void writeNumber(float f) throws IOException { _appendValue(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f)); } @Override public void writeNumber(BigDecimal dec) throws IOException { if (dec == null) { writeNull(); } else { _appendValue(JsonToken.VALUE_NUMBER_FLOAT, dec); } } @Override public void writeNumber(BigInteger v) throws IOException { if (v == null) { writeNull(); } else { _appendValue(JsonToken.VALUE_NUMBER_INT, v); } } @Override public void writeNumber(String encodedValue) throws IOException { /* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric * identity as long as possible */ _appendValue(JsonToken.VALUE_NUMBER_FLOAT, encodedValue); } @Override public void writeBoolean(boolean state) throws IOException { _appendValue(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE); } @Override public void writeNull() throws IOException { _appendValue(JsonToken.VALUE_NULL); } /* /*********************************************************** /* JsonGenerator implementation: write methods for POJOs/trees /*********************************************************** */ @Override public void writeObject(Object value) throws IOException { if (value == null) { writeNull(); return; } Class<?> raw = value.getClass(); if (raw == byte[].class || (value instanceof RawValue)) { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value); return; } if (_objectCodec == null) { /* 28-May-2014, tatu: Tricky choice here; if no codec, should we * err out, or just embed? For now, do latter. */ // throw new JsonMappingException("No ObjectCodec configured for TokenBuffer, writeObject() called"); _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value); } else { _objectCodec.writeValue(this, value); } } @Override public void writeTree(TreeNode node) throws IOException { if (node == null) { writeNull(); return; } if (_objectCodec == null) { // as with 'writeObject()', is codec optional? _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, node); } else { _objectCodec.writeTree(this, node); } } /* /*********************************************************** /* JsonGenerator implementation; binary /*********************************************************** */ @Override public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException { /* 31-Dec-2009, tatu: can do this using multiple alternatives; but for * now, let's try to limit number of conversions. * The only (?) tricky thing is that of whether to preserve variant, * seems pointless, so let's not worry about it unless there's some * compelling reason to. */ byte[] copy = new byte[len]; System.arraycopy(data, offset, copy, 0, len); writeObject(copy); }
Although we could support this method, it does not necessarily make sense: we cannot make good use of streaming because buffer must hold all the data. Because of this, currently this will simply throw UnsupportedOperationException
/** * Although we could support this method, it does not necessarily make * sense: we cannot make good use of streaming because buffer must * hold all the data. Because of this, currently this will simply * throw {@link UnsupportedOperationException} */
@Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) { throw new UnsupportedOperationException(); } /* /*********************************************************** /* JsonGenerator implementation: native ids /*********************************************************** */ @Override public boolean canWriteTypeId() { return _hasNativeTypeIds; } @Override public boolean canWriteObjectId() { return _hasNativeObjectIds; } @Override public void writeTypeId(Object id) { _typeId = id; _hasNativeId = true; } @Override public void writeObjectId(Object id) { _objectId = id; _hasNativeId = true; } @Override // since 2.8 public void writeEmbeddedObject(Object object) throws IOException { _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, object); } /* /********************************************************** /* JsonGenerator implementation; pass-through copy /********************************************************** */ @Override public void copyCurrentEvent(JsonParser p) throws IOException { if (_mayHaveNativeIds) { _checkNativeIds(p); } switch (p.getCurrentToken()) { case START_OBJECT: writeStartObject(); break; case END_OBJECT: writeEndObject(); break; case START_ARRAY: writeStartArray(); break; case END_ARRAY: writeEndArray(); break; case FIELD_NAME: writeFieldName(p.getCurrentName()); break; case VALUE_STRING: if (p.hasTextCharacters()) { writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength()); } else { writeString(p.getText()); } break; case VALUE_NUMBER_INT: switch (p.getNumberType()) { case INT: writeNumber(p.getIntValue()); break; case BIG_INTEGER: writeNumber(p.getBigIntegerValue()); break; default: writeNumber(p.getLongValue()); } break; case VALUE_NUMBER_FLOAT: if (_forceBigDecimal) { /* 10-Oct-2015, tatu: Ideally we would first determine whether underlying * number is already decoded into a number (in which case might as well * access as number); or is still retained as text (in which case we * should further defer decoding that may not need BigDecimal): */ writeNumber(p.getDecimalValue()); } else { switch (p.getNumberType()) { case BIG_DECIMAL: writeNumber(p.getDecimalValue()); break; case FLOAT: writeNumber(p.getFloatValue()); break; default: writeNumber(p.getDoubleValue()); } } break; case VALUE_TRUE: writeBoolean(true); break; case VALUE_FALSE: writeBoolean(false); break; case VALUE_NULL: writeNull(); break; case VALUE_EMBEDDED_OBJECT: writeObject(p.getEmbeddedObject()); break; default: throw new RuntimeException("Internal error: should never end up through this code path"); } } @Override public void copyCurrentStructure(JsonParser p) throws IOException { JsonToken t = p.getCurrentToken(); // Let's handle field-name separately first if (t == JsonToken.FIELD_NAME) { if (_mayHaveNativeIds) { _checkNativeIds(p); } writeFieldName(p.getCurrentName()); t = p.nextToken(); // fall-through to copy the associated value } if (_mayHaveNativeIds) { _checkNativeIds(p); } switch (t) { case START_ARRAY: writeStartArray(); while (p.nextToken() != JsonToken.END_ARRAY) { copyCurrentStructure(p); } writeEndArray(); break; case START_OBJECT: writeStartObject(); while (p.nextToken() != JsonToken.END_OBJECT) { copyCurrentStructure(p); } writeEndObject(); break; default: // others are simple: copyCurrentEvent(p); } } private final void _checkNativeIds(JsonParser jp) throws IOException { if ((_typeId = jp.getTypeId()) != null) { _hasNativeId = true; } if ((_objectId = jp.getObjectId()) != null) { _hasNativeId = true; } } /* /********************************************************** /* Internal methods /********************************************************** */ protected final void _append(JsonToken type) { Segment next = _hasNativeId ? _last.append(_appendAt, type, _objectId, _typeId) : _last.append(_appendAt, type); if (next == null) { ++_appendAt; } else { _last = next; _appendAt = 1; // since we added first at 0 } } protected final void _append(JsonToken type, Object value) { Segment next = _hasNativeId ? _last.append(_appendAt, type, value, _objectId, _typeId) : _last.append(_appendAt, type, value); if (next == null) { ++_appendAt; } else { _last = next; _appendAt = 1; } }
Similar to _append(JsonToken) but also updates context with knowledge that a scalar value was written
Since:2.6.4
/** * Similar to {@link #_append(JsonToken)} but also updates context with * knowledge that a scalar value was written * * @since 2.6.4 */
protected final void _appendValue(JsonToken type) { _writeContext.writeValue(); Segment next = _hasNativeId ? _last.append(_appendAt, type, _objectId, _typeId) : _last.append(_appendAt, type); if (next == null) { ++_appendAt; } else { _last = next; _appendAt = 1; // since we added first at 0 } }
Similar to _append(JsonToken, Object) but also updates context with knowledge that a scalar value was written
Since:2.6.4
/** * Similar to {@link #_append(JsonToken,Object)} but also updates context with * knowledge that a scalar value was written * * @since 2.6.4 */
protected final void _appendValue(JsonToken type, Object value) { _writeContext.writeValue(); Segment next = _hasNativeId ? _last.append(_appendAt, type, value, _objectId, _typeId) : _last.append(_appendAt, type, value); if (next == null) { ++_appendAt; } else { _last = next; _appendAt = 1; } } // 21-Oct-2016, tatu: Does not seem to be used or needed /* protected final void _appendRaw(int rawType, Object value) { Segment next = _hasNativeId ? _last.appendRaw(_appendAt, rawType, value, _objectId, _typeId) : _last.appendRaw(_appendAt, rawType, value); if (next == null) { ++_appendAt; } else { _last = next; _appendAt = 1; } } */ @Override protected void _reportUnsupportedOperation() { throw new UnsupportedOperationException("Called operation not supported for TokenBuffer"); } /* /********************************************************** /* Supporting classes /********************************************************** */ protected final static class Parser extends ParserMinimalBase { /* /********************************************************** /* Configuration /********************************************************** */ protected ObjectCodec _codec;
Since:2.3
/** * @since 2.3 */
protected final boolean _hasNativeTypeIds;
Since:2.3
/** * @since 2.3 */
protected final boolean _hasNativeObjectIds; protected final boolean _hasNativeIds; /* /********************************************************** /* Parsing state /********************************************************** */
Currently active segment
/** * Currently active segment */
protected Segment _segment;
Pointer to current token within current 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).
/** * Information about parser context, context in which * the next token is to be parsed (root, array, object). */
protected TokenBufferReadContext _parsingContext; protected boolean _closed; protected transient ByteArrayBuilder _byteBuilder; protected JsonLocation _location = null; /* /********************************************************** /* Construction, init /********************************************************** */ @Deprecated // since 2.9 public Parser(Segment firstSeg, ObjectCodec codec, boolean hasNativeTypeIds, boolean hasNativeObjectIds) { this(firstSeg, codec, hasNativeTypeIds, hasNativeObjectIds, null); } public Parser(Segment firstSeg, ObjectCodec codec, boolean hasNativeTypeIds, boolean hasNativeObjectIds, JsonStreamContext parentContext) { super(0); _segment = firstSeg; _segmentPtr = -1; // not yet read _codec = codec; _parsingContext = TokenBufferReadContext.createRootContext(parentContext); _hasNativeTypeIds = hasNativeTypeIds; _hasNativeObjectIds = hasNativeObjectIds; _hasNativeIds = (hasNativeTypeIds | hasNativeObjectIds); } public void setLocation(JsonLocation l) { _location = l; } @Override public ObjectCodec getCodec() { return _codec; } @Override public void setCodec(ObjectCodec c) { _codec = c; } @Override public Version version() { return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; } /* /********************************************************** /* Extended API beyond JsonParser /********************************************************** */ public JsonToken peekNextToken() throws IOException { // closed? nothing more to peek, either if (_closed) return null; Segment seg = _segment; int ptr = _segmentPtr+1; if (ptr >= Segment.TOKENS_PER_SEGMENT) { ptr = 0; seg = (seg == null) ? null : seg.next(); } return (seg == null) ? null : seg.type(ptr); } /* /********************************************************** /* Closeable implementation /********************************************************** */ @Override public void close() throws IOException { if (!_closed) { _closed = true; } } /* /********************************************************** /* Public API, traversal /********************************************************** */ @Override public JsonToken nextToken() throws IOException { // If we are closed, nothing more to do if (_closed || (_segment == null)) return null; // Ok, then: any more tokens? if (++_segmentPtr >= Segment.TOKENS_PER_SEGMENT) { _segmentPtr = 0; _segment = _segment.next(); if (_segment == null) { return null; } } _currToken = _segment.type(_segmentPtr); // Field name? Need to update context if (_currToken == JsonToken.FIELD_NAME) { Object ob = _currentObject(); String name = (ob instanceof String) ? ((String) ob) : ob.toString(); _parsingContext.setCurrentName(name); } else if (_currToken == JsonToken.START_OBJECT) { _parsingContext = _parsingContext.createChildObjectContext(); } else if (_currToken == JsonToken.START_ARRAY) { _parsingContext = _parsingContext.createChildArrayContext(); } else if (_currToken == JsonToken.END_OBJECT || _currToken == JsonToken.END_ARRAY) { // Closing JSON Object/Array? Close matching context _parsingContext = _parsingContext.parentOrCopy(); } return _currToken; } @Override public String nextFieldName() throws IOException { // inlined common case from nextToken() if (_closed || (_segment == null)) { return null; } int ptr = _segmentPtr+1; if ((ptr < Segment.TOKENS_PER_SEGMENT) && (_segment.type(ptr) == JsonToken.FIELD_NAME)) { _segmentPtr = ptr; _currToken = JsonToken.FIELD_NAME; Object ob = _segment.get(ptr); // inlined _currentObject(); String name = (ob instanceof String) ? ((String) ob) : ob.toString(); _parsingContext.setCurrentName(name); return name; } return (nextToken() == JsonToken.FIELD_NAME) ? getCurrentName() : null; } @Override public boolean isClosed() { return _closed; } /* /********************************************************** /* Public API, token accessors /********************************************************** */ @Override public JsonStreamContext getParsingContext() { return _parsingContext; } @Override public JsonLocation getTokenLocation() { return getCurrentLocation(); } @Override public JsonLocation getCurrentLocation() { return (_location == null) ? JsonLocation.NA : _location; } @Override public String getCurrentName() { // 25-Jun-2015, tatu: as per [databind#838], needs to be same as ParserBase if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { JsonStreamContext parent = _parsingContext.getParent(); return parent.getCurrentName(); } return _parsingContext.getCurrentName(); } @Override public void overrideCurrentName(String name) { // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing: JsonStreamContext ctxt = _parsingContext; if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { ctxt = ctxt.getParent(); } if (ctxt instanceof TokenBufferReadContext) { try { ((TokenBufferReadContext) ctxt).setCurrentName(name); } catch (IOException e) { throw new RuntimeException(e); } } } /* /********************************************************** /* Public API, access to token information, text /********************************************************** */ @Override public String getText() { // common cases first: if (_currToken == JsonToken.VALUE_STRING || _currToken == JsonToken.FIELD_NAME) { Object ob = _currentObject(); if (ob instanceof String) { return (String) ob; } return ClassUtil.nullOrToString(ob); } if (_currToken == null) { return null; } switch (_currToken) { case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return ClassUtil.nullOrToString(_currentObject()); default: return _currToken.asString(); } } @Override public char[] getTextCharacters() { String str = getText(); return (str == null) ? null : str.toCharArray(); } @Override public int getTextLength() { String str = getText(); return (str == null) ? 0 : str.length(); } @Override public int getTextOffset() { return 0; } @Override public boolean hasTextCharacters() { // We never have raw buffer available, so: return false; } /* /********************************************************** /* Public API, access to token information, numeric /********************************************************** */ @Override public boolean isNaN() { // can only occur for floating-point numbers if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { Object value = _currentObject(); if (value instanceof Double) { Double v = (Double) value; return v.isNaN() || v.isInfinite(); } if (value instanceof Float) { Float v = (Float) value; return v.isNaN() || v.isInfinite(); } } return false; } @Override public BigInteger getBigIntegerValue() throws IOException { Number n = getNumberValue(); if (n instanceof BigInteger) { return (BigInteger) n; } if (getNumberType() == NumberType.BIG_DECIMAL) { return ((BigDecimal) n).toBigInteger(); } // int/long is simple, but let's also just truncate float/double: return BigInteger.valueOf(n.longValue()); } @Override public BigDecimal getDecimalValue() throws IOException { Number n = getNumberValue(); if (n instanceof BigDecimal) { return (BigDecimal) n; } switch (getNumberType()) { case INT: case LONG: return BigDecimal.valueOf(n.longValue()); case BIG_INTEGER: return new BigDecimal((BigInteger) n); default: } // float or double return BigDecimal.valueOf(n.doubleValue()); } @Override public double getDoubleValue() throws IOException { return getNumberValue().doubleValue(); } @Override public float getFloatValue() throws IOException { return getNumberValue().floatValue(); } @Override public int getIntValue() throws IOException { Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ? ((Number) _currentObject()) : getNumberValue(); if ((n instanceof Integer) || _smallerThanInt(n)) { return n.intValue(); } return _convertNumberToInt(n); } @Override public long getLongValue() throws IOException { Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ? ((Number) _currentObject()) : getNumberValue(); if ((n instanceof Long) || _smallerThanLong(n)) { return n.longValue(); } return _convertNumberToLong(n); } @Override public NumberType getNumberType() throws IOException { Number n = getNumberValue(); if (n instanceof Integer) return NumberType.INT; if (n instanceof Long) return NumberType.LONG; if (n instanceof Double) return NumberType.DOUBLE; if (n instanceof BigDecimal) return NumberType.BIG_DECIMAL; if (n instanceof BigInteger) return NumberType.BIG_INTEGER; if (n instanceof Float) return NumberType.FLOAT; if (n instanceof Short) return NumberType.INT; // should be SHORT return null; } @Override public final Number getNumberValue() throws IOException { _checkIsNumber(); Object value = _currentObject(); if (value instanceof Number) { return (Number) value; } // Difficult to really support numbers-as-Strings; but let's try. // NOTE: no access to DeserializationConfig, unfortunately, so cannot // try to determine Double/BigDecimal preference... if (value instanceof String) { String str = (String) value; if (str.indexOf('.') >= 0) { return Double.parseDouble(str); } return Long.parseLong(str); } if (value == null) { return null; } throw new IllegalStateException("Internal error: entry should be a Number, but is of type " +value.getClass().getName()); } private final boolean _smallerThanInt(Number n) { return (n instanceof Short) || (n instanceof Byte); } private final boolean _smallerThanLong(Number n) { return (n instanceof Integer) || (n instanceof Short) || (n instanceof Byte); } // 02-Jan-2017, tatu: Modified from method(s) in `ParserBase` protected int _convertNumberToInt(Number n) throws IOException { if (n instanceof Long) { long l = n.longValue(); int result = (int) l; if (((long) result) != l) { reportOverflowInt(); } return result; } if (n instanceof BigInteger) { BigInteger big = (BigInteger) n; if (BI_MIN_INT.compareTo(big) > 0 || BI_MAX_INT.compareTo(big) < 0) { reportOverflowInt(); } } else if ((n instanceof Double) || (n instanceof Float)) { double d = n.doubleValue(); // Need to check boundaries if (d < MIN_INT_D || d > MAX_INT_D) { reportOverflowInt(); } return (int) d; } else if (n instanceof BigDecimal) { BigDecimal big = (BigDecimal) n; if (BD_MIN_INT.compareTo(big) > 0 || BD_MAX_INT.compareTo(big) < 0) { reportOverflowInt(); } } else { _throwInternal(); } return n.intValue(); } protected long _convertNumberToLong(Number n) throws IOException { if (n instanceof BigInteger) { BigInteger big = (BigInteger) n; if (BI_MIN_LONG.compareTo(big) > 0 || BI_MAX_LONG.compareTo(big) < 0) { reportOverflowLong(); } } else if ((n instanceof Double) || (n instanceof Float)) { double d = n.doubleValue(); // Need to check boundaries if (d < MIN_LONG_D || d > MAX_LONG_D) { reportOverflowLong(); } return (long) d; } else if (n instanceof BigDecimal) { BigDecimal big = (BigDecimal) n; if (BD_MIN_LONG.compareTo(big) > 0 || BD_MAX_LONG.compareTo(big) < 0) { reportOverflowLong(); } } else { _throwInternal(); } return n.longValue(); } /* /********************************************************** /* Public API, access to token information, other /********************************************************** */ @Override public Object getEmbeddedObject() { if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) { return _currentObject(); } return null; } @Override @SuppressWarnings("resource") public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException { // First: maybe we some special types? if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) { // Embedded byte array would work nicely... Object ob = _currentObject(); if (ob instanceof byte[]) { return (byte[]) ob; } // fall through to error case } if (_currToken != JsonToken.VALUE_STRING) { throw _constructError("Current token ("+_currToken+") not VALUE_STRING (or VALUE_EMBEDDED_OBJECT with byte[]), cannot access as binary"); } final String str = getText(); if (str == null) { return null; } ByteArrayBuilder builder = _byteBuilder; if (builder == null) { _byteBuilder = builder = new ByteArrayBuilder(100); } else { _byteBuilder.reset(); } _decodeBase64(str, builder, b64variant); return builder.toByteArray(); } @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { byte[] data = getBinaryValue(b64variant); if (data != null) { out.write(data, 0, data.length); return data.length; } return 0; } /* /********************************************************** /* Public API, native ids /********************************************************** */ @Override public boolean canReadObjectId() { return _hasNativeObjectIds; } @Override public boolean canReadTypeId() { return _hasNativeTypeIds; } @Override public Object getTypeId() { return _segment.findTypeId(_segmentPtr); } @Override public Object getObjectId() { return _segment.findObjectId(_segmentPtr); } /* /********************************************************** /* Internal methods /********************************************************** */ protected final Object _currentObject() { return _segment.get(_segmentPtr); } protected final void _checkIsNumber() throws JsonParseException { if (_currToken == null || !_currToken.isNumeric()) { throw _constructError("Current token ("+_currToken+") not numeric, cannot use numeric value accessors"); } } @Override protected void _handleEOF() throws JsonParseException { _throwInternal(); } }
Individual segment of TokenBuffer that can store up to 16 tokens (limited by 4 bits per token type marker requirement). Current implementation uses fixed length array; could alternatively use 16 distinct fields and switch statement (slightly more efficient storage, slightly slower access)
/** * 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 JsonToken instances
/** * Static array used for fast conversion between token markers and * matching {@link JsonToken} instances */
private final static JsonToken[] TOKEN_TYPES_BY_INDEX; static { // ... here we know that there are <= 15 values in JsonToken enum TOKEN_TYPES_BY_INDEX = new JsonToken[16]; JsonToken[] t = JsonToken.values(); // and reserve entry 0 for "not available" System.arraycopy(t, 1, TOKEN_TYPES_BY_INDEX, 1, Math.min(15, t.length - 1)); } // // // Linking protected Segment _next; // // // State
Bit field used to store types of buffered tokens; 4 bits per token. Value 0 is reserved for "not in use"
/** * Bit field used to store types of buffered tokens; 4 bits per token. * Value 0 is reserved for "not in use" */
protected long _tokenTypes; // Actual tokens protected final Object[] _tokens = new Object[TOKENS_PER_SEGMENT];
Lazily constructed Map for storing native type and object ids, if any
/** * Lazily constructed Map for storing native type and object ids, if any */
protected TreeMap<Integer,Object> _nativeIds; public Segment() { } // // // Accessors public JsonToken type(int index) { long l = _tokenTypes; if (index > 0) { l >>= (index << 2); } int ix = ((int) l) & 0xF; return TOKEN_TYPES_BY_INDEX[ix]; } public int rawType(int index) { long l = _tokenTypes; if (index > 0) { l >>= (index << 2); } int ix = ((int) l) & 0xF; return ix; } public Object get(int index) { return _tokens[index]; } public Segment next() { return _next; }
Accessor for checking whether this segment may have native type or object ids.
/** * Accessor for checking whether this segment may have native * type or object ids. */
public boolean hasIds() { return _nativeIds != null; } // // // Mutators public Segment append(int index, JsonToken tokenType) { if (index < TOKENS_PER_SEGMENT) { set(index, tokenType); return null; } _next = new Segment(); _next.set(0, tokenType); return _next; } public Segment append(int index, JsonToken tokenType, Object objectId, Object typeId) { if (index < TOKENS_PER_SEGMENT) { set(index, tokenType, objectId, typeId); return null; } _next = new Segment(); _next.set(0, tokenType, objectId, typeId); return _next; } public Segment append(int index, JsonToken tokenType, Object value) { if (index < TOKENS_PER_SEGMENT) { set(index, tokenType, value); return null; } _next = new Segment(); _next.set(0, tokenType, value); return _next; } public Segment append(int index, JsonToken tokenType, Object value, Object objectId, Object typeId) { if (index < TOKENS_PER_SEGMENT) { set(index, tokenType, value, objectId, typeId); return null; } _next = new Segment(); _next.set(0, tokenType, value, objectId, typeId); return _next; } /* public Segment appendRaw(int index, int rawTokenType, Object value) { if (index < TOKENS_PER_SEGMENT) { set(index, rawTokenType, value); return null; } _next = new Segment(); _next.set(0, rawTokenType, value); return _next; } public Segment appendRaw(int index, int rawTokenType, Object value, Object objectId, Object typeId) { if (index < TOKENS_PER_SEGMENT) { set(index, rawTokenType, value, objectId, typeId); return null; } _next = new Segment(); _next.set(0, rawTokenType, value, objectId, typeId); return _next; } private void set(int index, int rawTokenType, Object value, Object objectId, Object typeId) { _tokens[index] = value; long typeCode = (long) rawTokenType; if (index > 0) { typeCode <<= (index << 2); } _tokenTypes |= typeCode; assignNativeIds(index, objectId, typeId); } private void set(int index, int rawTokenType, Object value) { _tokens[index] = value; long typeCode = (long) rawTokenType; if (index > 0) { typeCode <<= (index << 2); } _tokenTypes |= typeCode; } */ private void set(int index, JsonToken tokenType) { /* Assumption here is that there are no overwrites, just appends; * and so no masking is needed (nor explicit setting of null) */ long typeCode = tokenType.ordinal(); if (index > 0) { typeCode <<= (index << 2); } _tokenTypes |= typeCode; } private void set(int index, JsonToken tokenType, Object objectId, Object typeId) { long typeCode = tokenType.ordinal(); if (index > 0) { typeCode <<= (index << 2); } _tokenTypes |= typeCode; assignNativeIds(index, objectId, typeId); } private void set(int index, JsonToken tokenType, Object value) { _tokens[index] = value; long typeCode = tokenType.ordinal(); if (index > 0) { typeCode <<= (index << 2); } _tokenTypes |= typeCode; } private void set(int index, JsonToken tokenType, Object value, Object objectId, Object typeId) { _tokens[index] = value; long typeCode = tokenType.ordinal(); if (index > 0) { typeCode <<= (index << 2); } _tokenTypes |= typeCode; assignNativeIds(index, objectId, typeId); } private final void assignNativeIds(int index, Object objectId, Object typeId) { if (_nativeIds == null) { _nativeIds = new TreeMap<Integer,Object>(); } if (objectId != null) { _nativeIds.put(_objectIdIndex(index), objectId); } if (typeId != null) { _nativeIds.put(_typeIdIndex(index), typeId); } }
Since:2.3
/** * @since 2.3 */
private Object findObjectId(int index) { return (_nativeIds == null) ? null : _nativeIds.get(_objectIdIndex(index)); }
Since:2.3
/** * @since 2.3 */
private Object findTypeId(int index) { return (_nativeIds == null) ? null : _nativeIds.get(_typeIdIndex(index)); } private final int _typeIdIndex(int i) { return i+i; } private final int _objectIdIndex(int i) { return i+i+1; } } }