• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.node;
2 
3 import java.util.*;
4 
5 import com.fasterxml.jackson.core.*;
6 import com.fasterxml.jackson.databind.JsonNode;
7 
8 /**
9  * Helper class used by {@link TreeTraversingParser} to keep track
10  * of current location within traversed JSON tree.
11  */
12 abstract class NodeCursor
13     extends JsonStreamContext
14 {
15     /**
16      * Parent cursor of this cursor, if any; null for root
17      * cursors.
18      */
19     protected final NodeCursor _parent;
20 
21     /**
22      * Current field name
23      */
24     protected String _currentName;
25 
26     /**
27      * @since 2.5
28      */
29     protected java.lang.Object _currentValue;
30 
NodeCursor(int contextType, NodeCursor p)31     public NodeCursor(int contextType, NodeCursor p)
32     {
33         super();
34         _type = contextType;
35         _index = -1;
36         _parent = p;
37     }
38 
39     /*
40     /**********************************************************
41     /* JsonStreamContext impl
42     /**********************************************************
43      */
44 
45     // note: co-variant return type
46     @Override
getParent()47     public final NodeCursor getParent() { return _parent; }
48 
49     @Override
getCurrentName()50     public final String getCurrentName() {
51         return _currentName;
52     }
53 
54     /**
55      * @since 2.0
56      */
overrideCurrentName(String name)57     public void overrideCurrentName(String name) {
58         _currentName = name;
59     }
60 
61     @Override
getCurrentValue()62     public java.lang.Object getCurrentValue() {
63         return _currentValue;
64     }
65 
66     @Override
setCurrentValue(java.lang.Object v)67     public void setCurrentValue(java.lang.Object v) {
68         _currentValue = v;
69     }
70 
71     /*
72     /**********************************************************
73     /* Extended API
74     /**********************************************************
75      */
76 
nextToken()77     public abstract JsonToken nextToken();
currentNode()78     public abstract JsonNode currentNode();
79 
startObject()80     public abstract NodeCursor startObject();
startArray()81     public abstract NodeCursor startArray();
82 
83     /**
84      * Method called to create a new context for iterating all
85      * contents of the current structured value (JSON array or object)
86      */
iterateChildren()87     public final NodeCursor iterateChildren() {
88         JsonNode n = currentNode();
89         if (n == null) throw new IllegalStateException("No current node");
90         if (n.isArray()) { // false since we have already returned START_ARRAY
91             return new ArrayCursor(n, this);
92         }
93         if (n.isObject()) {
94             return new ObjectCursor(n, this);
95         }
96         throw new IllegalStateException("Current node of type "+n.getClass().getName());
97     }
98 
99     /*
100     /**********************************************************
101     /* Concrete implementations
102     /**********************************************************
103      */
104 
105     /**
106      * Context for all root-level value nodes (including Arrays and Objects):
107      * only context for scalar values.
108      */
109     protected final static class RootCursor
110         extends NodeCursor
111     {
112         protected JsonNode _node;
113 
114         protected boolean _done = false;
115 
RootCursor(JsonNode n, NodeCursor p)116         public RootCursor(JsonNode n, NodeCursor p) {
117             super(JsonStreamContext.TYPE_ROOT, p);
118             _node = n;
119         }
120 
121         @Override
overrideCurrentName(String name)122         public void overrideCurrentName(String name) {
123 
124         }
125 
126         @Override
nextToken()127         public JsonToken nextToken() {
128             if (!_done) {
129                 ++_index;
130                 _done = true;
131                 return _node.asToken();
132             }
133             _node = null;
134             return null;
135         }
136 
137         @Override
currentNode()138         public JsonNode currentNode() {
139             // May look weird, but is necessary so as not to expose current node
140             // before it has been traversed
141             return _done ? _node : null;
142         }
143 
144         @Override
startArray()145         public NodeCursor startArray() { return new ArrayCursor(_node, this); }
146 
147         @Override
startObject()148         public NodeCursor startObject() { return new ObjectCursor(_node, this); }
149     }
150 
151     // Cursor used for traversing JSON Array nodes
152     protected final static class ArrayCursor
153         extends NodeCursor
154     {
155         protected Iterator<JsonNode> _contents;
156 
157         protected JsonNode _currentElement;
158 
ArrayCursor(JsonNode n, NodeCursor p)159         public ArrayCursor(JsonNode n, NodeCursor p) {
160             super(JsonStreamContext.TYPE_ARRAY, p);
161             _contents = n.elements();
162         }
163 
164         @Override
nextToken()165         public JsonToken nextToken()
166         {
167             if (!_contents.hasNext()) {
168                 _currentElement = null;
169                 return JsonToken.END_ARRAY;
170             }
171             ++_index;
172             _currentElement = _contents.next();
173             return _currentElement.asToken();
174         }
175 
176         @Override
currentNode()177         public JsonNode currentNode() { return _currentElement; }
178 
179         @Override
startArray()180         public NodeCursor startArray() { return new ArrayCursor(_currentElement, this); }
181 
182         @Override
startObject()183         public NodeCursor startObject() { return new ObjectCursor(_currentElement, this); }
184     }
185 
186     // Cursor used for traversing JSON Object nodes
187     protected final static class ObjectCursor
188         extends NodeCursor
189     {
190         protected Iterator<Map.Entry<String, JsonNode>> _contents;
191         protected Map.Entry<String, JsonNode> _current;
192 
193         protected boolean _needEntry;
194 
ObjectCursor(JsonNode n, NodeCursor p)195         public ObjectCursor(JsonNode n, NodeCursor p)
196         {
197             super(JsonStreamContext.TYPE_OBJECT, p);
198             _contents = ((ObjectNode) n).fields();
199             _needEntry = true;
200         }
201 
202         @Override
nextToken()203         public JsonToken nextToken()
204         {
205             // Need a new entry?
206             if (_needEntry) {
207                 if (!_contents.hasNext()) {
208                     _currentName = null;
209                     _current = null;
210                     return JsonToken.END_OBJECT;
211                 }
212                 ++_index;
213                 _needEntry = false;
214                 _current = _contents.next();
215                 _currentName = (_current == null) ? null : _current.getKey();
216                 return JsonToken.FIELD_NAME;
217             }
218             _needEntry = true;
219             return _current.getValue().asToken();
220         }
221 
222         @Override
currentNode()223         public JsonNode currentNode() {
224             return (_current == null) ? null : _current.getValue();
225         }
226 
227         @Override
startArray()228         public NodeCursor startArray() { return new ArrayCursor(currentNode(), this); }
229 
230         @Override
startObject()231         public NodeCursor startObject() { return new ObjectCursor(currentNode(), this); }
232     }
233 }
234