NodeCursor.java
package tools.jackson.databind.node;
import java.util.*;
import tools.jackson.core.*;
import tools.jackson.databind.JsonNode;
/**
* Helper class used by {@link TreeTraversingParser} to keep track
* of current location within traversed JSON tree.
*/
abstract class NodeCursor
extends TokenStreamContext
{
/**
* Parent cursor of this cursor, if any; null for root
* cursors.
*/
protected final NodeCursor _parent;
/**
* Current field name
*/
protected String _currentName;
/**
* @since 2.5
*/
protected java.lang.Object _currentValue;
public NodeCursor(int contextType, NodeCursor p)
{
super();
_type = contextType;
_index = -1;
_parent = p;
}
/*
/**********************************************************************
/* JsonStreamContext impl
/**********************************************************************
*/
@Override
public TokenStreamContext getParent() { return _parent; }
/**
* Internal accessor used for tree traversal navigation: returns the parent
* cursor <i>within the traversed tree</i> ({@code null} for the root cursor).
* Unlike {@link #getParent()} this never exposes an externally-attached parent
* context (see {@link RootCursor}).
*
* @since 3.3
*/
public final NodeCursor parentCursor() { return _parent; }
@Override
public final String currentName() {
return _currentName;
}
public void overrideCurrentName(String name) {
_currentName = name;
}
@Override
public java.lang.Object currentValue() {
return _currentValue;
}
@Override
public void assignCurrentValue(java.lang.Object v) {
_currentValue = v;
}
/*
/**********************************************************************
/* Extended API
/**********************************************************************
*/
public abstract JsonToken nextToken();
public abstract JsonNode currentNode();
public abstract NodeCursor startObject();
public abstract NodeCursor startArray();
/**
* Method called to create a new context for iterating all
* contents of the current structured value (JSON array or object)
*/
public final NodeCursor iterateChildren() {
JsonNode n = currentNode();
if (n == null) throw new IllegalStateException("No current node");
if (n.isArray()) { // false since we have already returned START_ARRAY
return new ArrayCursor(n, this);
}
if (n.isObject()) {
return new ObjectCursor(n, this);
}
throw new IllegalStateException("Current node of type "+n.getClass().getName());
}
/*
/**********************************************************************
/* Concrete implementations
/**********************************************************************
*/
/**
* Context for all root-level value nodes (including Arrays and Objects):
* only context for scalar values.
*/
protected final static class RootCursor
extends NodeCursor
{
protected JsonNode _node;
protected boolean _done = false;
/**
* Optional, externally-attached parent context (e.g. the context of the
* parser that produced the tree being traversed); {@code null} if none.
* Exposed via {@link #getParent()} so that callers relying on parent
* hierarchy (such as {@code pathAsPointer()}) see the full path. Kept
* separate from the (cursor) {@code _parent} chain used for navigation,
* which is always {@code null} for the root.
*
* @since 3.3
*/
protected final TokenStreamContext _parentContext;
public RootCursor(JsonNode n, TokenStreamContext parentContext) {
super(TokenStreamContext.TYPE_ROOT, null);
_node = n;
_parentContext = parentContext;
}
@Override
public TokenStreamContext getParent() { return _parentContext; }
@Override
public void overrideCurrentName(String name) {
}
@Override
public JsonToken nextToken() {
if (!_done) {
++_index;
_done = true;
return _node.asToken();
}
_node = null;
return null;
}
@Override
public JsonNode currentNode() {
// May look weird, but is necessary so as not to expose current node
// before it has been traversed
return _done ? _node : null;
}
@Override
public NodeCursor startArray() { return new ArrayCursor(_node, this); }
@Override
public NodeCursor startObject() { return new ObjectCursor(_node, this); }
}
// Cursor used for traversing JSON Array nodes
protected final static class ArrayCursor
extends NodeCursor
{
protected Iterator<JsonNode> _contents;
protected JsonNode _currentElement;
public ArrayCursor(JsonNode n, NodeCursor p) {
super(TokenStreamContext.TYPE_ARRAY, p);
_contents = n.values().iterator();
}
@Override
public JsonToken nextToken()
{
if (!_contents.hasNext()) {
_currentElement = null;
return JsonToken.END_ARRAY;
}
++_index;
_currentElement = _contents.next();
return _currentElement.asToken();
}
@Override
public JsonNode currentNode() { return _currentElement; }
@Override
public NodeCursor startArray() { return new ArrayCursor(_currentElement, this); }
@Override
public NodeCursor startObject() { return new ObjectCursor(_currentElement, this); }
}
// Cursor used for traversing JSON Object nodes
protected final static class ObjectCursor
extends NodeCursor
{
protected Iterator<Map.Entry<String, JsonNode>> _contents;
protected Map.Entry<String, JsonNode> _current;
protected boolean _needEntry;
public ObjectCursor(JsonNode n, NodeCursor p)
{
super(TokenStreamContext.TYPE_OBJECT, p);
_contents = n.properties().iterator();
_needEntry = true;
}
@Override
public JsonToken nextToken()
{
// Need a new entry?
if (_needEntry) {
if (!_contents.hasNext()) {
_currentName = null;
_current = null;
return JsonToken.END_OBJECT;
}
++_index;
_needEntry = false;
_current = _contents.next();
_currentName = (_current == null) ? null : _current.getKey();
return JsonToken.PROPERTY_NAME;
}
_needEntry = true;
return _current.getValue().asToken();
}
@Override
public JsonNode currentNode() {
return (_current == null) ? null : _current.getValue();
}
@Override
public NodeCursor startArray() { return new ArrayCursor(currentNode(), this); }
@Override
public NodeCursor startObject() { return new ObjectCursor(currentNode(), this); }
}
}