/*******************************************************************************
* 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.impl.parser.objectnode;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import org.ebayopensource.turmeric.runtime.binding.impl.parser.ChildNodeStreamingIterator;
import org.ebayopensource.turmeric.runtime.binding.objectnode.ObjectNode;
import org.ebayopensource.turmeric.runtime.binding.objectnode.StreamableObjectNode;
import org.ebayopensource.turmeric.runtime.binding.objectnode.impl.ObjectNodeImpl;
import org.ebayopensource.turmeric.runtime.binding.utils.XMLStreamReaderUtils;
/**
* @author wdeng
*
*/
public class StreamableObjectNodeImpl extends ObjectNodeImpl
implements StreamableObjectNode {
private ObjectNodeStreamReader m_context;
private boolean m_built;
private int m_childIndex; // last index of child returned by next().
private String[] m_namespacePairs; // List of prefix1, nsURI1, prefix2, nsURI2...
public StreamableObjectNodeImpl(ObjectNodeStreamReader context) {
this(ROOT_NODE_QNAME, null, context);
}
public StreamableObjectNodeImpl(QName name, ObjectNode parent, ObjectNodeStreamReader context) {
super(name, parent);
m_context = context;
m_built = false;
m_childIndex = -1;
}
public StreamableObjectNode nextChild() throws XMLStreamException {
// Finishes the current child if it is not in done stage.
// Read the next start tag and fill in the name and attributes.
// But leaves the childrens and end tags to be processed later.
//
if (m_childIndex + 1 < m_children.size()) {
return (StreamableObjectNodeImpl) m_children.get(m_childIndex++);
}
if (!m_context.allowNodeBuilding()) {
return null;
}
StreamableObjectNodeImpl currentChild = null;
if (m_childIndex >= 0 && m_childIndex < m_children.size()) {
currentChild = (StreamableObjectNodeImpl)m_children.get(m_childIndex);
}
boolean currentChildBuilt = null == currentChild || currentChild.m_built;
int event = m_context.getCurrentRawEvent();
if (event == XMLStreamConstants.START_DOCUMENT) {
event = m_context.nextRawEvent();
if (event == XMLStreamConstants.END_DOCUMENT) {
completeNodeBuilding();
return null;
}
}
if (event == XMLStreamConstants.CHARACTERS && currentChildBuilt) {
buildNodeValue();
}
if (!currentChildBuilt) {
fullyBuildNode(currentChild);
}
event = m_context.getCurrentRawEvent();
if (event == XMLStreamConstants.END_ELEMENT) {
completeNodeBuilding();
return null;
}
StreamableObjectNodeImpl child = null;
event = m_context.getCurrentRawEvent();
if (!m_built && event == XMLStreamConstants.START_ELEMENT) {
child = createNewChild();
addChild(child);
m_childIndex++;
}
return child;
}
@Override
public ObjectNode cloneNode() throws XMLStreamException {
if (!m_built) {
if (m_context.allowNodeBuilding()) {
fullyBuildNode(this);
} else {
throw new IllegalStateException("Node building is not allowed");
}
}
return super.cloneNode();
}
@Override
public ObjectNode getChildNode(int index) throws XMLStreamException {
if (!m_built) {
ObjectNode node = this;
while (node != null && m_childIndex != index) {
node = nextChild();
}
}
return super.getChildNode(index);
}
@Override
public ObjectNode getChildNode(QName name, int index) throws XMLStreamException {
if (!m_built) {
if (!m_context.allowNodeBuilding()) {
throw new IllegalStateException("Node building is not allowed");
}
fullyBuildNode(this);
}
return super.getChildNode(name, index);
}
@Override
public boolean hasChildNodes() throws XMLStreamException {
if (m_children.size() > 0) {
return true;
}
if (!m_built) {
if (!m_context.allowNodeBuilding()) {
throw new IllegalStateException("Node building is not allowed");
}
nextChild();
}
return super.hasChildNodes();
}
@Override
public List<ObjectNode> getChildNodes() throws XMLStreamException {
if (!m_built) {
if (!m_context.allowNodeBuilding()) {
throw new IllegalStateException("Node building is not allowed");
}
fullyBuildNode(this);
}
return super.getChildNodes();
}
@Override
public List<ObjectNode> getChildNodes(QName name) throws XMLStreamException {
if (!m_built) {
if (!m_context.allowNodeBuilding()) {
throw new IllegalStateException("Node building is not allowed");
}
fullyBuildNode(this);
}
return super.getChildNodes(name);
}
@Override
public Iterator<ObjectNode> getChildrenIterator() throws XMLStreamException {
if (m_children.size() <= 0 && !m_built) {
nextChild();
}
return new ChildNodeStreamingIterator(this);
}
@Override
public String getNodeValue() {
return super.getNodeValue();
}
@Override
public int getChildNodesSize() throws XMLStreamException {
if (!m_built) {
if (!m_context.allowNodeBuilding()) {
throw new IllegalStateException("Node building is not allowed");
}
fullyBuildNode(this);
}
return super.getChildNodesSize();
}
public int getNamespaceCount() {
if (null == m_namespacePairs) {
return 0;
}
return m_namespacePairs.length/2;
}
public String getNamespacePrefix(int index) {
if (null == m_namespacePairs) {
return null;
}
return m_namespacePairs[2*index];
}
public String getNamespaceURI(int index) {
if (null == m_namespacePairs) {
return null;
}
return m_namespacePairs[2*index + 1];
}
public String getNamespaceURI(String prefix) {
if (null == m_namespacePairs) {
return null;
}
for (int i=0; i<m_namespacePairs.length/2; i++) {
if (m_namespacePairs[2*i].equals(prefix)) {
return m_namespacePairs[2*i + 1];
}
}
return null;
}
Iterator<ObjectNode> getIteratorForBuiltChildren() throws XMLStreamException {
return super.getChildrenIterator();
}
boolean isBuilding() {
return false == m_built;
}
private void buildNodeValue() throws XMLStreamException {
StringBuilder buf = new StringBuilder();
do {
buf.append(m_context.getRawText());
m_location = m_context.getRawLocation();
} while (m_context.nextRawEvent() == XMLStreamConstants.CHARACTERS);
setNodeValue(buf.toString());
}
private void fullyBuildNode(StreamableObjectNodeImpl child) throws XMLStreamException {
StreamableObjectNodeImpl node = (StreamableObjectNodeImpl)child.nextChild();
while (node != null && !node.m_built) {
m_location = m_context.getRawLocation();
node = (StreamableObjectNodeImpl)child.nextChild();
}
}
/**
* Completes the building of the callee node by complete the end tag.
*
*/
private void completeNodeBuilding() throws XMLStreamException {
int event = m_context.getCurrentRawEvent();
while (!m_built) {
switch (event) {
case XMLStreamConstants.CHARACTERS:
buildNodeValue();
break;
case XMLStreamConstants.END_DOCUMENT:
m_built = true;
m_location = m_context.getRawLocation();
break;
case XMLStreamConstants.END_ELEMENT:
m_built = true;
m_location = m_context.getRawLocation();
event = m_context.nextRawEvent();
break;
case XMLStreamConstants.START_ELEMENT:
createNewChild();
break;
default:
throw new XMLStreamException("Unexpected xml stream event '"
+ XMLStreamReaderUtils.xmlStreamReaderEventName(event)
+ "'.");
}
}
}
private StreamableObjectNodeImpl createNewChild() throws XMLStreamException {
// create a new child.
m_location = m_context.getRawLocation();
int event = m_context.getCurrentRawEvent();
if (event != XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException("Unexpected xml stream event. expecting '"
+ XMLStreamReaderUtils.xmlStreamReaderEventName(XMLStreamConstants.START_ELEMENT)
+ "', but got '"
+ XMLStreamReaderUtils.xmlStreamReaderEventName(event)
+ "'.");
}
QName name = m_context.getRawName();
StreamableObjectNodeImpl newChild = new StreamableObjectNodeImpl(name, this, m_context);
int nsCnt = m_context.getRawNamespaceCount();
if (nsCnt > 0) {
newChild.m_namespacePairs = new String[nsCnt * 2];
String[] msPairs = newChild.m_namespacePairs;
for (int i=0; i<nsCnt; i++) {
String prefix = fixNull(m_context.getRawNamespacePrefix(i));
msPairs[2*i] = prefix;
String uri = fixNull(m_context.getRawNamespaceURI(i));
msPairs[2*i + 1] = uri;
}
}
int attrCnt = m_context.getRawAttributeCount();
for (int i=0; i<attrCnt; i++) {
QName attrName = new QName(m_context.getRawAttributeNamespace(i),
m_context.getRawAttributeLocalName(i), m_context.getRawAttributePrefix(i));
String attrValue = m_context.getRawAttributeValue(i);
ObjectNodeImpl attrNode = new ObjectNodeImpl(attrName, newChild, true);
attrNode.setNodeValue(attrValue);
newChild.addAttribute(attrNode);
}
m_location = m_context.getRawLocation();
event = m_context.nextRawEvent();
if (event == XMLStreamConstants.CHARACTERS) {
newChild.buildNodeValue();
}
return newChild;
}
private static String fixNull(String s) {
if (s == null) {
return "";
}
return s;
}
}