/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
package org.ebayopensource.turmeric.runtime.binding.objectnode.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import org.ebayopensource.turmeric.runtime.binding.BindingConstants;
import org.ebayopensource.turmeric.runtime.binding.objectnode.ObjectNode;
import org.ebayopensource.turmeric.runtime.binding.objectnode.ObjectNodeType;
public class ObjectNodeImpl implements ObjectNode {
public static final QName ROOT_NODE_QNAME = new QName("root");
public static final ObjectNodeImpl EMPTY_ROOT_NODE = new ObjectNodeImpl(ROOT_NODE_QNAME, null);
private QName m_name;
private String m_value;
private boolean m_isNull;
protected ArrayList<ObjectNode> m_children;
protected ArrayList<ObjectNode> m_attributes = null;
protected Location m_location;
private ObjectNode m_parent;
protected boolean m_isAttribute = false;
public static final ObjectNodeImpl createEmptyRootNode() {
return new ObjectNodeImpl(ROOT_NODE_QNAME, null);
}
public ObjectNodeImpl(QName name, ObjectNode parent) {
this(name, parent, false);
}
public ObjectNodeImpl(QName name, ObjectNode parent, boolean isAttribute) {
m_name = name;
m_isAttribute = isAttribute;
handleAtMarkedElementName();
m_parent = parent;
m_children = new ArrayList<ObjectNode>();
m_attributes = new ArrayList<ObjectNode>();
}
/**
*
* @param name
* @return
* @throws XMLStreamException
*/
public List<ObjectNode> getChildNodes(QName name) throws XMLStreamException {
ArrayList<ObjectNode> children = new ArrayList<ObjectNode>();
boolean found = false;
for (Iterator<ObjectNode> it = m_children.iterator(); it.hasNext();) {
ObjectNode child = it.next();
if (sameQName(child.getNodeName(), name)) {
children.add(child);
found = true;
} else if (found) {
// Assumption here is that children with the same name are
// listed one after each other in the list. Once we found one
// child not with the same name, we can stop.
break;
}
}
return found ? children : null;
}
/**
* Gets (index + 1)st child with the same name.
*
* @param name
* @param index
* @return
* @throws XMLStreamException
*/
public ObjectNode getChildNode(QName name, int index) throws XMLStreamException {
int i = getChildNodeIndex(name, index);
return i > -1 ? m_children.get(i) : null;
}
/**
* Gets child at the index position of the m_children array.
*
* @param index
* @return
* @throws XMLStreamException
*/
public ObjectNode getChildNode(int index) throws XMLStreamException {
return m_children.get(index);
}
/**
* Returns the index of the child node in the m_children list.
*
* @param name
* @param index
* @return
*/
private int getChildNodeIndex(QName name, int index) {
boolean found = false;
int count = 0;
for (int i=0; i<m_children.size(); i++) {
ObjectNode child = m_children.get(i);
if (sameQName(child.getNodeName(), name)) {
if (count == index) {
return i;
}
count++;
} else if (found) {
// Assumption here is that children with the same name are
// listed one after each other in the list. Once we found one
// child not with the same name, we can stop.
break;
}
}
return -1;
}
/**
* Set the child and returns its index in the child array;
*
* @param name
* @param child
* @return
*/
public int setChild(QName name, ObjectNodeImpl child) {
return setChild(name, Integer.MAX_VALUE, child);
}
public int addChild(ObjectNodeImpl child) {
m_children.add(child);
return m_children.size() - 1;
}
/**
* Set the child and returns its index in the whole children list;
*
* @param name
* @param index
* @param child
* @return
*/
public int setChild(QName name, int index, ObjectNodeImpl child) {
if (null == name) {
return -1;
}
int i = getChildNodeIndex(name, index);
if (i < 0) {
m_children.add(index, child);
} else {
m_children.set(index, child);
}
return index;
}
public boolean hasChildNode(QName name, int index) {
return getChildNodeIndex(name, index) > -1;
}
public boolean hasChildNodes() throws XMLStreamException {
return m_children.size() > 0;
}
public void setName(QName name) {
this.m_name = name;
}
public String getNodeValue() {
return m_value;
}
public void setNodeValue(Object value) {
if (value instanceof String) {
this.m_value = (String) value;
return;
}
throw new IllegalArgumentException("Expects " + String.class.getName());
}
public Iterator<ObjectNode> getChildrenIterator() throws XMLStreamException {
return new ChildNodeIterator(this);
}
public QName getNodeName() {
return m_name;
}
public ObjectNodeType getNodeType() {
return ObjectNodeType.XML;
}
public Object getUnderlyingRawNode() {
throw new UnsupportedOperationException(this.getClass().getName()
+ ".getUnderlyingRawNode");
}
public ObjectNode getParentNode() {
return m_parent;
}
public void setParentNode(ObjectNode parent) {
this.m_parent = parent;
}
/**
* Returns the child nodes. If there are no child nodes, it returns null.
*
* @return List of child nodes.
*/
public List<ObjectNode> getChildNodes() throws XMLStreamException {
return Collections.unmodifiableList(m_children);
}
public int getChildNodesSize() throws XMLStreamException {
return m_children.size();
}
public void insertChildAt(ObjectNode node, int index)
throws IndexOutOfBoundsException {
throw new UnsupportedOperationException(this.getClass().getName()
+ ".insertChildAt");
}
public void replaceChildAt(ObjectNode node, int index)
throws IndexOutOfBoundsException {
throw new UnsupportedOperationException(this.getClass().getName()
+ ".replaceChildAt");
}
public ObjectNode cloneNode() throws XMLStreamException {
ObjectNodeImpl clone = new ObjectNodeImpl(this.m_name, this.m_parent);
clone.m_value = this.m_value;
clone.m_children = this.m_children;
return clone;
}
public void setIsNull(boolean isNull) {
m_isNull = isNull;
}
public boolean getIsNull() {
return m_isNull;
}
public boolean isAttribute() {
return m_isAttribute;
}
public boolean hasAttributes() {
if (m_attributes == null) {
return false;
}
return m_attributes.size() > 0;
}
public int getAttributeCount() {
if (m_attributes == null) {
return 0;
}
return m_attributes.size();
}
/**
*
* @return all the attributes of this node.
*/
public List<ObjectNode> getAttributes() {
return m_attributes;
}
public ObjectNode getAttribute(int n) {
return m_attributes == null ? null : m_attributes.get(n);
}
public void addAttribute(ObjectNode o) {
if (null == m_attributes) {
m_attributes = new ArrayList<ObjectNode>();
}
m_attributes.add(o);
}
public Location getLocation() {
return m_location;
}
public String getTree() throws XMLStreamException {
StringBuffer msg = new StringBuffer();
buildString(msg, 0);
return msg.toString();
}
public void buildString(StringBuffer msg, int level) throws XMLStreamException {
for (int i = 0; i < level; i++) {
msg.append('\t');
}
QName node = getNodeName();
String prefix = node.getPrefix();
if (null != prefix && prefix.length() > 0) {
msg.append(node.getPrefix());
msg.append(".");
}
msg.append(node.getLocalPart());
msg.append(":");
String value = null;
value = getNodeValue();
if (null != value) {
msg.append(value);
msg.append("\n");
return;
}
Iterator<ObjectNode> iter = getChildrenIterator();
if (!iter.hasNext()) {
msg.append("{}\n");
return;
}
msg.append("{\n");
while (iter.hasNext()) {
ObjectNodeImpl child = (ObjectNodeImpl)iter.next();
child.buildString(msg, level + 1);
}
for (int i = 0; i < level; i++) {
msg.append('\t');
}
msg.append("}\n");
}
@Override
public String toString() {
return getNodeName().toString();
}
private boolean sameQName(QName n1, QName n2) {
if (null == n1) {
return null == n2;
}
return equals(n1.getLocalPart(), n2.getLocalPart())
&& equals(n1.getNamespaceURI(), n2.getNamespaceURI());
}
private boolean equals(Object o1, Object o2) {
if (null == o1) {
return null == o2;
}
return o1.equals(o2);
}
/**
* Sets m_isAttribute flag from element name. In NV and JSON format, we prefix "@" in element name to indicate
* it is an attribute. This logic is to based on the "@" mark to set m_isAttribute and removes "@" from element name.
*/
private void handleAtMarkedElementName() {
String localName = m_name.getLocalPart();
boolean hasAtMark = (localName != null && localName.startsWith(BindingConstants.ATTRIBUTE_MARK));
if (hasAtMark) {
localName = localName.substring(1);
m_name = new QName(m_name.getNamespaceURI(), localName, m_name.getPrefix());
}
m_isAttribute = (m_isAttribute || hasAtMark);
}
}