package sh.calaba.org.codehaus.jackson.node; import java.io.IOException; import java.math.BigDecimal; import java.util.*; import sh.calaba.org.codehaus.jackson.*; import sh.calaba.org.codehaus.jackson.map.SerializerProvider; import sh.calaba.org.codehaus.jackson.map.TypeSerializer; /** * Node class that represents Arrays mapped from Json content. */ public final class ArrayNode extends ContainerNode { protected ArrayList _children; public ArrayNode(JsonNodeFactory nc) { super(nc); } /* /********************************************************** /* Implementation of core JsonNode API /********************************************************** */ @Override public JsonToken asToken() { return JsonToken.START_ARRAY; } @Override public boolean isArray() { return true; } @Override public int size() { return (_children == null) ? 0 : _children.size(); } @Override public Iterator getElements() { return (_children == null) ? NoNodesIterator.instance() : _children.iterator(); } @Override public JsonNode get(int index) { if (index >= 0 && (_children != null) && index < _children.size()) { return _children.get(index); } return null; } @Override public JsonNode get(String fieldName) { return null; } @Override public JsonNode path(String fieldName) { return MissingNode.getInstance(); } @Override public JsonNode path(int index) { if (index >= 0 && (_children != null) && index < _children.size()) { return _children.get(index); } return MissingNode.getInstance(); } /* /********************************************************** /* Public API, serialization /********************************************************** */ @Override public final void serialize(JsonGenerator jg, SerializerProvider provider) throws IOException, JsonProcessingException { jg.writeStartArray(); if (_children != null) { for (JsonNode n : _children) { /* 17-Feb-2009, tatu: Can we trust that all nodes will always * extend BaseJsonNode? Or if not, at least implement * JsonSerializable? Let's start with former, change if * we must. */ ((BaseJsonNode)n).serialize(jg, provider); } } jg.writeEndArray(); } @Override public void serializeWithType(JsonGenerator jg, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException { typeSer.writeTypePrefixForArray(this, jg); if (_children != null) { for (JsonNode n : _children) { ((BaseJsonNode)n).serialize(jg, provider); } } typeSer.writeTypeSuffixForArray(this, jg); } /* /********************************************************** /* Public API, finding value nodes /********************************************************** */ @Override public JsonNode findValue(String fieldName) { if (_children != null) { for (JsonNode node : _children) { JsonNode value = node.findValue(fieldName); if (value != null) { return value; } } } return null; } @Override public List findValues(String fieldName, List foundSoFar) { if (_children != null) { for (JsonNode node : _children) { foundSoFar = node.findValues(fieldName, foundSoFar); } } return foundSoFar; } @Override public List findValuesAsText(String fieldName, List foundSoFar) { if (_children != null) { for (JsonNode node : _children) { foundSoFar = node.findValuesAsText(fieldName, foundSoFar); } } return foundSoFar; } @Override public ObjectNode findParent(String fieldName) { if (_children != null) { for (JsonNode node : _children) { JsonNode parent = node.findParent(fieldName); if (parent != null) { return (ObjectNode) parent; } } } return null; } @Override public List findParents(String fieldName, List foundSoFar) { if (_children != null) { for (JsonNode node : _children) { foundSoFar = node.findParents(fieldName, foundSoFar); } } return foundSoFar; } /* /********************************************************** /* Extended ObjectNode API, accessors /********************************************************** */ /** * Method that will set specified field, replacing old value, * if any. * * @param value to set field to; if null, will be converted * to a {@link NullNode} first (to remove field entry, call * {@link #remove} instead) * * @return Old value of the field, if any; null if there was no * old value. */ public JsonNode set(int index, JsonNode value) { if (value == null) { // let's not store 'raw' nulls but nodes value = nullNode(); } return _set(index, value); } public void add(JsonNode value) { if (value == null) { // let's not store 'raw' nulls but nodes value = nullNode(); } _add(value); } /** * Method for adding all child nodes of given Array, appending to * child nodes this array contains * * @param other Array to add contents from * * @return This node (to allow chaining) * * @since 1.3 */ public JsonNode addAll(ArrayNode other) { int len = other.size(); if (len > 0) { if (_children == null) { _children = new ArrayList(len+2); } other.addContentsTo(_children); } return this; } /** * Method for adding given nodes as child nodes of this array node. * * @param nodes Nodes to add * * @return This node (to allow chaining) * * @since 1.3 */ public JsonNode addAll(Collection nodes) { int len = nodes.size(); if (len > 0) { if (_children == null) { _children = new ArrayList(nodes); } else { _children.addAll(nodes); } } return this; } /** * Method for inserting specified child node as an element * of this Array. If index is 0 or less, it will be inserted as * the first element; if >= size(), appended at the end, and otherwise * inserted before existing element in specified index. * No exceptions are thrown for any index. */ public void insert(int index, JsonNode value) { if (value == null) { value = nullNode(); } _insert(index, value); } /** * Method for removing an entry from this ArrayNode. * Will return value of the entry at specified index, if entry existed; * null if not. */ public JsonNode remove(int index) { if (index >= 0 && (_children != null) && index < _children.size()) { return _children.remove(index); } return null; } @Override public ArrayNode removeAll() { _children = null; return this; } /* /********************************************************** /* Extended ObjectNode API, mutators, generic; addXxx()/insertXxx() /********************************************************** */ /** * Method that will construct an ArrayNode and add it as a * field of this ObjectNode, replacing old value, if any. * * @return Newly constructed ArrayNode */ public ArrayNode addArray() { ArrayNode n = arrayNode(); _add(n); return n; } /** * Method that will construct an ObjectNode and add it at the end * of this array node. * * @return Newly constructed ObjectNode */ public ObjectNode addObject() { ObjectNode n = objectNode(); _add(n); return n; } /** * Method that will construct a POJONode and add it at the end * of this array node. */ public void addPOJO(Object value) { if (value == null) { addNull(); } else { _add(POJONode(value)); } } public void addNull() { _add(nullNode()); } /** * Method for setting value of a field to specified numeric value. */ public void add(int v) { _add(numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void add(Integer value) { if (value == null) { addNull(); } else { _add(numberNode(value.intValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void add(long v) { _add(numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void add(Long value) { if (value == null) { addNull(); } else { _add(numberNode(value.longValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void add(float v) { _add(numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void add(Float value) { if (value == null) { addNull(); } else { _add(numberNode(value.floatValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void add(double v) { _add(numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void add(Double value) { if (value == null) { addNull(); } else { _add(numberNode(value.doubleValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void add(BigDecimal v) { if (v == null) { addNull(); } else { _add(numberNode(v)); } } /** * Method for setting value of a field to specified String value. */ public void add(String v) { if (v == null) { addNull(); } else { _add(textNode(v)); } } /** * Method for setting value of a field to specified String value. */ public void add(boolean v) { _add(booleanNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void add(Boolean value) { if (value == null) { addNull(); } else { _add(booleanNode(value.booleanValue())); } } /** * Method for setting value of a field to specified binary value */ public void add(byte[] v) { if (v == null) { addNull(); } else { _add(binaryNode(v)); } } public ArrayNode insertArray(int index) { ArrayNode n = arrayNode(); _insert(index, n); return n; } /** * Method that will construct an ObjectNode and add it at the end * of this array node. * * @return Newly constructed ObjectNode */ public ObjectNode insertObject(int index) { ObjectNode n = objectNode(); _insert(index, n); return n; } /** * Method that will construct a POJONode and add it at the end * of this array node. */ public void insertPOJO(int index, Object value) { if (value == null) { insertNull(index); } else { _insert(index, POJONode(value)); } } public void insertNull(int index) { _insert(index, nullNode()); } /** * Method for setting value of a field to specified numeric value. */ public void insert(int index, int v) { _insert(index, numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void insert(int index, Integer value) { if (value == null) { insertNull(index); } else { _insert(index, numberNode(value.intValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void insert(int index, long v) { _insert(index, numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void insert(int index, Long value) { if (value == null) { insertNull(index); } else { _insert(index, numberNode(value.longValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void insert(int index, float v) { _insert(index, numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void insert(int index, Float value) { if (value == null) { insertNull(index); } else { _insert(index, numberNode(value.floatValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void insert(int index, double v) { _insert(index, numberNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void insert(int index, Double value) { if (value == null) { insertNull(index); } else { _insert(index, numberNode(value.doubleValue())); } } /** * Method for setting value of a field to specified numeric value. */ public void insert(int index, BigDecimal v) { if (v == null) { insertNull(index); } else { _insert(index, numberNode(v)); } } /** * Method for setting value of a field to specified String value. */ public void insert(int index, String v) { if (v == null) { insertNull(index); } else { _insert(index, textNode(v)); } } /** * Method for setting value of a field to specified String value. */ public void insert(int index, boolean v) { _insert(index, booleanNode(v)); } /** * Alternative method that we need to avoid bumping into NPE issues * with auto-unboxing. * * @since 1.9 */ public void insert(int index, Boolean value) { if (value == null) { insertNull(index); } else { _insert(index, booleanNode(value.booleanValue())); } } /** * Method for setting value of a field to specified binary value */ public void insert(int index, byte[] v) { if (v == null) { insertNull(index); } else { _insert(index, binaryNode(v)); } } /* /********************************************************** /* Package methods (for other node classes to use) /********************************************************** */ /** * @since 1.6 */ protected void addContentsTo(List dst) { if (_children != null) { for (JsonNode n : _children) { dst.add(n); } } } /* /********************************************************** /* Standard methods /********************************************************** */ @Override public boolean equals(Object o) { if (o == this) return true; if (o == null) return false; if (o.getClass() != getClass()) { // final class, can do this return false; } ArrayNode other = (ArrayNode) o; if (_children == null || _children.size() == 0) { return other.size() == 0; } return other._sameChildren(_children); } @Override public int hashCode() { int hash; if (_children == null) { hash = 1; } else { hash = _children.size(); for (JsonNode n : _children) { if (n != null) { hash ^= n.hashCode(); } } } return hash; } @Override public String toString() { StringBuilder sb = new StringBuilder(16 + (size() << 4)); sb.append('['); if (_children != null) { for (int i = 0, len = _children.size(); i < len; ++i) { if (i > 0) { sb.append(','); } sb.append(_children.get(i).toString()); } } sb.append(']'); return sb.toString(); } /* /********************************************************** /* Internal methods /********************************************************** */ public JsonNode _set(int index, JsonNode value) { if (_children == null || index < 0 || index >= _children.size()) { throw new IndexOutOfBoundsException("Illegal index "+index+", array size "+size()); } return _children.set(index, value); } private void _add(JsonNode node) { if (_children == null) { _children = new ArrayList(); } _children.add(node); } private void _insert(int index, JsonNode node) { if (_children == null) { _children = new ArrayList(); _children.add(node); return; } if (index < 0) { _children.add(0, node); } else if (index >= _children.size()) { _children.add(node); } else { _children.add(index, node); } } /** * Note: this method gets called iff otherChildren * is non-empty */ private boolean _sameChildren(ArrayList otherChildren) { int len = otherChildren.size(); if (this.size() != len) { // important: call size() to handle case of null list... return false; } for (int i = 0; i < len; ++i) { if (!_children.get(i).equals(otherChildren.get(i))) { return false; } } return true; } }