/*
* XMLElement.java NanoXML/Java $Revision: 1.5 $ $Date: 2002/02/06 18:50:12 $
* $Name: RELEASE_2_2_1 $ This file is part of NanoXML 2 for Java. Copyright (C)
* 2000-2002 Marc De Scheemaecker, All Rights Reserved. This software is
* provided 'as-is', without any express or implied warranty. In no event will
* the authors be held liable for any damages arising from the use of this
* software. Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and redistribute
* it freely, subject to the following restrictions: 1. The origin of this
* software must not be misrepresented; you must not claim that you wrote the
* original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required. 2.
* Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software. 3. This notice may not be
* removed or altered from any source distribution.
*/
package org.freeplane.n3.nanoxml;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
/**
* XMLElement is an XML element. The standard NanoXML builder generates a tree
* of such elements.
*
* @see org.freeplane.n3.nanoxml.StdXMLBuilder
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
*/
public class XMLElement implements Serializable {
/**
* No line number defined.
*/
public static final int NO_LINE = -1;
/**
* Necessary for serialization.
*/
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates an empty element.
*
* @param fullName
* the full name of the element
* @param namespace
* the namespace URI.
* @param systemID
* the system ID of the XML data where the element starts.
* @param lineNr
* the line in the XML data where the element starts.
*/
public static XMLElement createElement(final String fullName, final String namespace, final String systemID,
final int lineNr) {
return new XMLElement(fullName, namespace, systemID, lineNr);
}
/**
* The attributes of the element.
*/
private Vector<XMLAttribute> attributes;
/**
* The child elements.
*/
private Vector<XMLElement> children;
/**
* The content of the element.
*/
private String content;
/**
* The full name of the element.
*/
private String fullName;
/**
* The line in the source data where this element starts.
*/
final private int lineNr;
/**
* The name of the element.
*/
private String name;
/**
* The namespace URI.
*/
private String namespace;
/**
* The parent element.
*/
private XMLElement parent;
/**
* The system ID of the source data where this element is located.
*/
private String systemID;
/**
* Creates an empty element to be used for #PCDATA content.
*/
public XMLElement() {
this(null, null, null, XMLElement.NO_LINE);
}
/**
* Creates an empty element.
*
* @param fullName
* the name of the element.
*/
public XMLElement(final String fullName) {
this(fullName, null, null, XMLElement.NO_LINE);
}
/**
* Creates an empty element.
*
* @param fullName
* the full name of the element
* @param namespace
* the namespace URI.
*/
public XMLElement(final String fullName, final String namespace) {
this(fullName, namespace, null, XMLElement.NO_LINE);
}
/**
* Creates an empty element.
*
* @param fullName
* the name of the element.
* @param systemID
* the system ID of the XML data where the element starts.
* @param lineNr
* the line in the XML data where the element starts.
*/
public XMLElement(final String fullName, final String systemID, final int lineNr) {
this(fullName, null, systemID, lineNr);
}
/**
* Creates an empty element.
*
* @param fullName
* the full name of the element
* @param namespace
* the namespace URI.
* @param systemID
* the system ID of the XML data where the element starts.
* @param lineNr
* the line in the XML data where the element starts.
*/
public XMLElement(final String fullName, final String namespace, final String systemID, final int lineNr) {
attributes = new Vector<XMLAttribute>();
children = new Vector<XMLElement>(8);
this.fullName = fullName;
if (namespace == null) {
name = fullName;
}
else {
final int index = fullName.indexOf(':');
if (index >= 0) {
name = fullName.substring(index + 1);
}
else {
name = fullName;
}
}
this.namespace = namespace;
content = null;
this.lineNr = lineNr;
this.systemID = systemID;
parent = null;
}
/**
* Adds a child element.
*
* @param child
* the non-null child to add.
*/
public void addChild(final XMLElement child) {
if (child == null) {
throw new IllegalArgumentException("child must not be null");
}
if ((child.getName() == null) && (!children.isEmpty())) {
final XMLElement lastChild = children.lastElement();
if (lastChild.getName() == null) {
lastChild.setContent(lastChild.getContent() + child.getContent());
return;
}
}
(child).parent = this;
children.addElement(child);
}
/**
* Creates an empty element.
*
* @param fullName
* the name of the element.
*/
public XMLElement createElement(final String fullName) {
return new XMLElement(fullName);
}
/**
* Creates an empty element.
*
* @param fullName
* the full name of the element
* @param namespace
* the namespace URI.
*/
public XMLElement createElement(final String fullName, final String namespace) {
return new XMLElement(fullName, namespace);
}
/**
* Creates an empty element.
*
* @param fullName
* the name of the element.
* @param systemID
* the system ID of the XML data where the element starts.
* @param lineNr
* the line in the XML data where the element starts.
*/
public XMLElement createElement(final String fullName, final String systemID, final int lineNr) {
return new XMLElement(fullName, systemID, lineNr);
}
/**
* Creates an element to be used for #PCDATA content.
*/
public XMLElement createPCDataElement() {
return new XMLElement();
}
/**
* Returns an enumeration of all attribute names.
*
* @return the non-null enumeration.
*/
public Enumeration<String> enumerateAttributeNames() {
final Vector<String> result = new Vector<String>();
final Enumeration<XMLAttribute> enumeration = attributes.elements();
while (enumeration.hasMoreElements()) {
result.addElement(enumeration.nextElement().getFullName());
}
return result.elements();
}
/**
* Returns an enumeration of all child elements.
*
* @return the non-null enumeration
*/
public Enumeration<XMLElement> enumerateChildren() {
return children.elements();
}
/**
* Returns true if the element equals another element.
*
* @param rawElement
* the element to compare to
*/
@Override
public boolean equals(final Object rawElement) {
try {
return this.equalsXMLElement((XMLElement) rawElement);
}
catch (final ClassCastException e) {
return false;
}
}
/**
* Returns true if the element equals another element.
*
* @param rawElement
* the element to compare to
*/
public boolean equalsXMLElement(final XMLElement elt) {
if (!name.equals(elt.getName())) {
return false;
}
if (attributes.size() != elt.getAttributeCount()) {
return false;
}
final Enumeration<XMLAttribute> enumeration = attributes.elements();
while (enumeration.hasMoreElements()) {
final XMLAttribute attr = enumeration.nextElement();
if (!elt.hasAttribute(attr.getName(), attr.getNamespace())) {
return false;
}
final String value = elt.getAttribute(attr.getName(), attr.getNamespace(), null);
if (!attr.getValue().equals(value)) {
return false;
}
final String type = elt.getAttributeType(attr.getName(), attr.getNamespace());
if (!attr.getType().equals(type)) {
return false;
}
}
if (children.size() != elt.getChildrenCount()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
final XMLElement child1 = this.getChildAtIndex(i);
final XMLElement child2 = elt.getChildAtIndex(i);
if (!child1.equalsXMLElement(child2)) {
return false;
}
}
return true;
}
/**
* Cleans up the object when it's destroyed.
*/
@Override
protected void finalize() throws Throwable {
attributes.clear();
attributes = null;
children = null;
fullName = null;
name = null;
namespace = null;
content = null;
systemID = null;
parent = null;
super.finalize();
}
/**
* Searches an attribute.
*
* @param fullName
* the non-null full name of the attribute.
* @return the attribute, or null if the attribute does not exist.
*/
private XMLAttribute findAttribute(final String fullName) {
if (fullName == null) {
throw new IllegalArgumentException("fullName must not be null");
}
final Enumeration<XMLAttribute> enumeration = attributes.elements();
while (enumeration.hasMoreElements()) {
final XMLAttribute attr = enumeration.nextElement();
if (attr.getFullName().equals(fullName)) {
return attr;
}
}
return null;
}
/**
* Searches an attribute.
*
* @param name
* the non-null short name of the attribute.
* @param namespace
* the name space, which may be null.
* @return the attribute, or null if the attribute does not exist.
*/
private XMLAttribute findAttribute(final String name, final String namespace) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
final Enumeration<XMLAttribute> enumeration = attributes.elements();
while (enumeration.hasMoreElements()) {
final XMLAttribute attr = enumeration.nextElement();
boolean found = attr.getName().equals(name);
if (namespace == null) {
found &= (attr.getNamespace() == null);
}
else {
found &= namespace.equals(attr.getNamespace());
}
if (found) {
return attr;
}
}
return null;
}
/**
* @deprecated As of NanoXML/Java 2.1, replaced by
* {@link #getAttribute(java.lang.String,java.lang.String)}
* Returns the value of an attribute.
* @param name
* the non-null name of the attribute.
* @return the value, or null if the attribute does not exist.
*/
@Deprecated
public String getAttribute(final String name) {
return this.getAttribute(name, null);
}
/**
* Returns the value of an attribute.
*
* @param name
* the non-null full name of the attribute.
* @param defaultValue
* the default value of the attribute.
* @return the value, or defaultValue if the attribute does not exist.
*/
public int getAttribute(final String name, final int defaultValue) {
final String value = this.getAttribute(name, Integer.toString(defaultValue));
return Integer.parseInt(value);
}
/**
* Returns the value of an attribute.
*
* @param name
* the non-null full name of the attribute.
* @param defaultValue
* the default value of the attribute.
* @return the value, or defaultValue if the attribute does not exist.
*/
public String getAttribute(final String name, final String defaultValue) {
final XMLAttribute attr = this.findAttribute(name);
if (attr == null) {
return defaultValue;
}
else {
return attr.getValue();
}
}
/**
* Returns the value of an attribute.
*
* @param name
* the non-null name of the attribute.
* @param namespace
* the namespace URI, which may be null.
* @param defaultValue
* the default value of the attribute.
* @return the value, or defaultValue if the attribute does not exist.
*/
public int getAttribute(final String name, final String namespace, final int defaultValue) {
final String value = this.getAttribute(name, namespace, Integer.toString(defaultValue));
return Integer.parseInt(value);
}
/**
* Returns the value of an attribute.
*
* @param name
* the non-null name of the attribute.
* @param namespace
* the namespace URI, which may be null.
* @param defaultValue
* the default value of the attribute.
* @return the value, or defaultValue if the attribute does not exist.
*/
public String getAttribute(final String name, final String namespace, final String defaultValue) {
final XMLAttribute attr = this.findAttribute(name, namespace);
if (attr == null) {
return defaultValue;
}
else {
return attr.getValue();
}
}
/**
* Returns the number of attributes.
*/
public int getAttributeCount() {
return attributes.size();
}
/**
* Returns the namespace of an attribute.
*
* @param name
* the non-null full name of the attribute.
* @return the namespace, or null if there is none associated.
*/
public String getAttributeNamespace(final String name) {
final XMLAttribute attr = this.findAttribute(name);
if (attr == null) {
return null;
}
else {
return attr.getNamespace();
}
}
/**
* Returns all attributes as a Properties object.
*
* @return the non-null set.
*/
public Properties getAttributes() {
final Properties result = new Properties();
final Enumeration<XMLAttribute> enumeration = attributes.elements();
while (enumeration.hasMoreElements()) {
final XMLAttribute attr = enumeration.nextElement();
result.put(attr.getFullName(), attr.getValue());
}
return result;
}
/**
* Returns all attributes in a specific namespace as a Properties object.
*
* @param namespace
* the namespace URI of the attributes, which may be null.
* @return the non-null set.
*/
public Properties getAttributesInNamespace(final String namespace) {
final Properties result = new Properties();
final Enumeration<XMLAttribute> enumeration = attributes.elements();
while (enumeration.hasMoreElements()) {
final XMLAttribute attr = enumeration.nextElement();
if (namespace == null) {
if (attr.getNamespace() == null) {
result.put(attr.getName(), attr.getValue());
}
}
else {
if (namespace.equals(attr.getNamespace())) {
result.put(attr.getName(), attr.getValue());
}
}
}
return result;
}
/**
* Returns the type of an attribute.
*
* @param name
* the non-null full name of the attribute.
* @return the type, or null if the attribute does not exist.
*/
public String getAttributeType(final String name) {
final XMLAttribute attr = this.findAttribute(name);
if (attr == null) {
return null;
}
else {
return attr.getType();
}
}
/**
* Returns the type of an attribute.
*
* @param name
* the non-null name of the attribute.
* @param namespace
* the namespace URI, which may be null.
* @return the type, or null if the attribute does not exist.
*/
public String getAttributeType(final String name, final String namespace) {
final XMLAttribute attr = this.findAttribute(name, namespace);
if (attr == null) {
return null;
}
else {
return attr.getType();
}
}
/**
* Returns the child at a specific index.
*
* @param index
* the index of the child
* @return the non-null child
* @throws java.lang.ArrayIndexOutOfBoundsException
* if the index is out of bounds.
*/
public XMLElement getChildAtIndex(final int index) throws ArrayIndexOutOfBoundsException {
return (XMLElement) children.elementAt(index);
}
/**
* Returns a vector containing all the child elements.
*
* @return the vector.
*/
public Vector<XMLElement> getChildren() {
return children;
}
/**
* Returns the number of children.
*
* @return the count.
*/
public int getChildrenCount() {
return children.size();
}
/**
* Returns a vector of all child elements named <I>name</I>.
*
* @param name
* the full name of the children to search for.
* @return the non-null vector of child elements.
*/
public Vector<XMLElement> getChildrenNamed(final String name) {
final Vector<XMLElement> result = new Vector<XMLElement>(children.size());
final Enumeration<XMLElement> enumeration = children.elements();
while (enumeration.hasMoreElements()) {
final XMLElement child = enumeration.nextElement();
final String childName = child.getFullName();
if ((childName != null) && childName.equals(name)) {
result.addElement(child);
}
}
return result;
}
/**
* Returns a vector of all child elements named <I>name</I>.
*
* @param name
* the name of the children to search for.
* @param namespace
* the namespace, which may be null.
* @return the non-null vector of child elements.
*/
public Vector<XMLElement> getChildrenNamed(final String name, final String namespace) {
final Vector<XMLElement> result = new Vector<XMLElement>(children.size());
final Enumeration<XMLElement> enumeration = children.elements();
while (enumeration.hasMoreElements()) {
final XMLElement child = (XMLElement) enumeration.nextElement();
String str = child.getName();
boolean found = (str != null) && (str.equals(name));
str = child.getNamespace();
if (str == null) {
found &= (name == null);
}
else {
found &= str.equals(namespace);
}
if (found) {
result.addElement(child);
}
}
return result;
}
/**
* Return the #PCDATA content of the element. If the element has a
* combination of #PCDATA content and child elements, the #PCDATA sections
* can be retrieved as unnamed child objects. In this case, this method
* returns null.
*
* @return the content.
*/
public String getContent() {
return content;
}
/**
* Searches a child element.
*
* @param name
* the full name of the child to search for.
* @return the child element, or null if no such child was found.
*/
public XMLElement getFirstChildNamed(final String name) {
final Enumeration<XMLElement> enumeration = children.elements();
while (enumeration.hasMoreElements()) {
final XMLElement child = enumeration.nextElement();
final String childName = child.getFullName();
if ((childName != null) && childName.equals(name)) {
return child;
}
}
return null;
}
/**
* Searches a child element.
*
* @param name
* the name of the child to search for.
* @param namespace
* the namespace, which may be null.
* @return the child element, or null if no such child was found.
*/
public XMLElement getFirstChildNamed(final String name, final String namespace) {
final Enumeration<XMLElement> enumeration = children.elements();
while (enumeration.hasMoreElements()) {
final XMLElement child = enumeration.nextElement();
String str = child.getName();
boolean found = (str != null) && (str.equals(name));
str = child.getNamespace();
if (str == null) {
found &= (name == null);
}
else {
found &= str.equals(namespace);
}
if (found) {
return child;
}
}
return null;
}
/**
* Returns the full name (i.e. the name including an eventual namespace
* prefix) of the element.
*
* @return the name, or null if the element only contains #PCDATA.
*/
public String getFullName() {
return fullName;
}
/**
* Returns the line number in the data where the element started.
*
* @return the line number, or NO_LINE if unknown.
* @see #NO_LINE
* @see #getSystemID
*/
public int getLineNr() {
return lineNr;
}
/**
* Returns the name of the element.
*
* @return the name, or null if the element only contains #PCDATA.
*/
public String getName() {
return name;
}
/**
* Returns the namespace of the element.
*
* @return the namespace, or null if no namespace is associated with the
* element.
*/
public String getNamespace() {
return namespace;
}
/**
* Returns the parent element. This method returns null for the root
* element.
*/
public XMLElement getParent() {
return parent;
}
/**
* Returns the system ID of the data where the element started.
*
* @return the system ID, or null if unknown.
* @see #getLineNr
*/
public String getSystemID() {
return systemID;
}
/**
* Returns whether an attribute exists.
*
* @return true if the attribute exists.
*/
public boolean hasAttribute(final String name) {
return this.findAttribute(name) != null;
}
/**
* Returns whether an attribute exists.
*
* @return true if the attribute exists.
*/
public boolean hasAttribute(final String name, final String namespace) {
return this.findAttribute(name, namespace) != null;
}
/**
* Returns whether the element has children.
*
* @return true if the element has children.
*/
public boolean hasChildren() {
return (!children.isEmpty());
}
/**
* Inserts a child element.
*
* @param child
* the non-null child to add.
* @param index
* where to put the child.
*/
public void insertChild(final XMLElement child, final int index) {
if (child == null) {
throw new IllegalArgumentException("child must not be null");
}
if ((child.getName() == null) && (!children.isEmpty())) {
final XMLElement lastChild = (XMLElement) children.lastElement();
if (lastChild.getName() == null) {
lastChild.setContent(lastChild.getContent() + child.getContent());
return;
}
}
(child).parent = this;
children.insertElementAt(child, index);
}
/**
* Returns whether the element is a leaf element.
*
* @return true if the element has no children.
*/
public boolean isLeaf() {
return children.isEmpty();
}
/**
* Removes an attribute.
*
* @param name
* the non-null name of the attribute.
*/
public void removeAttribute(final String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
for (int i = 0; i < attributes.size(); i++) {
final XMLAttribute attr = attributes.elementAt(i);
if (attr.getFullName().equals(name)) {
attributes.removeElementAt(i);
return;
}
}
}
/**
* Removes an attribute.
*
* @param name
* the non-null name of the attribute.
* @param namespace
* the namespace URI of the attribute, which may be null.
*/
public void removeAttribute(final String name, final String namespace) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
for (int i = 0; i < attributes.size(); i++) {
final XMLAttribute attr = attributes.elementAt(i);
boolean found = attr.getName().equals(name);
if (namespace == null) {
found &= (attr.getNamespace() == null);
}
else {
found &= attr.getNamespace().equals(namespace);
}
if (found) {
attributes.removeElementAt(i);
return;
}
}
}
/**
* Removes a child element.
*
* @param child
* the non-null child to remove.
*/
public void removeChild(final XMLElement child) {
if (child == null) {
throw new IllegalArgumentException("child must not be null");
}
children.removeElement(child);
}
/**
* Removes the child located at a certain index.
*
* @param index
* the index of the child, where the first child has index 0.
*/
public void removeChildAtIndex(final int index) {
children.removeElementAt(index);
}
/**
* Sets an attribute.
*
* @param name
* the non-null full name of the attribute.
* @param value
* the non-null value of the attribute.
*/
public void setAttribute(final String name, final String value) {
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
XMLAttribute attr = this.findAttribute(name);
if (attr == null) {
attr = new XMLAttribute(name, name, null, value, "CDATA");
attributes.addElement(attr);
}
else {
attr.setValue(value);
}
}
/**
* Sets an attribute.
*
* @param fullName
* the non-null full name of the attribute.
* @param namespace
* the namespace URI of the attribute, which may be null.
* @param value
* the non-null value of the attribute.
*/
public void setAttribute(final String fullName, final String namespace, final String value) {
if (fullName == null) {
throw new IllegalArgumentException("fullName must not be null");
}
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
final int index = fullName.indexOf(':');
final String name = fullName.substring(index + 1);
XMLAttribute attr = this.findAttribute(name, namespace);
if (attr == null) {
attr = new XMLAttribute(fullName, name, namespace, value, "CDATA");
attributes.addElement(attr);
}
else {
attr.setValue(value);
}
}
/**
* Sets the #PCDATA content. It is an error to call this method with a
* non-null value if there are child objects.
*
* @param content
* the (possibly null) content.
*/
public void setContent(final String content) {
this.content = content;
}
/**
* Sets the full name. This method also sets the short name and clears the
* namespace URI.
*
* @param name
* the non-null name.
*/
public void setName(final String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
this.name = name;
fullName = name;
namespace = null;
}
/**
* Sets the name.
*
* @param fullName
* the non-null full name.
* @param namespace
* the namespace URI, which may be null.
*/
public void setName(final String fullName, final String namespace) {
if (fullName == null) {
throw new IllegalArgumentException("fullName must not be null");
}
final int index = fullName.indexOf(':');
if ((namespace == null) || (index < 0)) {
name = fullName;
}
else {
name = fullName.substring(index + 1);
}
this.fullName = fullName;
this.namespace = namespace;
}
}