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