1 package com.fasterxml.jackson.databind.util;
2 
3 import java.io.*;
4 import java.math.BigDecimal;
5 import java.math.BigInteger;
6 import java.util.TreeMap;
7 
8 import com.fasterxml.jackson.core.*;
9 import com.fasterxml.jackson.core.base.ParserMinimalBase;
10 import com.fasterxml.jackson.core.json.JsonWriteContext;
11 import com.fasterxml.jackson.core.util.ByteArrayBuilder;
12 import com.fasterxml.jackson.core.util.JacksonFeatureSet;
13 import com.fasterxml.jackson.databind.*;
14 
15 /**
16  * Utility class used for efficient storage of {@link JsonToken}
17  * sequences, needed for temporary buffering.
18  * Space efficient for different sequence lengths (especially so for smaller
19  * ones; but not significantly less efficient for larger), highly efficient
20  * for linear iteration and appending. Implemented as segmented/chunked
21  * linked list of tokens; only modifications are via appends.
22  *<p>
23  * Note that before version 2.0, this class was located in the "core"
24  * bundle, not data-binding; but since it was only used by data binding,
25  * was moved here to reduce size of core package
26  */
27 public class TokenBuffer
28 /* Won't use JsonGeneratorBase, to minimize overhead for validity
29  * checking
30  */
31     extends JsonGenerator
32 {
33     protected final static int DEFAULT_GENERATOR_FEATURES = JsonGenerator.Feature.collectDefaults();
34 
35     /*
36     /**********************************************************
37     /* Configuration
38     /**********************************************************
39      */
40 
41     /**
42      * Object codec to use for stream-based object
43      * conversion through parser/generator interfaces. If null,
44      * such methods cannot be used.
45      */
46     protected ObjectCodec _objectCodec;
47 
48     /**
49      * Parse context from "parent" parser (one from which content to buffer is read,
50      * if specified). Used, if available, when reading content, to present full
51      * context as if content was read from the original parser: this is useful
52      * in error reporting and sometimes processing as well.
53      */
54     protected JsonStreamContext _parentContext;
55 
56     /**
57      * Bit flag composed of bits that indicate which
58      * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
59      * are enabled.
60      *<p>
61      * NOTE: most features have no effect on this class
62      */
63     protected int _generatorFeatures;
64 
65     protected boolean _closed;
66 
67     /**
68      * @since 2.3
69      */
70     protected boolean _hasNativeTypeIds;
71 
72     /**
73      * @since 2.3
74      */
75     protected boolean _hasNativeObjectIds;
76 
77     /**
78      * @since 2.3
79      */
80     protected boolean _mayHaveNativeIds;
81 
82     /**
83      * Flag set during construction, if use of {@link BigDecimal} is to be forced
84      * on all floating-point values.
85      *
86      * @since 2.7
87      */
88     protected boolean _forceBigDecimal;
89 
90     /*
91     /**********************************************************
92     /* Token buffering state
93     /**********************************************************
94      */
95 
96     /**
97      * First segment, for contents this buffer has
98      */
99     protected Segment _first;
100 
101     /**
102      * Last segment of this buffer, one that is used
103      * for appending more tokens
104      */
105     protected Segment _last;
106 
107     /**
108      * Offset within last segment,
109      */
110     protected int _appendAt;
111 
112     /**
113      * If native type ids supported, this is the id for following
114      * value (or first token of one) to be written.
115      */
116     protected Object _typeId;
117 
118     /**
119      * If native object ids supported, this is the id for following
120      * value (or first token of one) to be written.
121      */
122     protected Object _objectId;
123 
124     /**
125      * Do we currently have a native type or object id buffered?
126      */
127     protected boolean _hasNativeId = false;
128 
129     /*
130     /**********************************************************
131     /* Output state
132     /**********************************************************
133      */
134 
135     protected JsonWriteContext _writeContext;
136 
137     /*
138     /**********************************************************
139     /* Life-cycle
140     /**********************************************************
141      */
142 
143     /**
144      * @param codec Object codec to use for stream-based object
145      *   conversion through parser/generator interfaces. If null,
146      *   such methods cannot be used.
147      * @param hasNativeIds Whether resulting {@link JsonParser} (if created)
148      *   is considered to support native type and object ids
149      */
TokenBuffer(ObjectCodec codec, boolean hasNativeIds)150     public TokenBuffer(ObjectCodec codec, boolean hasNativeIds)
151     {
152         _objectCodec = codec;
153         _generatorFeatures = DEFAULT_GENERATOR_FEATURES;
154         _writeContext = JsonWriteContext.createRootContext(null);
155         // at first we have just one segment
156         _first = _last = new Segment();
157         _appendAt = 0;
158         _hasNativeTypeIds = hasNativeIds;
159         _hasNativeObjectIds = hasNativeIds;
160 
161         _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds;
162     }
163 
164     /**
165      * @since 2.3
166      */
TokenBuffer(JsonParser p)167     public TokenBuffer(JsonParser p) {
168         this(p, null);
169     }
170 
171     /**
172      * @since 2.7
173      */
TokenBuffer(JsonParser p, DeserializationContext ctxt)174     public TokenBuffer(JsonParser p, DeserializationContext ctxt)
175     {
176         _objectCodec = p.getCodec();
177         _parentContext = p.getParsingContext();
178         _generatorFeatures = DEFAULT_GENERATOR_FEATURES;
179         _writeContext = JsonWriteContext.createRootContext(null);
180         // at first we have just one segment
181         _first = _last = new Segment();
182         _appendAt = 0;
183         _hasNativeTypeIds = p.canReadTypeId();
184         _hasNativeObjectIds = p.canReadObjectId();
185         _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds;
186         _forceBigDecimal = (ctxt == null) ? false
187                 : ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
188     }
189 
190     /**
191      * Convenience method, equivalent to:
192      *<pre>
193      * TokenBuffer b = new TokenBuffer(p);
194      * b.copyCurrentStructure(p);
195      * return b;
196      *</pre>
197      *
198      * @since 2.9
199      */
asCopyOfValue(JsonParser p)200     public static TokenBuffer asCopyOfValue(JsonParser p) throws IOException {
201         TokenBuffer b = new TokenBuffer(p);
202         b.copyCurrentStructure(p);
203         return b;
204     }
205 
206     /**
207      * Method that allows explicitly specifying parent parse context to associate
208      * with contents of this buffer. Usually context is assigned at construction,
209      * based on given parser; but it is not always available, and may not contain
210      * intended context.
211      *
212      * @since 2.9
213      */
overrideParentContext(JsonStreamContext ctxt)214     public TokenBuffer overrideParentContext(JsonStreamContext ctxt) {
215         _parentContext = ctxt;
216         return this;
217     }
218 
219     /**
220      * @since 2.7
221      */
forceUseOfBigDecimal(boolean b)222     public TokenBuffer forceUseOfBigDecimal(boolean b) {
223         _forceBigDecimal = b;
224         return this;
225     }
226 
227     @Override
version()228     public Version version() {
229         return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
230     }
231 
232     /**
233      * Method used to create a {@link JsonParser} that can read contents
234      * stored in this buffer. Will use default <code>_objectCodec</code> for
235      * object conversions.
236      *<p>
237      * Note: instances are not synchronized, that is, they are not thread-safe
238      * if there are concurrent appends to the underlying buffer.
239      *
240      * @return Parser that can be used for reading contents stored in this buffer
241      */
asParser()242     public JsonParser asParser() {
243         return asParser(_objectCodec);
244     }
245 
246     /**
247      * Same as:
248      *<pre>
249      *  JsonParser p = asParser();
250      *  p.nextToken();
251      *  return p;
252      *</pre>
253      *
254      * @since 2.9
255      */
asParserOnFirstToken()256     public JsonParser asParserOnFirstToken() throws IOException {
257         JsonParser p = asParser(_objectCodec);
258         p.nextToken();
259         return p;
260     }
261 
262     /**
263      * Method used to create a {@link JsonParser} that can read contents
264      * stored in this buffer.
265      *<p>
266      * Note: instances are not synchronized, that is, they are not thread-safe
267      * if there are concurrent appends to the underlying buffer.
268      *
269      * @param codec Object codec to use for stream-based object
270      *   conversion through parser/generator interfaces. If null,
271      *   such methods cannot be used.
272      *
273      * @return Parser that can be used for reading contents stored in this buffer
274      */
asParser(ObjectCodec codec)275     public JsonParser asParser(ObjectCodec codec)
276     {
277         return new Parser(_first, codec, _hasNativeTypeIds, _hasNativeObjectIds, _parentContext);
278     }
279 
280     /**
281      * @param src Parser to use for accessing source information
282      *    like location, configured codec
283      */
asParser(JsonParser src)284     public JsonParser asParser(JsonParser src)
285     {
286         Parser p = new Parser(_first, src.getCodec(), _hasNativeTypeIds, _hasNativeObjectIds, _parentContext);
287         p.setLocation(src.getTokenLocation());
288         return p;
289     }
290 
291     /*
292     /**********************************************************
293     /* Additional accessors
294     /**********************************************************
295      */
296 
firstToken()297     public JsonToken firstToken() {
298         // no need to null check; never create without `_first`
299         return _first.type(0);
300     }
301 
302     /*
303     /**********************************************************
304     /* Other custom methods not needed for implementing interfaces
305     /**********************************************************
306      */
307 
308     /**
309      * Helper method that will append contents of given buffer into this
310      * buffer.
311      * Not particularly optimized; can be made faster if there is need.
312      *
313      * @return This buffer
314      */
315     @SuppressWarnings("resource")
append(TokenBuffer other)316     public TokenBuffer append(TokenBuffer other) throws IOException
317     {
318         // Important? If source has native ids, need to store
319         if (!_hasNativeTypeIds) {
320             _hasNativeTypeIds = other.canWriteTypeId();
321         }
322         if (!_hasNativeObjectIds) {
323             _hasNativeObjectIds = other.canWriteObjectId();
324         }
325         _mayHaveNativeIds = _hasNativeTypeIds | _hasNativeObjectIds;
326 
327         JsonParser p = other.asParser();
328         while (p.nextToken() != null) {
329             copyCurrentStructure(p);
330         }
331         return this;
332     }
333 
334     /**
335      * Helper method that will write all contents of this buffer
336      * using given {@link JsonGenerator}.
337      *<p>
338      * Note: this method would be enough to implement
339      * <code>JsonSerializer</code>  for <code>TokenBuffer</code> type;
340      * but we cannot have upwards
341      * references (from core to mapper package); and as such we also
342      * cannot take second argument.
343      */
serialize(JsonGenerator gen)344     public void serialize(JsonGenerator gen) throws IOException
345     {
346         Segment segment = _first;
347         int ptr = -1;
348 
349         final boolean checkIds = _mayHaveNativeIds;
350         boolean hasIds = checkIds && (segment.hasIds());
351 
352         while (true) {
353             if (++ptr >= Segment.TOKENS_PER_SEGMENT) {
354                 ptr = 0;
355                 segment = segment.next();
356                 if (segment == null) break;
357                 hasIds = checkIds && (segment.hasIds());
358             }
359             JsonToken t = segment.type(ptr);
360             if (t == null) break;
361 
362             if (hasIds) {
363                 Object id = segment.findObjectId(ptr);
364                 if (id != null) {
365                     gen.writeObjectId(id);
366                 }
367                 id = segment.findTypeId(ptr);
368                 if (id != null) {
369                     gen.writeTypeId(id);
370                 }
371             }
372 
373             // Note: copied from 'copyCurrentEvent'...
374             switch (t) {
375             case START_OBJECT:
376                 gen.writeStartObject();
377                 break;
378             case END_OBJECT:
379                 gen.writeEndObject();
380                 break;
381             case START_ARRAY:
382                 gen.writeStartArray();
383                 break;
384             case END_ARRAY:
385                 gen.writeEndArray();
386                 break;
387             case FIELD_NAME:
388             {
389                 // 13-Dec-2010, tatu: Maybe we should start using different type tokens to reduce casting?
390                 Object ob = segment.get(ptr);
391                 if (ob instanceof SerializableString) {
392                     gen.writeFieldName((SerializableString) ob);
393                 } else {
394                     gen.writeFieldName((String) ob);
395                 }
396             }
397                 break;
398             case VALUE_STRING:
399                 {
400                     Object ob = segment.get(ptr);
401                     if (ob instanceof SerializableString) {
402                         gen.writeString((SerializableString) ob);
403                     } else {
404                         gen.writeString((String) ob);
405                     }
406                 }
407                 break;
408             case VALUE_NUMBER_INT:
409                 {
410                     Object n = segment.get(ptr);
411                     if (n instanceof Integer) {
412                         gen.writeNumber((Integer) n);
413                     } else if (n instanceof BigInteger) {
414                         gen.writeNumber((BigInteger) n);
415                     } else if (n instanceof Long) {
416                         gen.writeNumber((Long) n);
417                     } else if (n instanceof Short) {
418                         gen.writeNumber((Short) n);
419                     } else {
420                         gen.writeNumber(((Number) n).intValue());
421                     }
422                 }
423                 break;
424             case VALUE_NUMBER_FLOAT:
425                 {
426                     Object n = segment.get(ptr);
427                     if (n instanceof Double) {
428                         gen.writeNumber(((Double) n).doubleValue());
429                     } else if (n instanceof BigDecimal) {
430                         gen.writeNumber((BigDecimal) n);
431                     } else if (n instanceof Float) {
432                         gen.writeNumber(((Float) n).floatValue());
433                     } else if (n == null) {
434                         gen.writeNull();
435                     } else if (n instanceof String) {
436                         gen.writeNumber((String) n);
437                     } else {
438                         throw new JsonGenerationException(String.format(
439                                 "Unrecognized value type for VALUE_NUMBER_FLOAT: %s, cannot serialize",
440                                 n.getClass().getName()), gen);
441                     }
442                 }
443                 break;
444             case VALUE_TRUE:
445                 gen.writeBoolean(true);
446                 break;
447             case VALUE_FALSE:
448                 gen.writeBoolean(false);
449                 break;
450             case VALUE_NULL:
451                 gen.writeNull();
452                 break;
453             case VALUE_EMBEDDED_OBJECT:
454                 {
455                     Object value = segment.get(ptr);
456                     // 01-Sep-2016, tatu: as per [databind#1361], should use `writeEmbeddedObject()`;
457                     //    however, may need to consider alternatives for some well-known types
458                     //    first
459                     if (value instanceof RawValue) {
460                         ((RawValue) value).serialize(gen);
461                     } else if (value instanceof JsonSerializable) {
462                         gen.writeObject(value);
463                     } else {
464                         gen.writeEmbeddedObject(value);
465                     }
466                 }
467                 break;
468             default:
469                 throw new RuntimeException("Internal error: should never end up through this code path");
470             }
471         }
472     }
473 
474     /**
475      * Helper method used by standard deserializer.
476      *
477      * @since 2.3
478      */
deserialize(JsonParser p, DeserializationContext ctxt)479     public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
480     {
481         if (!p.hasToken(JsonToken.FIELD_NAME)) {
482             copyCurrentStructure(p);
483             return this;
484         }
485         /* 28-Oct-2014, tatu: As per [databind#592], need to support a special case of starting from
486          *    FIELD_NAME, which is taken to mean that we are missing START_OBJECT, but need
487          *    to assume one did exist.
488          */
489         JsonToken t;
490         writeStartObject();
491         do {
492             copyCurrentStructure(p);
493         } while ((t = p.nextToken()) == JsonToken.FIELD_NAME);
494         if (t != JsonToken.END_OBJECT) {
495             ctxt.reportWrongTokenException(TokenBuffer.class, JsonToken.END_OBJECT,
496                     "Expected END_OBJECT after copying contents of a JsonParser into TokenBuffer, got "+t);
497             // never gets here
498         }
499         writeEndObject();
500         return this;
501     }
502 
503     @Override
504     @SuppressWarnings("resource")
toString()505     public String toString()
506     {
507         // Let's print up to 100 first tokens...
508         final int MAX_COUNT = 100;
509 
510         StringBuilder sb = new StringBuilder();
511         sb.append("[TokenBuffer: ");
512 
513         /*
514 sb.append("NativeTypeIds=").append(_hasNativeTypeIds).append(",");
515 sb.append("NativeObjectIds=").append(_hasNativeObjectIds).append(",");
516 */
517 
518         JsonParser jp = asParser();
519         int count = 0;
520         final boolean hasNativeIds = _hasNativeTypeIds || _hasNativeObjectIds;
521 
522         while (true) {
523             JsonToken t;
524             try {
525                 t = jp.nextToken();
526                 if (t == null) break;
527 
528                 if (hasNativeIds) {
529                     _appendNativeIds(sb);
530                 }
531 
532                 if (count < MAX_COUNT) {
533                     if (count > 0) {
534                         sb.append(", ");
535                     }
536                     sb.append(t.toString());
537                     if (t == JsonToken.FIELD_NAME) {
538                         sb.append('(');
539                         sb.append(jp.currentName());
540                         sb.append(')');
541                     }
542                 }
543             } catch (IOException ioe) { // should never occur
544                 throw new IllegalStateException(ioe);
545             }
546             ++count;
547         }
548 
549         if (count >= MAX_COUNT) {
550             sb.append(" ... (truncated ").append(count-MAX_COUNT).append(" entries)");
551         }
552         sb.append(']');
553         return sb.toString();
554     }
555 
_appendNativeIds(StringBuilder sb)556     private final void _appendNativeIds(StringBuilder sb)
557     {
558         Object objectId = _last.findObjectId(_appendAt-1);
559         if (objectId != null) {
560             sb.append("[objectId=").append(String.valueOf(objectId)).append(']');
561         }
562         Object typeId = _last.findTypeId(_appendAt-1);
563         if (typeId != null) {
564             sb.append("[typeId=").append(String.valueOf(typeId)).append(']');
565         }
566     }
567 
568     /*
569     /**********************************************************
570     /* JsonGenerator implementation: configuration
571     /**********************************************************
572      */
573 
574     @Override
enable(Feature f)575     public JsonGenerator enable(Feature f) {
576         _generatorFeatures |= f.getMask();
577         return this;
578     }
579 
580     @Override
disable(Feature f)581     public JsonGenerator disable(Feature f) {
582         _generatorFeatures &= ~f.getMask();
583         return this;
584     }
585 
586     //public JsonGenerator configure(SerializationFeature f, boolean state) { }
587 
588     @Override
isEnabled(Feature f)589     public boolean isEnabled(Feature f) {
590         return (_generatorFeatures & f.getMask()) != 0;
591     }
592 
593     @Override
getFeatureMask()594     public int getFeatureMask() {
595         return _generatorFeatures;
596     }
597 
598     @Override
599     @Deprecated
setFeatureMask(int mask)600     public JsonGenerator setFeatureMask(int mask) {
601         _generatorFeatures = mask;
602         return this;
603     }
604 
605     @Override
overrideStdFeatures(int values, int mask)606     public JsonGenerator overrideStdFeatures(int values, int mask) {
607         int oldState = getFeatureMask();
608         _generatorFeatures = (oldState & ~mask) | (values & mask);
609         return this;
610     }
611 
612     @Override
useDefaultPrettyPrinter()613     public JsonGenerator useDefaultPrettyPrinter() {
614         // No-op: we don't indent
615         return this;
616     }
617 
618     @Override
setCodec(ObjectCodec oc)619     public JsonGenerator setCodec(ObjectCodec oc) {
620         _objectCodec = oc;
621         return this;
622     }
623 
624     @Override
getCodec()625     public ObjectCodec getCodec() { return _objectCodec; }
626 
627     @Override
getOutputContext()628     public final JsonWriteContext getOutputContext() { return _writeContext; }
629 
630     /*
631     /**********************************************************
632     /* JsonGenerator implementation: capability introspection
633     /**********************************************************
634      */
635 
636     /**
637      * Since we can efficiently store <code>byte[]</code>, yes.
638      */
639     @Override
canWriteBinaryNatively()640     public boolean canWriteBinaryNatively() {
641         return true;
642     }
643 
644     // 20-May-2020, tatu: This may or may not be enough -- ideally access is
645     //    via `DeserializationContext`, not parser, but if latter is needed
646     //    then we'll need to pass this from parser contents if which were
647     //    buffered.
648     @Override
getWriteCapabilities()649     public JacksonFeatureSet<StreamWriteCapability> getWriteCapabilities() {
650         return DEFAULT_WRITE_CAPABILITIES;
651     }
652 
653     /*
654     /**********************************************************
655     /* JsonGenerator implementation: low-level output handling
656     /**********************************************************
657      */
658 
659     @Override
flush()660     public void flush() throws IOException { /* NOP */ }
661 
662     @Override
close()663     public void close() throws IOException {
664         _closed = true;
665     }
666 
667     @Override
isClosed()668     public boolean isClosed() { return _closed; }
669 
670     /*
671     /**********************************************************
672     /* JsonGenerator implementation: write methods, structural
673     /**********************************************************
674      */
675 
676     @Override
writeStartArray()677     public final void writeStartArray() throws IOException
678     {
679         _writeContext.writeValue();
680         _appendStartMarker(JsonToken.START_ARRAY);
681         _writeContext = _writeContext.createChildArrayContext();
682     }
683 
684     @Override // since 2.10.1
writeStartArray(Object forValue)685     public void writeStartArray(Object forValue) throws IOException {
686         _writeContext.writeValue();
687         _appendStartMarker(JsonToken.START_ARRAY);
688         _writeContext = _writeContext.createChildArrayContext(forValue);
689     }
690 
691     @Override // since 2.10.1
writeStartArray(Object forValue, int size)692     public void writeStartArray(Object forValue, int size) throws IOException {
693         _writeContext.writeValue();
694         _appendStartMarker(JsonToken.START_ARRAY);
695         _writeContext = _writeContext.createChildArrayContext(forValue);
696     }
697 
698     @Override
writeEndArray()699     public final void writeEndArray() throws IOException
700     {
701         _appendEndMarker(JsonToken.END_ARRAY);
702         // Let's allow unbalanced tho... i.e. not run out of root level, ever
703         JsonWriteContext c = _writeContext.getParent();
704         if (c != null) {
705             _writeContext = c;
706         }
707     }
708 
709     @Override
writeStartObject()710     public final void writeStartObject() throws IOException
711     {
712         _writeContext.writeValue();
713         _appendStartMarker(JsonToken.START_OBJECT);
714         _writeContext = _writeContext.createChildObjectContext();
715     }
716 
717     @Override // since 2.8
writeStartObject(Object forValue)718     public void writeStartObject(Object forValue) throws IOException
719     {
720         _writeContext.writeValue();
721         _appendStartMarker(JsonToken.START_OBJECT);
722         JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
723         _writeContext = ctxt;
724     }
725 
726     @Override // since 2.10.1
writeStartObject(Object forValue, int size)727     public void writeStartObject(Object forValue, int size) throws IOException
728     {
729         _writeContext.writeValue();
730         _appendStartMarker(JsonToken.START_OBJECT);
731         JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
732         _writeContext = ctxt;
733     }
734 
735     @Override
writeEndObject()736     public final void writeEndObject() throws IOException
737     {
738         _appendEndMarker(JsonToken.END_OBJECT);
739         // Let's allow unbalanced tho... i.e. not run out of root level, ever
740         JsonWriteContext c = _writeContext.getParent();
741         if (c != null) {
742             _writeContext = c;
743         }
744     }
745 
746     @Override
writeFieldName(String name)747     public final void writeFieldName(String name) throws IOException
748     {
749         _writeContext.writeFieldName(name);
750         _appendFieldName(name);
751     }
752 
753     @Override
writeFieldName(SerializableString name)754     public void writeFieldName(SerializableString name) throws IOException
755     {
756         _writeContext.writeFieldName(name.getValue());
757         _appendFieldName(name);
758     }
759 
760     /*
761     /**********************************************************
762     /* JsonGenerator implementation: write methods, textual
763     /**********************************************************
764      */
765 
766     @Override
writeString(String text)767     public void writeString(String text) throws IOException {
768         if (text == null) {
769             writeNull();
770         } else {
771             _appendValue(JsonToken.VALUE_STRING, text);
772         }
773     }
774 
775     @Override
writeString(char[] text, int offset, int len)776     public void writeString(char[] text, int offset, int len) throws IOException {
777         writeString(new String(text, offset, len));
778     }
779 
780     @Override
writeString(SerializableString text)781     public void writeString(SerializableString text) throws IOException {
782         if (text == null) {
783             writeNull();
784         } else {
785             _appendValue(JsonToken.VALUE_STRING, text);
786         }
787     }
788 
789     @Override
writeRawUTF8String(byte[] text, int offset, int length)790     public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
791     {
792         // could add support for buffering if we really want it...
793         _reportUnsupportedOperation();
794     }
795 
796     @Override
writeUTF8String(byte[] text, int offset, int length)797     public void writeUTF8String(byte[] text, int offset, int length) throws IOException
798     {
799         // could add support for buffering if we really want it...
800         _reportUnsupportedOperation();
801     }
802 
803     @Override
writeRaw(String text)804     public void writeRaw(String text) throws IOException {
805         _reportUnsupportedOperation();
806     }
807 
808     @Override
writeRaw(String text, int offset, int len)809     public void writeRaw(String text, int offset, int len) throws IOException {
810         _reportUnsupportedOperation();
811     }
812 
813     @Override
writeRaw(SerializableString text)814     public void writeRaw(SerializableString text) throws IOException {
815         _reportUnsupportedOperation();
816     }
817 
818     @Override
writeRaw(char[] text, int offset, int len)819     public void writeRaw(char[] text, int offset, int len) throws IOException {
820         _reportUnsupportedOperation();
821     }
822 
823     @Override
writeRaw(char c)824     public void writeRaw(char c) throws IOException {
825         _reportUnsupportedOperation();
826     }
827 
828     @Override
writeRawValue(String text)829     public void writeRawValue(String text) throws IOException {
830         _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
831     }
832 
833     @Override
writeRawValue(String text, int offset, int len)834     public void writeRawValue(String text, int offset, int len) throws IOException {
835         if (offset > 0 || len != text.length()) {
836             text = text.substring(offset, offset+len);
837         }
838         _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
839     }
840 
841     @Override
writeRawValue(char[] text, int offset, int len)842     public void writeRawValue(char[] text, int offset, int len) throws IOException {
843         _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new String(text, offset, len));
844     }
845 
846     /*
847     /**********************************************************
848     /* JsonGenerator implementation: write methods, primitive types
849     /**********************************************************
850      */
851 
852     @Override
writeNumber(short i)853     public void writeNumber(short i) throws IOException {
854         _appendValue(JsonToken.VALUE_NUMBER_INT, Short.valueOf(i));
855     }
856 
857     @Override
writeNumber(int i)858     public void writeNumber(int i) throws IOException {
859         _appendValue(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i));
860     }
861 
862     @Override
writeNumber(long l)863     public void writeNumber(long l) throws IOException {
864         _appendValue(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l));
865     }
866 
867     @Override
writeNumber(double d)868     public void writeNumber(double d) throws IOException {
869         _appendValue(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d));
870     }
871 
872     @Override
writeNumber(float f)873     public void writeNumber(float f) throws IOException {
874         _appendValue(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f));
875     }
876 
877     @Override
writeNumber(BigDecimal dec)878     public void writeNumber(BigDecimal dec) throws IOException {
879         if (dec == null) {
880             writeNull();
881         } else {
882             _appendValue(JsonToken.VALUE_NUMBER_FLOAT, dec);
883         }
884     }
885 
886     @Override
writeNumber(BigInteger v)887     public void writeNumber(BigInteger v) throws IOException {
888         if (v == null) {
889             writeNull();
890         } else {
891             _appendValue(JsonToken.VALUE_NUMBER_INT, v);
892         }
893     }
894 
895     @Override
writeNumber(String encodedValue)896     public void writeNumber(String encodedValue) throws IOException {
897         /* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric
898          *   identity as long as possible
899          */
900         _appendValue(JsonToken.VALUE_NUMBER_FLOAT, encodedValue);
901     }
902 
903     @Override
writeBoolean(boolean state)904     public void writeBoolean(boolean state) throws IOException {
905         _appendValue(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE);
906     }
907 
908     @Override
writeNull()909     public void writeNull() throws IOException {
910         _appendValue(JsonToken.VALUE_NULL);
911     }
912 
913     /*
914     /***********************************************************
915     /* JsonGenerator implementation: write methods for POJOs/trees
916     /***********************************************************
917      */
918 
919     @Override
writeObject(Object value)920     public void writeObject(Object value) throws IOException
921     {
922         if (value == null) {
923             writeNull();
924             return;
925         }
926         Class<?> raw = value.getClass();
927         if (raw == byte[].class || (value instanceof RawValue)) {
928             _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value);
929             return;
930         }
931         if (_objectCodec == null) {
932             /* 28-May-2014, tatu: Tricky choice here; if no codec, should we
933              *   err out, or just embed? For now, do latter.
934              */
935 //          throw new JsonMappingException("No ObjectCodec configured for TokenBuffer, writeObject() called");
936             _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value);
937         } else {
938             _objectCodec.writeValue(this, value);
939         }
940     }
941 
942     @Override
writeTree(TreeNode node)943     public void writeTree(TreeNode node) throws IOException
944     {
945         if (node == null) {
946             writeNull();
947             return;
948         }
949 
950         if (_objectCodec == null) {
951             // as with 'writeObject()', is codec optional?
952             _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, node);
953         } else {
954             _objectCodec.writeTree(this, node);
955         }
956     }
957 
958     /*
959     /***********************************************************
960     /* JsonGenerator implementation; binary
961     /***********************************************************
962      */
963 
964     @Override
writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)965     public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException
966     {
967         /* 31-Dec-2009, tatu: can do this using multiple alternatives; but for
968          *   now, let's try to limit number of conversions.
969          *   The only (?) tricky thing is that of whether to preserve variant,
970          *   seems pointless, so let's not worry about it unless there's some
971          *   compelling reason to.
972          */
973         byte[] copy = new byte[len];
974         System.arraycopy(data, offset, copy, 0, len);
975         writeObject(copy);
976     }
977 
978     /**
979      * Although we could support this method, it does not necessarily make
980      * sense: we cannot make good use of streaming because buffer must
981      * hold all the data. Because of this, currently this will simply
982      * throw {@link UnsupportedOperationException}
983      */
984     @Override
writeBinary(Base64Variant b64variant, InputStream data, int dataLength)985     public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) {
986         throw new UnsupportedOperationException();
987     }
988 
989     /*
990     /***********************************************************
991     /* JsonGenerator implementation: native ids
992     /***********************************************************
993      */
994 
995     @Override
canWriteTypeId()996     public boolean canWriteTypeId() {
997         return _hasNativeTypeIds;
998     }
999 
1000     @Override
canWriteObjectId()1001     public boolean canWriteObjectId() {
1002         return _hasNativeObjectIds;
1003     }
1004 
1005     @Override
writeTypeId(Object id)1006     public void writeTypeId(Object id) {
1007         _typeId = id;
1008         _hasNativeId = true;
1009     }
1010 
1011     @Override
writeObjectId(Object id)1012     public void writeObjectId(Object id) {
1013         _objectId = id;
1014         _hasNativeId = true;
1015     }
1016 
1017     @Override // since 2.8
writeEmbeddedObject(Object object)1018     public void writeEmbeddedObject(Object object) throws IOException {
1019         _appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, object);
1020     }
1021 
1022     /*
1023     /**********************************************************
1024     /* JsonGenerator implementation; pass-through copy
1025     /**********************************************************
1026      */
1027 
1028     @Override
copyCurrentEvent(JsonParser p)1029     public void copyCurrentEvent(JsonParser p) throws IOException
1030     {
1031         if (_mayHaveNativeIds) {
1032             _checkNativeIds(p);
1033         }
1034         switch (p.currentToken()) {
1035         case START_OBJECT:
1036             writeStartObject();
1037             break;
1038         case END_OBJECT:
1039             writeEndObject();
1040             break;
1041         case START_ARRAY:
1042             writeStartArray();
1043             break;
1044         case END_ARRAY:
1045             writeEndArray();
1046             break;
1047         case FIELD_NAME:
1048             writeFieldName(p.currentName());
1049             break;
1050         case VALUE_STRING:
1051             if (p.hasTextCharacters()) {
1052                 writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength());
1053             } else {
1054                 writeString(p.getText());
1055             }
1056             break;
1057         case VALUE_NUMBER_INT:
1058             switch (p.getNumberType()) {
1059             case INT:
1060                 writeNumber(p.getIntValue());
1061                 break;
1062             case BIG_INTEGER:
1063                 writeNumber(p.getBigIntegerValue());
1064                 break;
1065             default:
1066                 writeNumber(p.getLongValue());
1067             }
1068             break;
1069         case VALUE_NUMBER_FLOAT:
1070             if (_forceBigDecimal) {
1071                 // 10-Oct-2015, tatu: Ideally we would first determine whether underlying
1072                 //   number is already decoded into a number (in which case might as well
1073                 //   access as number); or is still retained as text (in which case we
1074                 //   should further defer decoding that may not need BigDecimal):
1075                 writeNumber(p.getDecimalValue());
1076             } else {
1077                 switch (p.getNumberType()) {
1078                 case BIG_DECIMAL:
1079                     writeNumber(p.getDecimalValue());
1080                     break;
1081                 case FLOAT:
1082                     writeNumber(p.getFloatValue());
1083                     break;
1084                 default:
1085                     writeNumber(p.getDoubleValue());
1086                 }
1087             }
1088             break;
1089         case VALUE_TRUE:
1090             writeBoolean(true);
1091             break;
1092         case VALUE_FALSE:
1093             writeBoolean(false);
1094             break;
1095         case VALUE_NULL:
1096             writeNull();
1097             break;
1098         case VALUE_EMBEDDED_OBJECT:
1099             writeObject(p.getEmbeddedObject());
1100             break;
1101         default:
1102             throw new RuntimeException("Internal error: unexpected token: "+p.currentToken());
1103         }
1104     }
1105 
1106     @Override
copyCurrentStructure(JsonParser p)1107     public void copyCurrentStructure(JsonParser p) throws IOException
1108     {
1109         JsonToken t = p.currentToken();
1110 
1111         // Let's handle field-name separately first
1112         if (t == JsonToken.FIELD_NAME) {
1113             if (_mayHaveNativeIds) {
1114                 _checkNativeIds(p);
1115             }
1116             writeFieldName(p.currentName());
1117             t = p.nextToken();
1118             // fall-through to copy the associated value
1119         } else if (t == null) {
1120             throw new IllegalStateException("No token available from argument `JsonParser`");
1121         }
1122 
1123         // We'll do minor handling here to separate structured, scalar values,
1124         // then delegate appropriately.
1125         // Plus also deal with oddity of "dangling" END_OBJECT/END_ARRAY
1126         switch (t) {
1127         case START_ARRAY:
1128             if (_mayHaveNativeIds) {
1129                 _checkNativeIds(p);
1130             }
1131             writeStartArray();
1132             _copyBufferContents(p);
1133             break;
1134         case START_OBJECT:
1135             if (_mayHaveNativeIds) {
1136                 _checkNativeIds(p);
1137             }
1138             writeStartObject();
1139             _copyBufferContents(p);
1140             break;
1141         case END_ARRAY:
1142             writeEndArray();
1143             break;
1144         case END_OBJECT:
1145             writeEndObject();
1146             break;
1147         default: // others are simple:
1148             _copyBufferValue(p, t);
1149         }
1150     }
1151 
_copyBufferContents(JsonParser p)1152     protected void _copyBufferContents(JsonParser p) throws IOException
1153     {
1154         int depth = 1;
1155         JsonToken t;
1156 
1157         while ((t = p.nextToken()) != null) {
1158             switch (t) {
1159             case FIELD_NAME:
1160                 if (_mayHaveNativeIds) {
1161                     _checkNativeIds(p);
1162                 }
1163                 writeFieldName(p.currentName());
1164                 break;
1165 
1166             case START_ARRAY:
1167                 if (_mayHaveNativeIds) {
1168                     _checkNativeIds(p);
1169                 }
1170                 writeStartArray();
1171                 ++depth;
1172                 break;
1173 
1174             case START_OBJECT:
1175                 if (_mayHaveNativeIds) {
1176                     _checkNativeIds(p);
1177                 }
1178                 writeStartObject();
1179                 ++depth;
1180                 break;
1181 
1182             case END_ARRAY:
1183                 writeEndArray();
1184                 if (--depth == 0) {
1185                     return;
1186                 }
1187                 break;
1188             case END_OBJECT:
1189                 writeEndObject();
1190                 if (--depth == 0) {
1191                     return;
1192                 }
1193                 break;
1194 
1195             default:
1196                 _copyBufferValue(p, t);
1197             }
1198         }
1199     }
1200 
1201     // NOTE: Copied from earlier `copyCurrentEvent()`
_copyBufferValue(JsonParser p, JsonToken t)1202     private void _copyBufferValue(JsonParser p, JsonToken t) throws IOException
1203     {
1204         if (_mayHaveNativeIds) {
1205             _checkNativeIds(p);
1206         }
1207         switch (t) {
1208         case VALUE_STRING:
1209             if (p.hasTextCharacters()) {
1210                 writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength());
1211             } else {
1212                 writeString(p.getText());
1213             }
1214             break;
1215         case VALUE_NUMBER_INT:
1216             switch (p.getNumberType()) {
1217             case INT:
1218                 writeNumber(p.getIntValue());
1219                 break;
1220             case BIG_INTEGER:
1221                 writeNumber(p.getBigIntegerValue());
1222                 break;
1223             default:
1224                 writeNumber(p.getLongValue());
1225             }
1226             break;
1227         case VALUE_NUMBER_FLOAT:
1228             if (_forceBigDecimal) {
1229                 writeNumber(p.getDecimalValue());
1230             } else {
1231                 // 09-Jul-2020, tatu: Used to just copy using most optimal method, but
1232                 //  issues like [databind#2644] force to use exact, not optimal type
1233                 final Number n = p.getNumberValueExact();
1234                 _appendValue(JsonToken.VALUE_NUMBER_FLOAT, n);
1235             }
1236             break;
1237         case VALUE_TRUE:
1238             writeBoolean(true);
1239             break;
1240         case VALUE_FALSE:
1241             writeBoolean(false);
1242             break;
1243         case VALUE_NULL:
1244             writeNull();
1245             break;
1246         case VALUE_EMBEDDED_OBJECT:
1247             writeObject(p.getEmbeddedObject());
1248             break;
1249         default:
1250             throw new RuntimeException("Internal error: unexpected token: "+t);
1251         }
1252     }
1253 
_checkNativeIds(JsonParser p)1254     private final void _checkNativeIds(JsonParser p) throws IOException
1255     {
1256         if ((_typeId = p.getTypeId()) != null) {
1257             _hasNativeId = true;
1258         }
1259         if ((_objectId = p.getObjectId()) != null) {
1260             _hasNativeId = true;
1261         }
1262     }
1263 
1264     /*
1265     /**********************************************************
1266     /* Internal methods
1267     /**********************************************************
1268      */
1269 
1270     /*// Not used in / since 2.10
1271     protected final void _append(JsonToken type)
1272     {
1273         Segment next;
1274 
1275         if (_hasNativeId) {
1276             next =_last.append(_appendAt, type, _objectId, _typeId);
1277         } else {
1278             next =  _last.append(_appendAt, type);
1279         }
1280         if (next == null) {
1281             ++_appendAt;
1282         } else {
1283             _last = next;
1284             _appendAt = 1; // since we added first at 0
1285         }
1286     }
1287 
1288     protected final void _append(JsonToken type, Object value)
1289     {
1290         Segment next;
1291         if (_hasNativeId) {
1292             next =  _last.append(_appendAt, type, value, _objectId, _typeId);
1293         } else {
1294             next = _last.append(_appendAt, type, value);
1295         }
1296         if (next == null) {
1297             ++_appendAt;
1298         } else {
1299             _last = next;
1300             _appendAt = 1;
1301         }
1302     }
1303     */
1304 
1305     /**
1306      * Method used for appending token known to represent a "simple" scalar
1307      * value where token is the only information
1308      *
1309      * @since 2.6.4
1310      */
_appendValue(JsonToken type)1311     protected final void _appendValue(JsonToken type)
1312     {
1313         _writeContext.writeValue();
1314         Segment next;
1315         if (_hasNativeId) {
1316             next = _last.append(_appendAt, type, _objectId, _typeId);
1317         } else {
1318             next = _last.append(_appendAt, type);
1319         }
1320         if (next == null) {
1321             ++_appendAt;
1322         } else {
1323             _last = next;
1324             _appendAt = 1; // since we added first at 0
1325         }
1326     }
1327 
1328     /**
1329      * Method used for appending token known to represent a scalar value
1330      * where there is additional content (text, number) beyond type token
1331      *
1332      * @since 2.6.4
1333      */
_appendValue(JsonToken type, Object value)1334     protected final void _appendValue(JsonToken type, Object value)
1335     {
1336         _writeContext.writeValue();
1337         Segment next;
1338         if (_hasNativeId) {
1339             next = _last.append(_appendAt, type, value, _objectId, _typeId);
1340         } else {
1341             next = _last.append(_appendAt, type, value);
1342         }
1343         if (next == null) {
1344             ++_appendAt;
1345         } else {
1346             _last = next;
1347             _appendAt = 1;
1348         }
1349     }
1350 
1351     /**
1352      * Specialized method used for appending a field name, appending either
1353      * {@link String} or {@link SerializableString}.
1354      *
1355      * @since 2.10
1356      */
_appendFieldName(Object value)1357     protected final void _appendFieldName(Object value)
1358     {
1359         // NOTE: do NOT clear _objectId / _typeId
1360         Segment next;
1361         if (_hasNativeId) {
1362             next =  _last.append(_appendAt, JsonToken.FIELD_NAME, value, _objectId, _typeId);
1363         } else {
1364             next = _last.append(_appendAt, JsonToken.FIELD_NAME, value);
1365         }
1366         if (next == null) {
1367             ++_appendAt;
1368         } else {
1369             _last = next;
1370             _appendAt = 1;
1371         }
1372     }
1373 
1374     /**
1375      * Specialized method used for appending a structural start Object/Array marker
1376      *
1377      * @since 2.10
1378      */
_appendStartMarker(JsonToken type)1379     protected final void _appendStartMarker(JsonToken type)
1380     {
1381         Segment next;
1382         if (_hasNativeId) {
1383             next =_last.append(_appendAt, type, _objectId, _typeId);
1384         } else {
1385             next =  _last.append(_appendAt, type);
1386         }
1387         if (next == null) {
1388             ++_appendAt;
1389         } else {
1390             _last = next;
1391             _appendAt = 1; // since we added first at 0
1392         }
1393     }
1394 
1395     /**
1396      * Specialized method used for appending a structural end Object/Array marker
1397      *
1398      * @since 2.10
1399      */
_appendEndMarker(JsonToken type)1400     protected final void _appendEndMarker(JsonToken type)
1401     {
1402         // NOTE: type/object id not relevant
1403         Segment next = _last.append(_appendAt, type);
1404         if (next == null) {
1405             ++_appendAt;
1406         } else {
1407             _last = next;
1408             _appendAt = 1;
1409         }
1410     }
1411 
1412     @Override
_reportUnsupportedOperation()1413     protected void _reportUnsupportedOperation() {
1414         throw new UnsupportedOperationException("Called operation not supported for TokenBuffer");
1415     }
1416 
1417     /*
1418     /**********************************************************
1419     /* Supporting classes
1420     /**********************************************************
1421      */
1422 
1423     protected final static class Parser
1424         extends ParserMinimalBase
1425     {
1426         /*
1427         /**********************************************************
1428         /* Configuration
1429         /**********************************************************
1430          */
1431 
1432         protected ObjectCodec _codec;
1433 
1434         /**
1435          * @since 2.3
1436          */
1437         protected final boolean _hasNativeTypeIds;
1438 
1439         /**
1440          * @since 2.3
1441          */
1442         protected final boolean _hasNativeObjectIds;
1443 
1444         protected final boolean _hasNativeIds;
1445 
1446         /*
1447         /**********************************************************
1448         /* Parsing state
1449         /**********************************************************
1450          */
1451 
1452         /**
1453          * Currently active segment
1454          */
1455         protected Segment _segment;
1456 
1457         /**
1458          * Pointer to current token within current segment
1459          */
1460         protected int _segmentPtr;
1461 
1462         /**
1463          * Information about parser context, context in which
1464          * the next token is to be parsed (root, array, object).
1465          */
1466         protected TokenBufferReadContext _parsingContext;
1467 
1468         protected boolean _closed;
1469 
1470         protected transient ByteArrayBuilder _byteBuilder;
1471 
1472         protected JsonLocation _location = null;
1473 
1474         /*
1475         /**********************************************************
1476         /* Construction, init
1477         /**********************************************************
1478          */
1479 
1480         @Deprecated // since 2.9
Parser(Segment firstSeg, ObjectCodec codec, boolean hasNativeTypeIds, boolean hasNativeObjectIds)1481         public Parser(Segment firstSeg, ObjectCodec codec,
1482                 boolean hasNativeTypeIds, boolean hasNativeObjectIds)
1483         {
1484             this(firstSeg, codec, hasNativeTypeIds, hasNativeObjectIds, null);
1485         }
1486 
Parser(Segment firstSeg, ObjectCodec codec, boolean hasNativeTypeIds, boolean hasNativeObjectIds, JsonStreamContext parentContext)1487         public Parser(Segment firstSeg, ObjectCodec codec,
1488                 boolean hasNativeTypeIds, boolean hasNativeObjectIds,
1489                 JsonStreamContext parentContext)
1490         {
1491             super(0);
1492             _segment = firstSeg;
1493             _segmentPtr = -1; // not yet read
1494             _codec = codec;
1495             _parsingContext = TokenBufferReadContext.createRootContext(parentContext);
1496             _hasNativeTypeIds = hasNativeTypeIds;
1497             _hasNativeObjectIds = hasNativeObjectIds;
1498             _hasNativeIds = (hasNativeTypeIds | hasNativeObjectIds);
1499         }
1500 
setLocation(JsonLocation l)1501         public void setLocation(JsonLocation l) {
1502             _location = l;
1503         }
1504 
1505         @Override
getCodec()1506         public ObjectCodec getCodec() { return _codec; }
1507 
1508         @Override
setCodec(ObjectCodec c)1509         public void setCodec(ObjectCodec c) { _codec = c; }
1510 
1511         /*
1512         /**********************************************************
1513         /* Public API, config access, capability introspection
1514         /**********************************************************
1515          */
1516 
1517         @Override
version()1518         public Version version() {
1519             return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
1520         }
1521 
1522         // 20-May-2020, tatu: This may or may not be enough -- ideally access is
1523         //    via `DeserializationContext`, not parser, but if latter is needed
1524         //    then we'll need to pass this from parser contents if which were
1525         //    buffered.
1526         @Override
getReadCapabilities()1527         public JacksonFeatureSet<StreamReadCapability> getReadCapabilities() {
1528             return DEFAULT_READ_CAPABILITIES;
1529         }
1530 
1531         /*
1532         /**********************************************************
1533         /* Extended API beyond JsonParser
1534         /**********************************************************
1535          */
1536 
peekNextToken()1537         public JsonToken peekNextToken() throws IOException
1538         {
1539             // closed? nothing more to peek, either
1540             if (_closed) return null;
1541             Segment seg = _segment;
1542             int ptr = _segmentPtr+1;
1543             if (ptr >= Segment.TOKENS_PER_SEGMENT) {
1544                 ptr = 0;
1545                 seg = (seg == null) ? null : seg.next();
1546             }
1547             return (seg == null) ? null : seg.type(ptr);
1548         }
1549 
1550         /*
1551         /**********************************************************
1552         /* Closeable implementation
1553         /**********************************************************
1554          */
1555 
1556         @Override
close()1557         public void close() throws IOException {
1558             if (!_closed) {
1559                 _closed = true;
1560             }
1561         }
1562 
1563         /*
1564         /**********************************************************
1565         /* Public API, traversal
1566         /**********************************************************
1567          */
1568 
1569         @Override
nextToken()1570         public JsonToken nextToken() throws IOException
1571         {
1572             // If we are closed, nothing more to do
1573             if (_closed || (_segment == null)) return null;
1574 
1575             // Ok, then: any more tokens?
1576             if (++_segmentPtr >= Segment.TOKENS_PER_SEGMENT) {
1577                 _segmentPtr = 0;
1578                 _segment = _segment.next();
1579                 if (_segment == null) {
1580                     return null;
1581                 }
1582             }
1583             _currToken = _segment.type(_segmentPtr);
1584             // Field name? Need to update context
1585             if (_currToken == JsonToken.FIELD_NAME) {
1586                 Object ob = _currentObject();
1587                 String name = (ob instanceof String) ? ((String) ob) : ob.toString();
1588                 _parsingContext.setCurrentName(name);
1589             } else if (_currToken == JsonToken.START_OBJECT) {
1590                 _parsingContext = _parsingContext.createChildObjectContext();
1591             } else if (_currToken == JsonToken.START_ARRAY) {
1592                 _parsingContext = _parsingContext.createChildArrayContext();
1593             } else if (_currToken == JsonToken.END_OBJECT
1594                     || _currToken == JsonToken.END_ARRAY) {
1595                 // Closing JSON Object/Array? Close matching context
1596                 _parsingContext = _parsingContext.parentOrCopy();
1597             } else {
1598                 _parsingContext.updateForValue();
1599             }
1600             return _currToken;
1601         }
1602 
1603         @Override
nextFieldName()1604         public String nextFieldName() throws IOException
1605         {
1606             // inlined common case from nextToken()
1607             if (_closed || (_segment == null)) {
1608                 return null;
1609             }
1610 
1611             int ptr = _segmentPtr+1;
1612             if ((ptr < Segment.TOKENS_PER_SEGMENT) && (_segment.type(ptr) == JsonToken.FIELD_NAME)) {
1613                 _segmentPtr = ptr;
1614                 _currToken = JsonToken.FIELD_NAME;
1615                 Object ob = _segment.get(ptr); // inlined _currentObject();
1616                 String name = (ob instanceof String) ? ((String) ob) : ob.toString();
1617                 _parsingContext.setCurrentName(name);
1618                 return name;
1619             }
1620             return (nextToken() == JsonToken.FIELD_NAME) ? currentName() : null;
1621         }
1622 
1623         @Override
isClosed()1624         public boolean isClosed() { return _closed; }
1625 
1626         /*
1627         /**********************************************************
1628         /* Public API, token accessors
1629         /**********************************************************
1630          */
1631 
1632         @Override
getParsingContext()1633         public JsonStreamContext getParsingContext() { return _parsingContext; }
1634 
1635         @Override
getTokenLocation()1636         public JsonLocation getTokenLocation() { return getCurrentLocation(); }
1637 
1638         @Override
getCurrentLocation()1639         public JsonLocation getCurrentLocation() {
1640             return (_location == null) ? JsonLocation.NA : _location;
1641         }
1642 
1643         @Override
currentName()1644         public String currentName() {
1645             // 25-Jun-2015, tatu: as per [databind#838], needs to be same as ParserBase
1646             if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
1647                 JsonStreamContext parent = _parsingContext.getParent();
1648                 return parent.getCurrentName();
1649             }
1650             return _parsingContext.getCurrentName();
1651         }
1652 
1653         @Override // since 2.12 delegate to the new method
getCurrentName()1654         public String getCurrentName() { return currentName(); }
1655 
1656         @Override
overrideCurrentName(String name)1657         public void overrideCurrentName(String name)
1658         {
1659             // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing:
1660             JsonStreamContext ctxt = _parsingContext;
1661             if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
1662                 ctxt = ctxt.getParent();
1663             }
1664             if (ctxt instanceof TokenBufferReadContext) {
1665                 try {
1666                     ((TokenBufferReadContext) ctxt).setCurrentName(name);
1667                 } catch (IOException e) {
1668                     throw new RuntimeException(e);
1669                 }
1670             }
1671         }
1672 
1673         /*
1674         /**********************************************************
1675         /* Public API, access to token information, text
1676         /**********************************************************
1677          */
1678 
1679         @Override
getText()1680         public String getText()
1681         {
1682             // common cases first:
1683             if (_currToken == JsonToken.VALUE_STRING
1684                     || _currToken == JsonToken.FIELD_NAME) {
1685                 Object ob = _currentObject();
1686                 if (ob instanceof String) {
1687                     return (String) ob;
1688                 }
1689                 return ClassUtil.nullOrToString(ob);
1690             }
1691             if (_currToken == null) {
1692                 return null;
1693             }
1694             switch (_currToken) {
1695             case VALUE_NUMBER_INT:
1696             case VALUE_NUMBER_FLOAT:
1697                 return ClassUtil.nullOrToString(_currentObject());
1698             default:
1699             	return _currToken.asString();
1700             }
1701         }
1702 
1703         @Override
getTextCharacters()1704         public char[] getTextCharacters() {
1705             String str = getText();
1706             return (str == null) ? null : str.toCharArray();
1707         }
1708 
1709         @Override
getTextLength()1710         public int getTextLength() {
1711             String str = getText();
1712             return (str == null) ? 0 : str.length();
1713         }
1714 
1715         @Override
getTextOffset()1716         public int getTextOffset() { return 0; }
1717 
1718         @Override
hasTextCharacters()1719         public boolean hasTextCharacters() {
1720             // We never have raw buffer available, so:
1721             return false;
1722         }
1723 
1724         /*
1725         /**********************************************************
1726         /* Public API, access to token information, numeric
1727         /**********************************************************
1728          */
1729 
1730         @Override
isNaN()1731         public boolean isNaN() {
1732             // can only occur for floating-point numbers
1733             if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
1734                 Object value = _currentObject();
1735                 if (value instanceof Double) {
1736                     Double v = (Double) value;
1737                     return v.isNaN() || v.isInfinite();
1738                 }
1739                 if (value instanceof Float) {
1740                     Float v = (Float) value;
1741                     return v.isNaN() || v.isInfinite();
1742                 }
1743             }
1744             return false;
1745         }
1746 
1747         @Override
getBigIntegerValue()1748         public BigInteger getBigIntegerValue() throws IOException
1749         {
1750             Number n = getNumberValue();
1751             if (n instanceof BigInteger) {
1752                 return (BigInteger) n;
1753             }
1754             if (getNumberType() == NumberType.BIG_DECIMAL) {
1755                 return ((BigDecimal) n).toBigInteger();
1756             }
1757             // int/long is simple, but let's also just truncate float/double:
1758             return BigInteger.valueOf(n.longValue());
1759         }
1760 
1761         @Override
getDecimalValue()1762         public BigDecimal getDecimalValue() throws IOException
1763         {
1764             Number n = getNumberValue();
1765             if (n instanceof BigDecimal) {
1766                 return (BigDecimal) n;
1767             }
1768             switch (getNumberType()) {
1769             case INT:
1770             case LONG:
1771                 return BigDecimal.valueOf(n.longValue());
1772             case BIG_INTEGER:
1773                 return new BigDecimal((BigInteger) n);
1774             default:
1775             }
1776             // float or double
1777             return BigDecimal.valueOf(n.doubleValue());
1778         }
1779 
1780         @Override
getDoubleValue()1781         public double getDoubleValue() throws IOException {
1782             return getNumberValue().doubleValue();
1783         }
1784 
1785         @Override
getFloatValue()1786         public float getFloatValue() throws IOException {
1787             return getNumberValue().floatValue();
1788         }
1789 
1790         @Override
getIntValue()1791         public int getIntValue() throws IOException
1792         {
1793             Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
1794                     ((Number) _currentObject()) : getNumberValue();
1795             if ((n instanceof Integer) || _smallerThanInt(n)) {
1796                 return n.intValue();
1797             }
1798             return _convertNumberToInt(n);
1799         }
1800 
1801         @Override
getLongValue()1802         public long getLongValue() throws IOException {
1803             Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
1804                     ((Number) _currentObject()) : getNumberValue();
1805             if ((n instanceof Long) || _smallerThanLong(n)) {
1806                 return n.longValue();
1807             }
1808             return _convertNumberToLong(n);
1809         }
1810 
1811         @Override
getNumberType()1812         public NumberType getNumberType() throws IOException
1813         {
1814             Number n = getNumberValue();
1815             if (n instanceof Integer) return NumberType.INT;
1816             if (n instanceof Long) return NumberType.LONG;
1817             if (n instanceof Double) return NumberType.DOUBLE;
1818             if (n instanceof BigDecimal) return NumberType.BIG_DECIMAL;
1819             if (n instanceof BigInteger) return NumberType.BIG_INTEGER;
1820             if (n instanceof Float) return NumberType.FLOAT;
1821             if (n instanceof Short) return NumberType.INT;       // should be SHORT
1822             return null;
1823         }
1824 
1825         @Override
getNumberValue()1826         public final Number getNumberValue() throws IOException {
1827             _checkIsNumber();
1828             Object value = _currentObject();
1829             if (value instanceof Number) {
1830                 return (Number) value;
1831             }
1832             // Difficult to really support numbers-as-Strings; but let's try.
1833             // NOTE: no access to DeserializationConfig, unfortunately, so cannot
1834             // try to determine Double/BigDecimal preference...
1835             if (value instanceof String) {
1836                 String str = (String) value;
1837                 if (str.indexOf('.') >= 0) {
1838                     return Double.parseDouble(str);
1839                 }
1840                 return Long.parseLong(str);
1841             }
1842             if (value == null) {
1843                 return null;
1844             }
1845             throw new IllegalStateException("Internal error: entry should be a Number, but is of type "
1846                     +value.getClass().getName());
1847         }
1848 
_smallerThanInt(Number n)1849         private final boolean _smallerThanInt(Number n) {
1850             return (n instanceof Short) || (n instanceof Byte);
1851         }
1852 
_smallerThanLong(Number n)1853         private final boolean _smallerThanLong(Number n) {
1854             return (n instanceof Integer) || (n instanceof Short) || (n instanceof Byte);
1855         }
1856 
1857         // 02-Jan-2017, tatu: Modified from method(s) in `ParserBase`
1858 
_convertNumberToInt(Number n)1859         protected int _convertNumberToInt(Number n) throws IOException
1860         {
1861             if (n instanceof Long) {
1862                 long l = n.longValue();
1863                 int result = (int) l;
1864                 if (((long) result) != l) {
1865                     reportOverflowInt();
1866                 }
1867                 return result;
1868             }
1869             if (n instanceof BigInteger) {
1870                 BigInteger big = (BigInteger) n;
1871                 if (BI_MIN_INT.compareTo(big) > 0
1872                         || BI_MAX_INT.compareTo(big) < 0) {
1873                     reportOverflowInt();
1874                 }
1875             } else if ((n instanceof Double) || (n instanceof Float)) {
1876                 double d = n.doubleValue();
1877                 // Need to check boundaries
1878                 if (d < MIN_INT_D || d > MAX_INT_D) {
1879                     reportOverflowInt();
1880                 }
1881                 return (int) d;
1882             } else if (n instanceof BigDecimal) {
1883                 BigDecimal big = (BigDecimal) n;
1884                 if (BD_MIN_INT.compareTo(big) > 0
1885                     || BD_MAX_INT.compareTo(big) < 0) {
1886                     reportOverflowInt();
1887                 }
1888             } else {
1889                 _throwInternal();
1890             }
1891             return n.intValue();
1892         }
1893 
_convertNumberToLong(Number n)1894         protected long _convertNumberToLong(Number n) throws IOException
1895         {
1896             if (n instanceof BigInteger) {
1897                 BigInteger big = (BigInteger) n;
1898                 if (BI_MIN_LONG.compareTo(big) > 0
1899                         || BI_MAX_LONG.compareTo(big) < 0) {
1900                     reportOverflowLong();
1901                 }
1902             } else if ((n instanceof Double) || (n instanceof Float)) {
1903                 double d = n.doubleValue();
1904                 // Need to check boundaries
1905                 if (d < MIN_LONG_D || d > MAX_LONG_D) {
1906                     reportOverflowLong();
1907                 }
1908                 return (long) d;
1909             } else if (n instanceof BigDecimal) {
1910                 BigDecimal big = (BigDecimal) n;
1911                 if (BD_MIN_LONG.compareTo(big) > 0
1912                     || BD_MAX_LONG.compareTo(big) < 0) {
1913                     reportOverflowLong();
1914                 }
1915             } else {
1916                 _throwInternal();
1917             }
1918             return n.longValue();
1919         }
1920 
1921         /*
1922         /**********************************************************
1923         /* Public API, access to token information, other
1924         /**********************************************************
1925          */
1926 
1927         @Override
getEmbeddedObject()1928         public Object getEmbeddedObject()
1929         {
1930             if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
1931                 return _currentObject();
1932             }
1933             return null;
1934         }
1935 
1936         @Override
1937         @SuppressWarnings("resource")
getBinaryValue(Base64Variant b64variant)1938         public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException
1939         {
1940             // First: maybe we some special types?
1941             if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
1942                 // Embedded byte array would work nicely...
1943                 Object ob = _currentObject();
1944                 if (ob instanceof byte[]) {
1945                     return (byte[]) ob;
1946                 }
1947                 // fall through to error case
1948             }
1949             if (_currToken != JsonToken.VALUE_STRING) {
1950                 throw _constructError("Current token ("+_currToken+") not VALUE_STRING (or VALUE_EMBEDDED_OBJECT with byte[]), cannot access as binary");
1951             }
1952             final String str = getText();
1953             if (str == null) {
1954                 return null;
1955             }
1956             ByteArrayBuilder builder = _byteBuilder;
1957             if (builder == null) {
1958                 _byteBuilder = builder = new ByteArrayBuilder(100);
1959             } else {
1960                 _byteBuilder.reset();
1961             }
1962             _decodeBase64(str, builder, b64variant);
1963             return builder.toByteArray();
1964         }
1965 
1966         @Override
readBinaryValue(Base64Variant b64variant, OutputStream out)1967         public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
1968         {
1969             byte[] data = getBinaryValue(b64variant);
1970             if (data != null) {
1971                 out.write(data, 0, data.length);
1972                 return data.length;
1973             }
1974             return 0;
1975         }
1976 
1977         /*
1978         /**********************************************************
1979         /* Public API, native ids
1980         /**********************************************************
1981          */
1982 
1983         @Override
canReadObjectId()1984         public boolean canReadObjectId() {
1985             return _hasNativeObjectIds;
1986         }
1987 
1988         @Override
canReadTypeId()1989         public boolean canReadTypeId() {
1990             return _hasNativeTypeIds;
1991         }
1992 
1993         @Override
getTypeId()1994         public Object getTypeId() {
1995             return _segment.findTypeId(_segmentPtr);
1996         }
1997 
1998         @Override
getObjectId()1999         public Object getObjectId() {
2000             return _segment.findObjectId(_segmentPtr);
2001         }
2002 
2003         /*
2004         /**********************************************************
2005         /* Internal methods
2006         /**********************************************************
2007          */
2008 
_currentObject()2009         protected final Object _currentObject() {
2010             return _segment.get(_segmentPtr);
2011         }
2012 
_checkIsNumber()2013         protected final void _checkIsNumber() throws JsonParseException
2014         {
2015             if (_currToken == null || !_currToken.isNumeric()) {
2016                 throw _constructError("Current token ("+_currToken+") not numeric, cannot use numeric value accessors");
2017             }
2018         }
2019 
2020         @Override
_handleEOF()2021         protected void _handleEOF() throws JsonParseException {
2022             _throwInternal();
2023         }
2024     }
2025 
2026     /**
2027      * Individual segment of TokenBuffer that can store up to 16 tokens
2028      * (limited by 4 bits per token type marker requirement).
2029      * Current implementation uses fixed length array; could alternatively
2030      * use 16 distinct fields and switch statement (slightly more efficient
2031      * storage, slightly slower access)
2032      */
2033     protected final static class Segment
2034     {
2035         public final static int TOKENS_PER_SEGMENT = 16;
2036 
2037         /**
2038          * Static array used for fast conversion between token markers and
2039          * matching {@link JsonToken} instances
2040          */
2041         private final static JsonToken[] TOKEN_TYPES_BY_INDEX;
2042         static {
2043             // ... here we know that there are <= 15 values in JsonToken enum
2044             TOKEN_TYPES_BY_INDEX = new JsonToken[16];
2045             JsonToken[] t = JsonToken.values();
2046             // and reserve entry 0 for "not available"
System.arraycopy(t, 1, TOKEN_TYPES_BY_INDEX, 1, Math.min(15, t.length - 1))2047             System.arraycopy(t, 1, TOKEN_TYPES_BY_INDEX, 1, Math.min(15, t.length - 1));
2048         }
2049 
2050         // // // Linking
2051 
2052         protected Segment _next;
2053 
2054         // // // State
2055 
2056         /**
2057          * Bit field used to store types of buffered tokens; 4 bits per token.
2058          * Value 0 is reserved for "not in use"
2059          */
2060         protected long _tokenTypes;
2061 
2062 
2063         // Actual tokens
2064 
2065         protected final Object[] _tokens = new Object[TOKENS_PER_SEGMENT];
2066 
2067         /**
2068          * Lazily constructed Map for storing native type and object ids, if any
2069          */
2070         protected TreeMap<Integer,Object> _nativeIds;
2071 
Segment()2072         public Segment() { }
2073 
2074         // // // Accessors
2075 
type(int index)2076         public JsonToken type(int index)
2077         {
2078             long l = _tokenTypes;
2079             if (index > 0) {
2080                 l >>= (index << 2);
2081             }
2082             int ix = ((int) l) & 0xF;
2083             return TOKEN_TYPES_BY_INDEX[ix];
2084         }
2085 
rawType(int index)2086         public int rawType(int index)
2087         {
2088             long l = _tokenTypes;
2089             if (index > 0) {
2090                 l >>= (index << 2);
2091             }
2092             int ix = ((int) l) & 0xF;
2093             return ix;
2094         }
2095 
get(int index)2096         public Object get(int index) {
2097             return _tokens[index];
2098         }
2099 
next()2100         public Segment next() { return _next; }
2101 
2102         /**
2103          * Accessor for checking whether this segment may have native
2104          * type or object ids.
2105          */
hasIds()2106         public boolean hasIds() {
2107             return _nativeIds != null;
2108         }
2109 
2110         // // // Mutators
2111 
append(int index, JsonToken tokenType)2112         public Segment append(int index, JsonToken tokenType)
2113         {
2114             if (index < TOKENS_PER_SEGMENT) {
2115                 set(index, tokenType);
2116                 return null;
2117             }
2118             _next = new Segment();
2119             _next.set(0, tokenType);
2120             return _next;
2121         }
2122 
append(int index, JsonToken tokenType, Object objectId, Object typeId)2123         public Segment append(int index, JsonToken tokenType,
2124                 Object objectId, Object typeId)
2125         {
2126             if (index < TOKENS_PER_SEGMENT) {
2127                 set(index, tokenType, objectId, typeId);
2128                 return null;
2129             }
2130             _next = new Segment();
2131             _next.set(0, tokenType, objectId, typeId);
2132             return _next;
2133         }
2134 
append(int index, JsonToken tokenType, Object value)2135         public Segment append(int index, JsonToken tokenType, Object value)
2136         {
2137             if (index < TOKENS_PER_SEGMENT) {
2138                 set(index, tokenType, value);
2139                 return null;
2140             }
2141             _next = new Segment();
2142             _next.set(0, tokenType, value);
2143             return _next;
2144         }
2145 
append(int index, JsonToken tokenType, Object value, Object objectId, Object typeId)2146         public Segment append(int index, JsonToken tokenType, Object value,
2147                 Object objectId, Object typeId)
2148         {
2149             if (index < TOKENS_PER_SEGMENT) {
2150                 set(index, tokenType, value, objectId, typeId);
2151                 return null;
2152             }
2153             _next = new Segment();
2154             _next.set(0, tokenType, value, objectId, typeId);
2155             return _next;
2156         }
2157 
2158         /*
2159         public Segment appendRaw(int index, int rawTokenType, Object value)
2160         {
2161             if (index < TOKENS_PER_SEGMENT) {
2162                 set(index, rawTokenType, value);
2163                 return null;
2164             }
2165             _next = new Segment();
2166             _next.set(0, rawTokenType, value);
2167             return _next;
2168         }
2169 
2170         public Segment appendRaw(int index, int rawTokenType, Object value,
2171                 Object objectId, Object typeId)
2172         {
2173             if (index < TOKENS_PER_SEGMENT) {
2174                 set(index, rawTokenType, value, objectId, typeId);
2175                 return null;
2176             }
2177             _next = new Segment();
2178             _next.set(0, rawTokenType, value, objectId, typeId);
2179             return _next;
2180         }
2181 
2182         private void set(int index, int rawTokenType, Object value, Object objectId, Object typeId)
2183         {
2184             _tokens[index] = value;
2185             long typeCode = (long) rawTokenType;
2186             if (index > 0) {
2187                 typeCode <<= (index << 2);
2188             }
2189             _tokenTypes |= typeCode;
2190             assignNativeIds(index, objectId, typeId);
2191         }
2192 
2193         private void set(int index, int rawTokenType, Object value)
2194         {
2195             _tokens[index] = value;
2196             long typeCode = (long) rawTokenType;
2197             if (index > 0) {
2198                 typeCode <<= (index << 2);
2199             }
2200             _tokenTypes |= typeCode;
2201         }
2202         */
2203 
set(int index, JsonToken tokenType)2204         private void set(int index, JsonToken tokenType)
2205         {
2206             /* Assumption here is that there are no overwrites, just appends;
2207              * and so no masking is needed (nor explicit setting of null)
2208              */
2209             long typeCode = tokenType.ordinal();
2210             if (index > 0) {
2211                 typeCode <<= (index << 2);
2212             }
2213             _tokenTypes |= typeCode;
2214         }
2215 
set(int index, JsonToken tokenType, Object objectId, Object typeId)2216         private void set(int index, JsonToken tokenType,
2217                 Object objectId, Object typeId)
2218         {
2219             long typeCode = tokenType.ordinal();
2220             if (index > 0) {
2221                 typeCode <<= (index << 2);
2222             }
2223             _tokenTypes |= typeCode;
2224             assignNativeIds(index, objectId, typeId);
2225         }
2226 
set(int index, JsonToken tokenType, Object value)2227         private void set(int index, JsonToken tokenType, Object value)
2228         {
2229             _tokens[index] = value;
2230             long typeCode = tokenType.ordinal();
2231             if (index > 0) {
2232                 typeCode <<= (index << 2);
2233             }
2234             _tokenTypes |= typeCode;
2235         }
2236 
set(int index, JsonToken tokenType, Object value, Object objectId, Object typeId)2237         private void set(int index, JsonToken tokenType, Object value,
2238                 Object objectId, Object typeId)
2239         {
2240             _tokens[index] = value;
2241             long typeCode = tokenType.ordinal();
2242             if (index > 0) {
2243                 typeCode <<= (index << 2);
2244             }
2245             _tokenTypes |= typeCode;
2246             assignNativeIds(index, objectId, typeId);
2247         }
2248 
assignNativeIds(int index, Object objectId, Object typeId)2249         private final void assignNativeIds(int index, Object objectId, Object typeId)
2250         {
2251             if (_nativeIds == null) {
2252                 _nativeIds = new TreeMap<Integer,Object>();
2253             }
2254             if (objectId != null) {
2255                 _nativeIds.put(_objectIdIndex(index), objectId);
2256             }
2257             if (typeId != null) {
2258                 _nativeIds.put(_typeIdIndex(index), typeId);
2259             }
2260         }
2261 
2262         /**
2263          * @since 2.3
2264          */
findObjectId(int index)2265         private Object findObjectId(int index) {
2266             return (_nativeIds == null) ? null : _nativeIds.get(_objectIdIndex(index));
2267         }
2268 
2269         /**
2270          * @since 2.3
2271          */
findTypeId(int index)2272         private Object findTypeId(int index) {
2273             return (_nativeIds == null) ? null : _nativeIds.get(_typeIdIndex(index));
2274         }
2275 
_typeIdIndex(int i)2276         private final int _typeIdIndex(int i) { return i+i; }
_objectIdIndex(int i)2277         private final int _objectIdIndex(int i) { return i+i+1; }
2278     }
2279 }
2280