blob: 7d922d7a34fef345ae385955dcf61e3d7e2dd732 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axiom.om.impl.dom;
import org.apache.axiom.om.OMComment;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMDocType;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.om.OMProcessingInstruction;
import org.apache.axiom.om.OMSourcedElement;
import org.apache.axiom.om.OMText;
import org.apache.axiom.om.OMXMLParserWrapper;
import org.apache.axiom.om.OMXMLStreamReaderConfiguration;
import org.apache.axiom.om.impl.OMNodeEx;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.impl.common.OMChildrenLocalNameIterator;
import org.apache.axiom.om.impl.common.OMChildrenNamespaceIterator;
import org.apache.axiom.om.impl.common.OMChildrenQNameIterator;
import org.apache.axiom.om.impl.common.OMContainerHelper;
import org.apache.axiom.om.impl.common.OMDescendantsIterator;
import org.apache.axiom.om.impl.dom.factory.OMDOMFactory;
import org.apache.axiom.om.impl.jaxp.OMSource;
import org.apache.axiom.om.impl.traverse.OMChildrenIterator;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.sax.SAXSource;
import java.util.Iterator;
public abstract class ParentNode extends NodeImpl implements NodeList {
protected NodeImpl firstChild;
protected NodeImpl lastChild;
/** @param ownerDocument */
protected ParentNode(DocumentImpl ownerDocument, OMFactory factory) {
super(ownerDocument, factory);
}
protected ParentNode(OMFactory factory) {
super(factory);
}
// /
// /OMContainer methods
// /
public OMXMLParserWrapper getBuilder() {
return this.builder;
}
void internalAppendChild(NodeImpl node) {
insertBefore(node, null, false);
}
public void addChild(OMNode omNode) {
if (omNode.getOMFactory() instanceof OMDOMFactory) {
insertBefore((Node)omNode, null, false);
} else {
addChild(importNode(omNode));
}
}
public void buildNext() {
if (builder != null) {
if (!builder.isCompleted()) {
builder.next();
} else {
this.setComplete(true);
}
}
}
public Iterator getChildren() {
return new OMChildrenIterator(getFirstOMChild());
}
public Iterator getDescendants(boolean includeSelf) {
return new OMDescendantsIterator((OMContainer)this, includeSelf);
}
/**
* Returns an iterator of child nodes having a given qname.
*
* @see org.apache.axiom.om.OMContainer#getChildrenWithName (javax.xml.namespace.QName)
*/
public Iterator getChildrenWithName(QName elementQName) throws OMException {
return new OMChildrenQNameIterator(getFirstOMChild(), elementQName);
}
public Iterator getChildrenWithLocalName(String localName) {
return new OMChildrenLocalNameIterator(getFirstOMChild(),
localName);
}
public Iterator getChildrenWithNamespaceURI(String uri) {
return new OMChildrenNamespaceIterator(getFirstOMChild(),
uri);
}
/**
* Returns the first OMElement child node.
*
* @see org.apache.axiom.om.OMContainer#getFirstChildWithName (javax.xml.namespace.QName)
*/
public OMElement getFirstChildWithName(QName elementQName)
throws OMException {
Iterator children = new OMChildrenQNameIterator(getFirstOMChild(),
elementQName);
while (children.hasNext()) {
OMNode node = (OMNode) children.next();
// Return the first OMElement node that is found
if (node instanceof OMElement) {
return (OMElement) node;
}
}
return null;
}
public OMNode getFirstOMChild() {
while ((firstChild == null) && !done) {
buildNext();
}
return (OMNode)firstChild;
}
public OMNode getFirstOMChildIfAvailable() {
return (OMNode)firstChild;
}
public void setFirstChild(OMNode omNode) {
if (firstChild != null) {
((OMNodeEx) omNode).setParent((OMContainer)this);
}
this.firstChild = (NodeImpl) omNode;
}
/**
* Forcefully set the last child
* @param omNode
*/
public void setLastChild(OMNode omNode) {
this.lastChild = (NodeImpl) omNode;
}
// /
// /DOM Node methods
// /
public final NodeList getChildNodes() {
return this;
}
public final int getLength() {
int count = 0;
Node child = getFirstChild();
while (child != null) {
count++;
child = child.getNextSibling();
}
return count;
}
public final Node item(int index) {
int count = 0;
Node child = getFirstChild();
while (child != null) {
if (count == index) {
return child;
} else {
child = child.getNextSibling();
}
count++;
}
return null;
}
public Node getFirstChild() {
return (Node) this.getFirstOMChild();
}
public Node getLastChild() {
if (!this.done) {
this.build();
}
return this.lastChild;
}
public boolean hasChildNodes() {
while ((firstChild == null) && !done) {
buildNext();
}
return this.firstChild != null;
}
public final Node appendChild(Node newChild) throws DOMException {
return insertBefore(newChild, null);
}
/**
* Inserts newChild before the refChild. If the refChild is null then the newChild is made the
* last child.
*/
public Node insertBefore(Node newChild, Node refChild) throws DOMException {
return insertBefore(newChild, refChild, true);
}
private Node insertBefore(Node newChild, Node refChild, boolean useDomSemantics) {
NodeImpl newDomChild = (NodeImpl) newChild;
NodeImpl refDomChild = (NodeImpl) refChild;
if (useDomSemantics) {
checkSameOwnerDocument(newDomChild);
}
if (isAncestorOrSelf(newChild)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
DOMException.HIERARCHY_REQUEST_ERR, null));
}
if (newDomChild.parentNode() != null) {
//If the newChild is already in the tree remove it
newDomChild.parentNode().removeChild(newDomChild);
}
if (this instanceof Document) {
if (newDomChild instanceof ElementImpl) {
if (((DocumentImpl) this).getOMDocumentElement(false) != null) {
// Throw exception since there cannot be two document elements
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
DOMException.HIERARCHY_REQUEST_ERR, null));
}
if (newDomChild.parentNode() == null) {
newDomChild.setParent(this, useDomSemantics);
}
} else if (!(newDomChild instanceof CommentImpl
|| newDomChild instanceof ProcessingInstructionImpl
|| newDomChild instanceof DocumentFragmentImpl
|| newDomChild instanceof DocumentTypeImpl)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
DOMException.HIERARCHY_REQUEST_ERR, null));
}
}
if (refChild == null) { // Append the child to the end of the list
// if there are no children
if (this.lastChild == null && firstChild == null) {
this.lastChild = newDomChild;
this.firstChild = newDomChild;
this.firstChild.isFirstChild(true);
newDomChild.setParent(this, useDomSemantics);
} else {
this.lastChild.internalSetNextSibling(newDomChild);
newDomChild.internalSetPreviousSibling(this.lastChild);
this.lastChild = newDomChild;
this.lastChild.internalSetNextSibling(null);
}
if (newDomChild.parentNode() == null) {
newDomChild.setParent(this, useDomSemantics);
}
} else {
Iterator children = this.getChildren();
boolean found = false;
while (children.hasNext()) {
NodeImpl tempNode = (NodeImpl) children.next();
if (tempNode.equals(refChild)) {
// RefChild found
if (this.firstChild == tempNode) { // If the refChild is the
// first child
if (newChild instanceof DocumentFragmentImpl) {
// The new child is a DocumentFragment
DocumentFragmentImpl docFrag =
(DocumentFragmentImpl) newChild;
NodeImpl child = docFrag.firstChild;
while (child != null) {
child.setParent(this, useDomSemantics);
child = child.internalGetNextSibling();
}
this.firstChild = docFrag.firstChild;
docFrag.lastChild.internalSetNextSibling(refDomChild);
refDomChild.internalSetPreviousSibling(docFrag.lastChild.internalGetNextSibling());
docFrag.firstChild = null;
docFrag.lastChild = null;
} else {
// Make the newNode the first Child
this.firstChild = newDomChild;
newDomChild.internalSetNextSibling(refDomChild);
refDomChild.internalSetPreviousSibling(newDomChild);
this.firstChild.isFirstChild(true);
refDomChild.isFirstChild(false);
newDomChild.internalSetPreviousSibling(null); // Just to be
// sure :-)
}
} else { // If the refChild is not the fist child
NodeImpl previousNode = refDomChild.internalGetPreviousSibling();
if (newChild instanceof DocumentFragmentImpl) {
// the newChild is a document fragment
DocumentFragmentImpl docFrag =
(DocumentFragmentImpl) newChild;
NodeImpl child = docFrag.firstChild;
while (child != null) {
child.setParent(this, useDomSemantics);
child = child.internalGetNextSibling();
}
previousNode.internalSetNextSibling(docFrag.firstChild);
docFrag.firstChild.internalSetPreviousSibling(previousNode);
docFrag.lastChild.internalSetNextSibling(refDomChild);
refDomChild.internalSetPreviousSibling(docFrag.lastChild);
docFrag.firstChild = null;
docFrag.lastChild = null;
} else {
previousNode.internalSetNextSibling(newDomChild);
newDomChild.internalSetPreviousSibling(previousNode);
newDomChild.internalSetNextSibling(refDomChild);
refDomChild.internalSetPreviousSibling(newDomChild);
}
}
found = true;
break;
}
}
if (!found) {
throw new DOMException(DOMException.NOT_FOUND_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
DOMException.NOT_FOUND_ERR, null));
}
if (newDomChild.parentNode() == null) {
newDomChild.setParent(this, useDomSemantics);
}
}
if (!newDomChild.isComplete() && !(newDomChild instanceof OMSourcedElement)) {
setComplete(false);
}
return newChild;
}
/** Replaces the oldChild with the newChild. */
public final Node replaceChild(Node newChild, Node oldChild) throws DOMException {
NodeImpl newDomChild = (NodeImpl) newChild;
NodeImpl oldDomChild = (NodeImpl) oldChild;
if (newChild == null) {
return this.removeChild(oldChild);
}
if (isAncestorOrSelf(newChild)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
DOMException.HIERARCHY_REQUEST_ERR, null));
}
checkSameOwnerDocument(newDomChild);
Iterator children = this.getChildren();
boolean found = false;
while (!found && children.hasNext()) {
NodeImpl tempNode = (NodeImpl) children.next();
if (tempNode.equals(oldChild)) {
if (newChild instanceof DocumentFragmentImpl) {
DocumentFragmentImpl docFrag =
(DocumentFragmentImpl) newDomChild;
NodeImpl child = (NodeImpl) docFrag.getFirstChild();
this.replaceChild(child, oldChild);
//set the parent of all kids to me
while(child != null) {
child.setParent(this, true);
child = child.internalGetNextSibling();
}
this.lastChild = (NodeImpl)docFrag.getLastChild();
} else {
if (this.firstChild == oldDomChild) {
if (this.firstChild.internalGetNextSibling() != null) {
this.firstChild.internalGetNextSibling().internalSetPreviousSibling(newDomChild);
newDomChild.internalSetNextSibling(this.firstChild.internalGetNextSibling());
}
//Cleanup the current first child
this.firstChild.setParent(null, true);
this.firstChild.internalSetNextSibling(null);
//Set the new first child
this.firstChild = newDomChild;
} else {
newDomChild.internalSetNextSibling(oldDomChild.internalGetNextSibling());
newDomChild.internalSetPreviousSibling(oldDomChild.internalGetPreviousSibling());
oldDomChild.internalGetPreviousSibling().internalSetNextSibling(newDomChild);
// If the old child is not the last
if (oldDomChild.internalGetNextSibling() != null) {
oldDomChild.internalGetNextSibling().internalSetPreviousSibling(newDomChild);
} else {
this.lastChild = newDomChild;
}
}
newDomChild.setParent(this, true);
}
found = true;
// remove the old child's references to this tree
oldDomChild.internalSetNextSibling(null);
oldDomChild.internalSetPreviousSibling(null);
oldDomChild.setParent(null, true);
}
}
if (!found)
throw new DOMException(DOMException.NOT_FOUND_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN, DOMException.NOT_FOUND_ERR,
null));
return oldChild;
}
/** Removes the given child from the DOM Tree. */
public final Node removeChild(Node oldChild) throws DOMException {
if (oldChild.getParentNode() == this) {
((NodeImpl)oldChild).detach(true);
return oldChild;
} else {
throw new DOMException(DOMException.NOT_FOUND_ERR,
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN, DOMException.NOT_FOUND_ERR,
null));
}
}
/**
* Checks if the given node is an ancestor (or identical) to this node.
*
* @param node
* the node to check
* @return <code>true</code> if the node is an ancestor or indentical to this node
*/
private boolean isAncestorOrSelf(Node node) {
Node currentNode = this;
do {
if (currentNode == node) {
return true;
}
currentNode = currentNode.getParentNode();
} while (currentNode != null);
return false;
}
public Node cloneNode(boolean deep) {
ParentNode newnode = (ParentNode) super.cloneNode(deep);
// set parent and owner document
newnode.setParent(null, true);
// Need to break the association w/ original kids
newnode.firstChild = null;
newnode.lastChild = null;
// Then, if deep, clone the kids too.
if (deep) {
for (Node child = getFirstChild(); child != null;
child = child.getNextSibling()) {
newnode.appendChild(child.cloneNode(true));
}
}
return newnode;
}
/**
* This method is intended only to be used by Axiom intenals when merging Objects from different
* Axiom implementations to the DOOM implementation.
*
* @param child
*/
protected OMNode importNode(OMNode child) {
int type = child.getType();
switch (type) {
case (OMNode.ELEMENT_NODE): {
OMElement childElement = (OMElement) child;
OMElement newElement = (new StAXOMBuilder(this.factory,
childElement.getXMLStreamReader()))
.getDocumentElement();
newElement.build();
return (OMNode)ownerDocument().importNode((Element) newElement,
true);
}
case (OMNode.TEXT_NODE): {
OMText importedText = (OMText) child;
OMText newText;
if (importedText.isBinary()) {
boolean isOptimize = importedText.isOptimized();
newText = this.factory.createOMText(importedText
.getDataHandler(), isOptimize);
} else if (importedText.isCharacters()) {
newText = new TextImpl((DocumentImpl) this.getOwnerDocument(),
importedText.getTextCharacters(), this.factory);
} else {
newText = new TextImpl((DocumentImpl) this.getOwnerDocument(),
importedText.getText(), this.factory);
}
return newText;
}
case (OMNode.PI_NODE): {
OMProcessingInstruction importedPI = (OMProcessingInstruction) child;
OMProcessingInstruction newPI = this.factory
.createOMProcessingInstruction((OMContainer)this,
importedPI.getTarget(),
importedPI.getValue());
return newPI;
}
case (OMNode.COMMENT_NODE): {
OMComment importedComment = (OMComment) child;
OMComment newComment = this.factory.createOMComment((OMContainer)this,
importedComment.getValue());
DocumentImpl doc;
if (this instanceof DocumentImpl) {
doc = (DocumentImpl) this;
} else {
doc = (DocumentImpl) getOwnerDocument();
}
newComment = new CommentImpl(doc, importedComment.getValue(),
this.factory);
return newComment;
}
case (OMNode.DTD_NODE): {
OMDocType importedDocType = (OMDocType) child;
OMDocType newDocType = this.factory.createOMDocType((OMContainer)this,
importedDocType.getValue());
return newDocType;
}
default: {
throw new UnsupportedOperationException(
"Not Implemented Yet for the given node type");
}
}
}
public String getTextContent() throws DOMException {
Node child = getFirstChild();
if (child != null) {
Node next = child.getNextSibling();
if (next == null) {
return hasTextContent(child) ? ((NodeImpl)child).getTextContent() : "";
}
StringBuffer buf = new StringBuffer();
getTextContent(buf);
return buf.toString();
} else {
return "";
}
}
void getTextContent(StringBuffer buf) throws DOMException {
Node child = getFirstChild();
while (child != null) {
if (hasTextContent(child)) {
((NodeImpl)child).getTextContent(buf);
}
child = child.getNextSibling();
}
}
// internal method returning whether to take the given node's text content
private static boolean hasTextContent(Node child) {
return child.getNodeType() != Node.COMMENT_NODE &&
child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE /* &&
(child.getNodeType() != Node.TEXT_NODE ||
((TextImpl) child).isIgnorableWhitespace() == false)*/;
}
public void setTextContent(String textContent) throws DOMException {
// get rid of any existing children
// TODO: there is probably a better way to remove all children
Node child;
while ((child = getFirstChild()) != null) {
removeChild(child);
}
// create a Text node to hold the given content
if (textContent != null && textContent.length() != 0) {
addChild(factory.createOMText(textContent));
}
}
public XMLStreamReader getXMLStreamReaderWithoutCaching() {
return getXMLStreamReader(false);
}
public XMLStreamReader getXMLStreamReader() {
return getXMLStreamReader(true);
}
public XMLStreamReader getXMLStreamReader(boolean cache) {
return OMContainerHelper.getXMLStreamReader((OMContainer)this, cache);
}
public XMLStreamReader getXMLStreamReader(boolean cache, OMXMLStreamReaderConfiguration configuration) {
return OMContainerHelper.getXMLStreamReader((OMContainer)this, cache, configuration);
}
public SAXSource getSAXSource(boolean cache) {
return new OMSource((OMContainer)this);
}
void notifyChildComplete() {
if (!this.done && builder == null) {
Iterator iterator = getChildren();
while (iterator.hasNext()) {
OMNode node = (OMNode) iterator.next();
if (!node.isComplete()) {
return;
}
}
this.setComplete(true);
}
}
void normalize(DOMConfigurationImpl config) {
OMNode child = getFirstOMChild();
while (child != null) {
((NodeImpl)child).normalize(config);
child = child.getNextOMSibling();
}
}
}