• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.node;
2 
3 import java.io.IOException;
4 import java.io.OutputStream;
5 import java.math.BigDecimal;
6 import java.math.BigInteger;
7 
8 import com.fasterxml.jackson.core.*;
9 import com.fasterxml.jackson.core.base.ParserMinimalBase;
10 import com.fasterxml.jackson.core.util.JacksonFeatureSet;
11 import com.fasterxml.jackson.databind.JsonNode;
12 
13 /**
14  * Facade over {@link JsonNode} that implements {@link JsonParser} to allow
15  * accessing contents of JSON tree in alternate form (stream of tokens).
16  * Useful when a streaming source is expected by code, such as data binding
17  * functionality.
18  */
19 public class TreeTraversingParser extends ParserMinimalBase
20 {
21     /*
22     /**********************************************************
23     /* Configuration
24     /**********************************************************
25      */
26 
27     protected ObjectCodec _objectCodec;
28 
29     /**
30      * Traversal context within tree
31      */
32     protected NodeCursor _nodeCursor;
33 
34     /*
35     /**********************************************************
36     /* State
37     /**********************************************************
38      */
39 
40     /**
41      * Flag that indicates whether parser is closed or not. Gets
42      * set when parser is either closed by explicit call
43      * ({@link #close}) or when end-of-input is reached.
44      */
45     protected boolean _closed;
46 
47     /*
48     /**********************************************************
49     /* Life-cycle
50     /**********************************************************
51      */
52 
TreeTraversingParser(JsonNode n)53     public TreeTraversingParser(JsonNode n) { this(n, null); }
54 
TreeTraversingParser(JsonNode n, ObjectCodec codec)55     public TreeTraversingParser(JsonNode n, ObjectCodec codec)
56     {
57         super(0);
58         _objectCodec = codec;
59         _nodeCursor = new NodeCursor.RootCursor(n, null);
60     }
61 
62     @Override
setCodec(ObjectCodec c)63     public void setCodec(ObjectCodec c) {
64         _objectCodec = c;
65     }
66 
67     @Override
getCodec()68     public ObjectCodec getCodec() {
69         return _objectCodec;
70     }
71 
72     @Override
version()73     public Version version() {
74         return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
75     }
76 
77     @Override
getReadCapabilities()78     public JacksonFeatureSet<StreamReadCapability> getReadCapabilities() {
79         // Defaults are fine
80         return DEFAULT_READ_CAPABILITIES;
81     }
82 
83     /*
84     /**********************************************************
85     /* Closeable implementation
86     /**********************************************************
87      */
88 
89     @Override
close()90     public void close() throws IOException
91     {
92         if (!_closed) {
93             _closed = true;
94             _nodeCursor = null;
95             _currToken = null;
96         }
97     }
98 
99     /*
100     /**********************************************************
101     /* Public API, traversal
102     /**********************************************************
103      */
104 
105     @Override
nextToken()106     public JsonToken nextToken() throws IOException, JsonParseException
107     {
108         _currToken = _nodeCursor.nextToken();
109         if (_currToken == null) {
110             _closed = true; // if not already set
111             return null;
112         }
113         switch (_currToken) {
114         case START_OBJECT:
115             _nodeCursor = _nodeCursor.startObject();
116             break;
117         case START_ARRAY:
118             _nodeCursor = _nodeCursor.startArray();
119             break;
120         case END_OBJECT:
121         case END_ARRAY:
122             _nodeCursor = _nodeCursor.getParent();
123         default:
124         }
125         return _currToken;
126     }
127 
128     // default works well here:
129     //public JsonToken nextValue() throws IOException
130 
131     @Override
skipChildren()132     public JsonParser skipChildren() throws IOException
133     {
134         if (_currToken == JsonToken.START_OBJECT) {
135             _nodeCursor = _nodeCursor.getParent();
136             _currToken = JsonToken.END_OBJECT;
137         } else if (_currToken == JsonToken.START_ARRAY) {
138             _nodeCursor = _nodeCursor.getParent();
139             _currToken = JsonToken.END_ARRAY;
140         }
141         return this;
142     }
143 
144     @Override
isClosed()145     public boolean isClosed() {
146         return _closed;
147     }
148 
149     /*
150     /**********************************************************
151     /* Public API, token accessors
152     /**********************************************************
153      */
154 
getCurrentName()155     @Override public String getCurrentName() {
156         NodeCursor crsr = _nodeCursor;
157         if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
158             crsr = crsr.getParent();
159         }
160         return (crsr == null) ? null : crsr.getCurrentName();
161     }
162 
overrideCurrentName(String name)163     @Override public void overrideCurrentName(String name) {
164         NodeCursor crsr = _nodeCursor;
165         if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
166             crsr = crsr.getParent();
167         }
168         if (crsr != null) {
169             crsr.overrideCurrentName(name);
170         }
171     }
172 
173     @Override
getParsingContext()174     public JsonStreamContext getParsingContext() {
175         return _nodeCursor;
176     }
177 
178     @Override
getTokenLocation()179     public JsonLocation getTokenLocation() {
180         return JsonLocation.NA;
181     }
182 
183     @Override
getCurrentLocation()184     public JsonLocation getCurrentLocation() {
185         return JsonLocation.NA;
186     }
187 
188     /*
189     /**********************************************************
190     /* Public API, access to textual content
191     /**********************************************************
192      */
193 
194     @Override
getText()195     public String getText()
196     {
197         if (_closed) {
198             return null;
199         }
200         // need to separate handling a bit...
201         switch (_currToken) {
202         case FIELD_NAME:
203             return _nodeCursor.getCurrentName();
204         case VALUE_STRING:
205             return currentNode().textValue();
206         case VALUE_NUMBER_INT:
207         case VALUE_NUMBER_FLOAT:
208             return String.valueOf(currentNode().numberValue());
209         case VALUE_EMBEDDED_OBJECT:
210             JsonNode n = currentNode();
211             if (n != null && n.isBinary()) {
212                 // this will convert it to base64
213                 return n.asText();
214             }
215         default:
216         	return (_currToken == null) ? null : _currToken.asString();
217         }
218     }
219 
220     @Override
getTextCharacters()221     public char[] getTextCharacters() throws IOException, JsonParseException {
222         return getText().toCharArray();
223     }
224 
225     @Override
getTextLength()226     public int getTextLength() throws IOException, JsonParseException {
227         return getText().length();
228     }
229 
230     @Override
getTextOffset()231     public int getTextOffset() throws IOException, JsonParseException {
232         return 0;
233     }
234 
235     @Override
hasTextCharacters()236     public boolean hasTextCharacters() {
237         // generally we do not have efficient access as char[], hence:
238         return false;
239     }
240 
241     /*
242     /**********************************************************
243     /* Public API, typed non-text access
244     /**********************************************************
245      */
246 
247     //public byte getByteValue() throws IOException
248 
249     @Override
getNumberType()250     public NumberType getNumberType() throws IOException {
251         JsonNode n = currentNumericNode();
252         return (n == null) ? null : n.numberType();
253     }
254 
255     @Override
getBigIntegerValue()256     public BigInteger getBigIntegerValue() throws IOException
257     {
258         return currentNumericNode().bigIntegerValue();
259     }
260 
261     @Override
getDecimalValue()262     public BigDecimal getDecimalValue() throws IOException {
263         return currentNumericNode().decimalValue();
264     }
265 
266     @Override
getDoubleValue()267     public double getDoubleValue() throws IOException {
268         return currentNumericNode().doubleValue();
269     }
270 
271     @Override
getFloatValue()272     public float getFloatValue() throws IOException {
273         return (float) currentNumericNode().doubleValue();
274     }
275 
276     @Override
getIntValue()277     public int getIntValue() throws IOException {
278         final NumericNode node = (NumericNode) currentNumericNode();
279         if (!node.canConvertToInt()) {
280             reportOverflowInt();
281         }
282         return node.intValue();
283     }
284 
285     @Override
getLongValue()286     public long getLongValue() throws IOException {
287         final NumericNode node = (NumericNode) currentNumericNode();
288         if (!node.canConvertToLong()) {
289             reportOverflowLong();
290         }
291         return node.longValue();
292     }
293 
294     @Override
getNumberValue()295     public Number getNumberValue() throws IOException {
296         return currentNumericNode().numberValue();
297     }
298 
299     @Override
getEmbeddedObject()300     public Object getEmbeddedObject()
301     {
302         if (!_closed) {
303             JsonNode n = currentNode();
304             if (n != null) {
305                 if (n.isPojo()) {
306                     return ((POJONode) n).getPojo();
307                 }
308                 if (n.isBinary()) {
309                     return ((BinaryNode) n).binaryValue();
310                 }
311             }
312         }
313         return null;
314     }
315 
316     @Override
isNaN()317     public boolean isNaN() {
318         if (!_closed) {
319             JsonNode n = currentNode();
320             if (n instanceof NumericNode) {
321                 return ((NumericNode) n).isNaN();
322             }
323         }
324         return false;
325     }
326 
327     /*
328     /**********************************************************
329     /* Public API, typed binary (base64) access
330     /**********************************************************
331      */
332 
333     @Override
getBinaryValue(Base64Variant b64variant)334     public byte[] getBinaryValue(Base64Variant b64variant)
335         throws IOException, JsonParseException
336     {
337         // Multiple possibilities...
338         JsonNode n = currentNode();
339         if (n != null) {
340             // [databind#2096]: although `binaryValue()` works for real binary node
341             // and embedded "POJO" node, coercion from TextNode may require variant, so:
342             if (n instanceof TextNode) {
343                 return ((TextNode) n).getBinaryValue(b64variant);
344             }
345             return n.binaryValue();
346         }
347         // otherwise return null to mark we have no binary content
348         return null;
349     }
350 
351 
352     @Override
readBinaryValue(Base64Variant b64variant, OutputStream out)353     public int readBinaryValue(Base64Variant b64variant, OutputStream out)
354             throws IOException, JsonParseException
355     {
356         byte[] data = getBinaryValue(b64variant);
357         if (data != null) {
358             out.write(data, 0, data.length);
359             return data.length;
360         }
361         return 0;
362     }
363 
364     /*
365     /**********************************************************
366     /* Internal methods
367     /**********************************************************
368      */
369 
currentNode()370     protected JsonNode currentNode() {
371         if (_closed || _nodeCursor == null) {
372             return null;
373         }
374         return _nodeCursor.currentNode();
375     }
376 
currentNumericNode()377     protected JsonNode currentNumericNode()
378         throws JsonParseException
379     {
380         JsonNode n = currentNode();
381         if (n == null || !n.isNumber()) {
382             JsonToken t = (n == null) ? null : n.asToken();
383             throw _constructError("Current token ("+t+") not numeric, cannot use numeric value accessors");
384         }
385         return n;
386     }
387 
388     @Override
_handleEOF()389     protected void _handleEOF() throws JsonParseException {
390         _throwInternal(); // should never get called
391     }
392 }
393