/*
* Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
*
* This software is open source.
* See the bottom of this file for the licence.
*/
package org.dom4j.tree;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.CDATA;
import org.dom4j.CharacterData;
import org.dom4j.Comment;
import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.Entity;
import org.dom4j.IllegalAddException;
import org.dom4j.Namespace;
import org.dom4j.Node;
import org.dom4j.ProcessingInstruction;
import org.dom4j.QName;
import org.dom4j.Text;
import org.dom4j.Visitor;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.xml.sax.Attributes;
/**
* <p>
* <code>AbstractElement</code> is an abstract base class for tree
* implementors to use for implementation inheritence.
* </p>
*
* @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
* @version $Revision: 1.80 $
*/
public abstract class AbstractElement extends AbstractBranch implements
org.dom4j.Element {
/** The <code>DocumentFactory</code> instance used by default */
private static final DocumentFactory DOCUMENT_FACTORY = DocumentFactory
.getInstance();
protected static final List EMPTY_LIST = Collections.EMPTY_LIST;
protected static final Iterator EMPTY_ITERATOR = EMPTY_LIST.iterator();
protected static final boolean VERBOSE_TOSTRING = false;
protected static final boolean USE_STRINGVALUE_SEPARATOR = false;
public AbstractElement() {
}
public short getNodeType() {
return ELEMENT_NODE;
}
public boolean isRootElement() {
Document document = getDocument();
if (document != null) {
Element root = document.getRootElement();
if (root == this) {
return true;
}
}
return false;
}
public void setName(String name) {
setQName(getDocumentFactory().createQName(name));
}
public void setNamespace(Namespace namespace) {
setQName(getDocumentFactory().createQName(getName(), namespace));
}
/**
* Returns the XPath expression to match this Elements name which is
* getQualifiedName() if there is a namespace prefix defined or if no
* namespace is present then it is getName() or if a namespace is defined
* with no prefix then the expression is [name()='X'] where X = getName().
*
* @return DOCUMENT ME!
*/
public String getXPathNameStep() {
String uri = getNamespaceURI();
if ((uri == null) || (uri.length() == 0)) {
return getName();
}
String prefix = getNamespacePrefix();
if ((prefix == null) || (prefix.length() == 0)) {
return "*[name()='" + getName() + "']";
}
return getQualifiedName();
}
public String getPath(Element context) {
if (this == context) {
return ".";
}
Element parent = getParent();
if (parent == null) {
return "/" + getXPathNameStep();
} else if (parent == context) {
return getXPathNameStep();
}
return parent.getPath(context) + "/" + getXPathNameStep();
}
public String getUniquePath(Element context) {
Element parent = getParent();
if (parent == null) {
return "/" + getXPathNameStep();
}
StringBuffer buffer = new StringBuffer();
if (parent != context) {
buffer.append(parent.getUniquePath(context));
buffer.append("/");
}
buffer.append(getXPathNameStep());
List mySiblings = parent.elements(getQName());
if (mySiblings.size() > 1) {
int idx = mySiblings.indexOf(this);
if (idx >= 0) {
buffer.append("[");
buffer.append(Integer.toString(++idx));
buffer.append("]");
}
}
return buffer.toString();
}
public String asXML() {
try {
StringWriter out = new StringWriter();
XMLWriter writer = new XMLWriter(out, new OutputFormat());
writer.write(this);
writer.flush();
return out.toString();
} catch (IOException e) {
throw new RuntimeException("IOException while generating "
+ "textual representation: " + e.getMessage());
}
}
public void write(Writer out) throws IOException {
XMLWriter writer = new XMLWriter(out, new OutputFormat());
writer.write(this);
}
/**
* <p>
* <code>accept</code> method is the <code>Visitor Pattern</code>
* method.
* </p>
*
* @param visitor
* <code>Visitor</code> is the visitor.
*/
public void accept(Visitor visitor) {
visitor.visit(this);
// visit attributes
for (int i = 0, size = attributeCount(); i < size; i++) {
Attribute attribute = attribute(i);
visitor.visit(attribute);
}
// visit content
for (int i = 0, size = nodeCount(); i < size; i++) {
Node node = node(i);
node.accept(visitor);
}
}
public String toString() {
String uri = getNamespaceURI();
if ((uri != null) && (uri.length() > 0)) {
if (VERBOSE_TOSTRING) {
return super.toString() + " [Element: <" + getQualifiedName()
+ " uri: " + uri + " attributes: " + attributeList()
+ " content: " + contentList() + " />]";
} else {
return super.toString() + " [Element: <" + getQualifiedName()
+ " uri: " + uri + " attributes: " + attributeList()
+ "/>]";
}
} else {
if (VERBOSE_TOSTRING) {
return super.toString() + " [Element: <" + getQualifiedName()
+ " attributes: " + attributeList() + " content: "
+ contentList() + " />]";
} else {
return super.toString() + " [Element: <" + getQualifiedName()
+ " attributes: " + attributeList() + "/>]";
}
}
}
// QName methods
// -------------------------------------------------------------------------
public Namespace getNamespace() {
return getQName().getNamespace();
}
public String getName() {
return getQName().getName();
}
public String getNamespacePrefix() {
return getQName().getNamespacePrefix();
}
public String getNamespaceURI() {
return getQName().getNamespaceURI();
}
public String getQualifiedName() {
return getQName().getQualifiedName();
}
public Object getData() {
return getText();
}
public void setData(Object data) {
// ignore this method
}
// Node methods
// -------------------------------------------------------------------------
public Node node(int index) {
if (index >= 0) {
List list = contentList();
if (index >= list.size()) {
return null;
}
Object node = list.get(index);
if (node != null) {
if (node instanceof Node) {
return (Node) node;
} else {
return getDocumentFactory().createText(node.toString());
}
}
}
return null;
}
public int indexOf(Node node) {
return contentList().indexOf(node);
}
public int nodeCount() {
return contentList().size();
}
public Iterator<Node> nodeIterator() {
return contentList().iterator();
}
// Element methods
// -------------------------------------------------------------------------
public Element element(String name) {
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Element) {
Element element = (Element) object;
if (name.equals(element.getName())) {
return element;
}
}
}
return null;
}
public Element element(QName qName) {
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Element) {
Element element = (Element) object;
if (qName.equals(element.getQName())) {
return element;
}
}
}
return null;
}
public Element element(String name, Namespace namespace) {
return element(getDocumentFactory().createQName(name, namespace));
}
public List<Element> elements() {
List list = contentList();
BackedList answer = createResultList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Element) {
answer.addLocal(object);
}
}
return answer;
}
public List<Element> elements(String name) {
List list = contentList();
BackedList answer = createResultList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Element) {
Element element = (Element) object;
if (name.equals(element.getName())) {
answer.addLocal(element);
}
}
}
return answer;
}
public List<Element> elements(QName qName) {
List list = contentList();
BackedList answer = createResultList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Element) {
Element element = (Element) object;
if (qName.equals(element.getQName())) {
answer.addLocal(element);
}
}
}
return answer;
}
public List elements(String name, Namespace namespace) {
return elements(getDocumentFactory().createQName(name, namespace));
}
public Iterator<Element> elementIterator() {
List list = elements();
return list.iterator();
}
public Iterator<Element> elementIterator(String name) {
List list = elements(name);
return list.iterator();
}
public Iterator<Element> elementIterator(QName qName) {
List list = elements(qName);
return list.iterator();
}
public Iterator elementIterator(String name, Namespace ns) {
return elementIterator(getDocumentFactory().createQName(name, ns));
}
// Attribute methods
// -------------------------------------------------------------------------
public List<Attribute> attributes() {
return new ContentListFacade(this, attributeList());
}
public Iterator<Attribute> attributeIterator() {
return attributeList().iterator();
}
public Attribute attribute(int index) {
return (Attribute) attributeList().get(index);
}
public int attributeCount() {
return attributeList().size();
}
public Attribute attribute(String name) {
List list = attributeList();
int size = list.size();
for (int i = 0; i < size; i++) {
Attribute attribute = (Attribute) list.get(i);
if (name.equals(attribute.getName())) {
return attribute;
}
}
return null;
}
public Attribute attribute(QName qName) {
List list = attributeList();
int size = list.size();
for (int i = 0; i < size; i++) {
Attribute attribute = (Attribute) list.get(i);
if (qName.equals(attribute.getQName())) {
return attribute;
}
}
return null;
}
public Attribute attribute(String name, Namespace namespace) {
return attribute(getDocumentFactory().createQName(name, namespace));
}
/**
* This method provides a more optimal way of setting all the attributes on
* an Element particularly for use in {@link org.dom4j.io.SAXReader}.
*
* @param attributes
* DOCUMENT ME!
* @param namespaceStack
* DOCUMENT ME!
* @param noNamespaceAttributes
* DOCUMENT ME!
*/
public void setAttributes(Attributes attributes,
NamespaceStack namespaceStack, boolean noNamespaceAttributes) {
// now lets add all attribute values
int size = attributes.getLength();
if (size > 0) {
DocumentFactory factory = getDocumentFactory();
if (size == 1) {
// allow lazy construction of the List of Attributes
String name = attributes.getQName(0);
if (noNamespaceAttributes || !name.startsWith("xmlns")) {
String attributeURI = attributes.getURI(0);
String attributeLocalName = attributes.getLocalName(0);
String attributeValue = attributes.getValue(0);
QName attributeQName = namespaceStack.getAttributeQName(
attributeURI, attributeLocalName, name);
add(factory.createAttribute(this, attributeQName,
attributeValue));
}
} else {
List list = attributeList(size);
list.clear();
for (int i = 0; i < size; i++) {
// optimised to avoid the call to attribute(QName) to
// lookup an attribute for a given QName
String attributeName = attributes.getQName(i);
if (noNamespaceAttributes
|| !attributeName.startsWith("xmlns")) {
String attributeURI = attributes.getURI(i);
String attributeLocalName = attributes.getLocalName(i);
String attributeValue = attributes.getValue(i);
QName attributeQName = namespaceStack
.getAttributeQName(attributeURI,
attributeLocalName, attributeName);
Attribute attribute = factory.createAttribute(this,
attributeQName, attributeValue);
list.add(attribute);
childAdded(attribute);
}
}
}
}
}
public String attributeValue(String name) {
Attribute attrib = attribute(name);
if (attrib == null) {
return null;
} else {
return attrib.getValue();
}
}
public String attributeValue(QName qName) {
Attribute attrib = attribute(qName);
if (attrib == null) {
return null;
} else {
return attrib.getValue();
}
}
public String attributeValue(String name, String defaultValue) {
String answer = attributeValue(name);
return (answer != null) ? answer : defaultValue;
}
public String attributeValue(QName qName, String defaultValue) {
String answer = attributeValue(qName);
return (answer != null) ? answer : defaultValue;
}
/**
* DOCUMENT ME!
*
* @param name
* DOCUMENT ME!
* @param value
* DOCUMENT ME!
*
* @deprecated As of version 0.5. Please use {@link
* #addAttribute(String,String)} instead. WILL BE REMOVED IN
* dom4j-1.6 !!
*/
public void setAttributeValue(String name, String value) {
addAttribute(name, value);
}
/**
* DOCUMENT ME!
*
* @param qName
* DOCUMENT ME!
* @param value
* DOCUMENT ME!
*
* @deprecated As of version 0.5. Please use {@link
* #addAttribute(String,String)} instead. WILL BE REMOVED IN
* dom4j-1.6 !!
*/
public void setAttributeValue(QName qName, String value) {
addAttribute(qName, value);
}
public void add(Attribute attribute) {
if (attribute.getParent() != null) {
String message = "The Attribute already has an existing parent \""
+ attribute.getParent().getQualifiedName() + "\"";
throw new IllegalAddException(this, attribute, message);
}
if (attribute.getValue() == null) {
// try remove a previous attribute with the same
// name since adding an attribute with a null value
// is equivalent to removing it.
Attribute oldAttribute = attribute(attribute.getQName());
if (oldAttribute != null) {
remove(oldAttribute);
}
} else {
attributeList().add(attribute);
childAdded(attribute);
}
}
public boolean remove(Attribute attribute) {
List list = attributeList();
boolean answer = list.remove(attribute);
if (answer) {
childRemoved(attribute);
} else {
// we may have a copy of the attribute
Attribute copy = attribute(attribute.getQName());
if (copy != null) {
list.remove(copy);
answer = true;
}
}
return answer;
}
// Processing instruction API
// -------------------------------------------------------------------------
public List<ProcessingInstruction> processingInstructions() {
List list = contentList();
BackedList answer = createResultList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof ProcessingInstruction) {
answer.addLocal(object);
}
}
return answer;
}
public List<ProcessingInstruction> processingInstructions(String target) {
List list = contentList();
BackedList answer = createResultList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof ProcessingInstruction) {
ProcessingInstruction pi = (ProcessingInstruction) object;
if (target.equals(pi.getName())) {
answer.addLocal(pi);
}
}
}
return answer;
}
public ProcessingInstruction processingInstruction(String target) {
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof ProcessingInstruction) {
ProcessingInstruction pi = (ProcessingInstruction) object;
if (target.equals(pi.getName())) {
return pi;
}
}
}
return null;
}
public boolean removeProcessingInstruction(String target) {
List list = contentList();
for (Iterator iter = list.iterator(); iter.hasNext();) {
Object object = iter.next();
if (object instanceof ProcessingInstruction) {
ProcessingInstruction pi = (ProcessingInstruction) object;
if (target.equals(pi.getName())) {
iter.remove();
return true;
}
}
}
return false;
}
// Content Model methods
// -------------------------------------------------------------------------
public Node getXPathResult(int index) {
Node answer = node(index);
if ((answer != null) && !answer.supportsParent()) {
return answer.asXPathResult(this);
}
return answer;
}
public Element addAttribute(String name, String value) {
// adding a null value is equivalent to removing the attribute
Attribute attribute = attribute(name);
if (value != null) {
if (attribute == null) {
add(getDocumentFactory().createAttribute(this, name, value));
} else if (attribute.isReadOnly()) {
remove(attribute);
add(getDocumentFactory().createAttribute(this, name, value));
} else {
attribute.setValue(value);
}
} else if (attribute != null) {
remove(attribute);
}
return this;
}
public Element addAttribute(QName qName, String value) {
// adding a null value is equivalent to removing the attribute
Attribute attribute = attribute(qName);
if (value != null) {
if (attribute == null) {
add(getDocumentFactory().createAttribute(this, qName, value));
} else if (attribute.isReadOnly()) {
remove(attribute);
add(getDocumentFactory().createAttribute(this, qName, value));
} else {
attribute.setValue(value);
}
} else if (attribute != null) {
remove(attribute);
}
return this;
}
public Element addCDATA(String cdata) {
CDATA node = getDocumentFactory().createCDATA(cdata);
addNewNode(node);
return this;
}
public Element addComment(String comment) {
Comment node = getDocumentFactory().createComment(comment);
addNewNode(node);
return this;
}
public Element addElement(String name) {
DocumentFactory factory = getDocumentFactory();
int index = name.indexOf(":");
String prefix = "";
String localName = name;
Namespace namespace = null;
if (index > 0) {
prefix = name.substring(0, index);
localName = name.substring(index + 1);
namespace = getNamespaceForPrefix(prefix);
if (namespace == null) {
throw new IllegalAddException("No such namespace prefix: "
+ prefix + " is in scope on: " + this
+ " so cannot add element: " + name);
}
} else {
namespace = getNamespaceForPrefix("");
}
Element node;
if (namespace != null) {
QName qname = factory.createQName(localName, namespace);
node = factory.createElement(qname);
} else {
node = factory.createElement(name);
}
addNewNode(node);
return node;
}
public Element addEntity(String name, String text) {
Entity node = getDocumentFactory().createEntity(name, text);
addNewNode(node);
return this;
}
public Element addNamespace(String prefix, String uri) {
Namespace node = getDocumentFactory().createNamespace(prefix, uri);
addNewNode(node);
return this;
}
public Element addProcessingInstruction(String target, String data) {
ProcessingInstruction node = getDocumentFactory()
.createProcessingInstruction(target, data);
addNewNode(node);
return this;
}
public Element addProcessingInstruction(String target, Map data) {
ProcessingInstruction node = getDocumentFactory()
.createProcessingInstruction(target, data);
addNewNode(node);
return this;
}
public Element addText(String text) {
Text node = getDocumentFactory().createText(text);
addNewNode(node);
return this;
}
// polymorphic node methods
public void add(Node node) {
switch (node.getNodeType()) {
case ELEMENT_NODE:
add((Element) node);
break;
case ATTRIBUTE_NODE:
add((Attribute) node);
break;
case TEXT_NODE:
add((Text) node);
break;
case CDATA_SECTION_NODE:
add((CDATA) node);
break;
case ENTITY_REFERENCE_NODE:
add((Entity) node);
break;
case PROCESSING_INSTRUCTION_NODE:
add((ProcessingInstruction) node);
break;
case COMMENT_NODE:
add((Comment) node);
break;
/*
* XXXX: to do! case DOCUMENT_TYPE_NODE: add((DocumentType) node);
* break;
*/
case NAMESPACE_NODE:
add((Namespace) node);
break;
default:
invalidNodeTypeAddException(node);
}
}
public boolean remove(Node node) {
switch (node.getNodeType()) {
case ELEMENT_NODE:
return remove((Element) node);
case ATTRIBUTE_NODE:
return remove((Attribute) node);
case TEXT_NODE:
return remove((Text) node);
case CDATA_SECTION_NODE:
return remove((CDATA) node);
case ENTITY_REFERENCE_NODE:
return remove((Entity) node);
case PROCESSING_INSTRUCTION_NODE:
return remove((ProcessingInstruction) node);
case COMMENT_NODE:
return remove((Comment) node);
/*
* case DOCUMENT_TYPE_NODE: return remove((DocumentType) node);
*/
case NAMESPACE_NODE:
return remove((Namespace) node);
default:
return false;
}
}
// typesafe versions using node classes
public void add(CDATA cdata) {
addNode(cdata);
}
public void add(Comment comment) {
addNode(comment);
}
public void add(Element element) {
addNode(element);
}
public void add(Entity entity) {
addNode(entity);
}
public void add(Namespace namespace) {
addNode(namespace);
}
public void add(ProcessingInstruction pi) {
addNode(pi);
}
public void add(Text text) {
addNode(text);
}
public boolean remove(CDATA cdata) {
return removeNode(cdata);
}
public boolean remove(Comment comment) {
return removeNode(comment);
}
public boolean remove(Element element) {
return removeNode(element);
}
public boolean remove(Entity entity) {
return removeNode(entity);
}
public boolean remove(Namespace namespace) {
return removeNode(namespace);
}
public boolean remove(ProcessingInstruction pi) {
return removeNode(pi);
}
public boolean remove(Text text) {
return removeNode(text);
}
// Helper methods
// -------------------------------------------------------------------------
public boolean hasMixedContent() {
List content = contentList();
if ((content == null) || content.isEmpty() || (content.size() < 2)) {
return false;
}
Class prevClass = null;
for (Iterator iter = content.iterator(); iter.hasNext();) {
Object object = iter.next();
Class newClass = object.getClass();
if (newClass != prevClass) {
if (prevClass != null) {
return true;
}
prevClass = newClass;
}
}
return false;
}
public boolean isTextOnly() {
List content = contentList();
if ((content == null) || content.isEmpty()) {
return true;
}
for (Iterator iter = content.iterator(); iter.hasNext();) {
Object object = iter.next();
if (!(object instanceof CharacterData)
&& !(object instanceof String)) {
return false;
}
}
return true;
}
public void setText(String text) {
/* remove all text nodes */
List allContent = contentList();
if (allContent != null) {
Iterator it = allContent.iterator();
while (it.hasNext()) {
Node node = (Node) it.next();
switch (node.getNodeType()) {
case CDATA_SECTION_NODE:
// case ENTITY_NODE:
case ENTITY_REFERENCE_NODE:
case TEXT_NODE:
it.remove();
default:
break;
}
}
}
addText(text);
}
public String getStringValue() {
List list = contentList();
int size = list.size();
if (size > 0) {
if (size == 1) {
// optimised to avoid StringBuffer creation
return getContentAsStringValue(list.get(0));
} else {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < size; i++) {
Object node = list.get(i);
String string = getContentAsStringValue(node);
if (string.length() > 0) {
if (USE_STRINGVALUE_SEPARATOR) {
if (buffer.length() > 0) {
buffer.append(' ');
}
}
buffer.append(string);
}
}
return buffer.toString();
}
}
return "";
}
/**
* Puts all <code>Text</code> nodes in the full depth of the sub-tree
* underneath this <code>Node</code>, including attribute nodes, into a
* "normal" form where only structure (e.g., elements, comments, processing
* instructions, CDATA sections, and entity references) separates
* <code>Text</code> nodes, i.e., there are neither adjacent
* <code>Text</code> nodes nor empty <code>Text</code> nodes. This can
* be used to ensure that the DOM view of a document is the same as if it
* were saved and re-loaded, and is useful when operations (such as XPointer
* lookups) that depend on a particular document tree structure are to be
* used.In cases where the document contains <code>CDATASections</code>,
* the normalize operation alone may not be sufficient, since XPointers do
* not differentiate between <code>Text</code> nodes and
* <code>CDATASection</code> nodes.
*
* @since DOM Level 2
*/
public void normalize() {
List content = contentList();
Text previousText = null;
int i = 0;
while (i < content.size()) {
Node node = (Node) content.get(i);
if (node instanceof Text) {
Text text = (Text) node;
if (previousText != null) {
previousText.appendText(text.getText());
remove(text);
} else {
String value = text.getText();
// only remove empty Text nodes, not whitespace nodes
// if ( value == null || value.trim().length() <= 0 ) {
if ((value == null) || (value.length() <= 0)) {
remove(text);
} else {
previousText = text;
i++;
}
}
} else {
if (node instanceof Element) {
Element element = (Element) node;
element.normalize();
}
previousText = null;
i++;
}
}
}
public String elementText(String name) {
Element element = element(name);
return (element != null) ? element.getText() : null;
}
public String elementText(QName qName) {
Element element = element(qName);
return (element != null) ? element.getText() : null;
}
public String elementTextTrim(String name) {
Element element = element(name);
return (element != null) ? element.getTextTrim() : null;
}
public String elementTextTrim(QName qName) {
Element element = element(qName);
return (element != null) ? element.getTextTrim() : null;
}
// add to me content from another element
// analagous to the addAll(collection) methods in Java 2 collections
public void appendAttributes(Element element) {
for (int i = 0, size = element.attributeCount(); i < size; i++) {
Attribute attribute = element.attribute(i);
if (attribute.supportsParent()) {
addAttribute(attribute.getQName(), attribute.getValue());
} else {
add(attribute);
}
}
}
/**
* <p>
* This returns a deep clone of this element. The new element is detached
* from its parent, and getParent() on the clone will return null.
* </p>
*
* @return the clone of this element
*/
/*
* public Object clone() { Element clone = createElement(getQName());
* clone.appendAttributes(this); clone.appendContent(this); return clone; }
*/
public Element createCopy() {
Element clone = createElement(getQName());
clone.appendAttributes(this);
clone.appendContent(this);
return clone;
}
public Element createCopy(String name) {
Element clone = createElement(name);
clone.appendAttributes(this);
clone.appendContent(this);
return clone;
}
public Element createCopy(QName qName) {
Element clone = createElement(qName);
clone.appendAttributes(this);
clone.appendContent(this);
return clone;
}
public QName getQName(String qualifiedName) {
String prefix = "";
String localName = qualifiedName;
int index = qualifiedName.indexOf(":");
if (index > 0) {
prefix = qualifiedName.substring(0, index);
localName = qualifiedName.substring(index + 1);
}
Namespace namespace = getNamespaceForPrefix(prefix);
if (namespace != null) {
return getDocumentFactory().createQName(localName, namespace);
} else {
return getDocumentFactory().createQName(localName);
}
}
public Namespace getNamespaceForPrefix(String prefix) {
if (prefix == null) {
prefix = "";
}
if (prefix.equals(getNamespacePrefix())) {
return getNamespace();
} else if (prefix.equals("xml")) {
return Namespace.XML_NAMESPACE;
} else {
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Namespace) {
Namespace namespace = (Namespace) object;
if (prefix.equals(namespace.getPrefix())) {
return namespace;
}
}
}
}
Element parent = getParent();
if (parent != null) {
Namespace answer = parent.getNamespaceForPrefix(prefix);
if (answer != null) {
return answer;
}
}
if ((prefix == null) || (prefix.length() <= 0)) {
return Namespace.NO_NAMESPACE;
}
return null;
}
public Namespace getNamespaceForURI(String uri) {
if ((uri == null) || (uri.length() <= 0)) {
return Namespace.NO_NAMESPACE;
} else if (uri.equals(getNamespaceURI())) {
return getNamespace();
} else {
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Namespace) {
Namespace namespace = (Namespace) object;
if (uri.equals(namespace.getURI())) {
return namespace;
}
}
}
return null;
}
}
public List<Namespace> getNamespacesForURI(String uri) {
BackedList answer = createResultList();
// if (getNamespaceURI().equals(uri)) {
//
// answer.addLocal(getNamespace());
//
// }
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if ((object instanceof Namespace)
&& ((Namespace) object).getURI().equals(uri)) {
answer.addLocal(object);
}
}
return answer;
}
public List<Namespace> declaredNamespaces() {
BackedList answer = createResultList();
// if (getNamespaceURI().length() > 0) {
//
// answer.addLocal(getNamespace());
//
// }
//
List list = contentList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Namespace) {
answer.addLocal(object);
}
}
return answer;
}
public List<Namespace> additionalNamespaces() {
List list = contentList();
int size = list.size();
BackedList answer = createResultList();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Namespace) {
Namespace namespace = (Namespace) object;
if (!namespace.equals(getNamespace())) {
answer.addLocal(namespace);
}
}
}
return answer;
}
public List additionalNamespaces(String defaultNamespaceURI) {
List list = contentList();
BackedList answer = createResultList();
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
if (object instanceof Namespace) {
Namespace namespace = (Namespace) object;
if (!defaultNamespaceURI.equals(namespace.getURI())) {
answer.addLocal(namespace);
}
}
}
return answer;
}
// Implementation helper methods
// -------------------------------------------------------------------------
/**
* Ensures that the list of attributes has the given size
*
* @param minCapacity
* DOCUMENT ME!
*/
public void ensureAttributesCapacity(int minCapacity) {
if (minCapacity > 1) {
List list = attributeList();
if (list instanceof ArrayList) {
ArrayList arrayList = (ArrayList) list;
arrayList.ensureCapacity(minCapacity);
}
}
}
// Implementation methods
// -------------------------------------------------------------------------
protected Element createElement(String name) {
return getDocumentFactory().createElement(name);
}
protected Element createElement(QName qName) {
return getDocumentFactory().createElement(qName);
}
protected void addNode(Node node) {
if (node.getParent() != null) {
// XXX: could clone here
String message = "The Node already has an existing parent of \""
+ node.getParent().getQualifiedName() + "\"";
throw new IllegalAddException(this, node, message);
}
addNewNode(node);
}
protected void addNode(int index, Node node) {
if (node.getParent() != null) {
// XXX: could clone here
String message = "The Node already has an existing parent of \""
+ node.getParent().getQualifiedName() + "\"";
throw new IllegalAddException(this, node, message);
}
addNewNode(index, node);
}
/**
* Like addNode() but does not require a parent check
*
* @param node
* DOCUMENT ME!
*/
protected void addNewNode(Node node) {
contentList().add(node);
childAdded(node);
}
protected void addNewNode(int index, Node node) {
contentList().add(index, node);
childAdded(node);
}
protected boolean removeNode(Node node) {
boolean answer = contentList().remove(node);
if (answer) {
childRemoved(node);
}
return answer;
}
/**
* Called when a new child node is added to create any parent relationships
*
* @param node
* DOCUMENT ME!
*/
protected void childAdded(Node node) {
if (node != null) {
node.setParent(this);
}
}
protected void childRemoved(Node node) {
if (node != null) {
node.setParent(null);
node.setDocument(null);
}
}
/**
* DOCUMENT ME!
*
* @return the internal List used to store attributes or creates one if one
* is not available
*/
protected abstract List attributeList();
/**
* DOCUMENT ME!
*
* @param attributeCount
* DOCUMENT ME!
*
* @return the internal List used to store attributes or creates one with
* the specified size if one is not available
*/
protected abstract List attributeList(int attributeCount);
protected DocumentFactory getDocumentFactory() {
QName qName = getQName();
// QName might be null as we might not have been constructed yet
if (qName != null) {
DocumentFactory factory = qName.getDocumentFactory();
if (factory != null) {
return factory;
}
}
return DOCUMENT_FACTORY;
}
/**
* A Factory Method pattern which creates a List implementation used to
* store attributes
*
* @return DOCUMENT ME!
*/
protected List createAttributeList() {
return createAttributeList(DEFAULT_CONTENT_LIST_SIZE);
}
/**
* A Factory Method pattern which creates a List implementation used to
* store attributes
*
* @param size
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected List createAttributeList(int size) {
return new ArrayList(size);
}
protected Iterator createSingleIterator(Object result) {
return new SingleIterator(result);
}
}
/*
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain copyright statements and
* notices. Redistributions must also contain a copy of this document.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name "DOM4J" must not be used to endorse or promote products derived
* from this Software without prior written permission of MetaStuff, Ltd. For
* written permission, please contact dom4j-info@metastuff.com.
*
* 4. Products derived from this Software may not be called "DOM4J" nor may
* "DOM4J" appear in their names without prior written permission of MetaStuff,
* Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
*
* 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
*
* THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
*/