blob: 876028e110db74436f6a470b4dc700fffc3e72a3 [file] [log] [blame]
package org.codehaus.jackson.node;
import java.util.*;
import org.codehaus.jackson.*;
/**
* Helper class used by {@link TreeTraversingParser} to keep track
* of current location within traversed JSON tree.
*/
abstract class NodeCursor
extends JsonStreamContext
{
/**
* Parent cursor of this cursor, if any; null for root
* cursors.
*/
final NodeCursor _parent;
public NodeCursor(int contextType, NodeCursor p)
{
super();
_type = contextType;
_index = -1;
_parent = p;
}
/*
/**********************************************************
/* JsonStreamContext impl
/**********************************************************
*/
// note: co-variant return type
@Override
public final NodeCursor getParent() { return _parent; }
@Override
public abstract String getCurrentName();
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public abstract JsonToken nextToken();
public abstract JsonToken nextValue();
public abstract JsonToken endToken();
public abstract JsonNode currentNode();
public abstract boolean currentHasChildren();
/**
* 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 Array(n, this);
}
if (n.isObject()) {
return new Object(n, this);
}
throw new IllegalStateException("Current node of type "+n.getClass().getName());
}
/*
/**********************************************************
/* Concrete implementations
/**********************************************************
*/
/**
* Context matching root-level value nodes (i.e. anything other
* than JSON Object and Array).
* Note that context is NOT created for leaf values.
*/
protected final static class RootValue
extends NodeCursor
{
JsonNode _node;
protected boolean _done = false;
public RootValue(JsonNode n, NodeCursor p) {
super(JsonStreamContext.TYPE_ROOT, p);
_node = n;
}
@Override
public String getCurrentName() { return null; }
@Override
public JsonToken nextToken() {
if (!_done) {
_done = true;
return _node.asToken();
}
_node = null;
return null;
}
@Override
public JsonToken nextValue() { return nextToken(); }
@Override
public JsonToken endToken() { return null; }
@Override
public JsonNode currentNode() { return _node; }
@Override
public boolean currentHasChildren() { return false; }
}
/**
* Cursor used for traversing non-empty JSON Array nodes
*/
protected final static class Array
extends NodeCursor
{
Iterator<JsonNode> _contents;
JsonNode _currentNode;
public Array(JsonNode n, NodeCursor p) {
super(JsonStreamContext.TYPE_ARRAY, p);
_contents = n.getElements();
}
@Override
public String getCurrentName() { return null; }
@Override
public JsonToken nextToken()
{
if (!_contents.hasNext()) {
_currentNode = null;
return null;
}
_currentNode = _contents.next();
return _currentNode.asToken();
}
@Override
public JsonToken nextValue() { return nextToken(); }
@Override
public JsonToken endToken() { return JsonToken.END_ARRAY; }
@Override
public JsonNode currentNode() { return _currentNode; }
@Override
public boolean currentHasChildren() {
// note: ONLY to be called for container nodes
return ((ContainerNode) currentNode()).size() > 0;
}
}
/**
* Cursor used for traversing non-empty JSON Object nodes
*/
protected final static class Object
extends NodeCursor
{
Iterator<Map.Entry<String, JsonNode>> _contents;
Map.Entry<String, JsonNode> _current;
boolean _needEntry;
public Object(JsonNode n, NodeCursor p)
{
super(JsonStreamContext.TYPE_OBJECT, p);
_contents = ((ObjectNode) n).getFields();
_needEntry = true;
}
@Override
public String getCurrentName() {
return (_current == null) ? null : _current.getKey();
}
@Override
public JsonToken nextToken()
{
// Need a new entry?
if (_needEntry) {
if (!_contents.hasNext()) {
_current = null;
return null;
}
_needEntry = false;
_current = _contents.next();
return JsonToken.FIELD_NAME;
}
_needEntry = true;
return _current.getValue().asToken();
}
@Override
public JsonToken nextValue()
{
JsonToken t = nextToken();
if (t == JsonToken.FIELD_NAME) {
t = nextToken();
}
return t;
}
@Override
public JsonToken endToken() { return JsonToken.END_OBJECT; }
@Override
public JsonNode currentNode() {
return (_current == null) ? null : _current.getValue();
}
@Override
public boolean currentHasChildren() {
// note: ONLY to be called for container nodes
return ((ContainerNode) currentNode()).size() > 0;
}
}
}