/* * Copyright 2007 Edward Kuns * * 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 * * 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. * * $Id: Element.java 0000 2007-01-11 00:00:00Z ekuns $ */ package org.castor.xmlctf.xmldiff.xml.nodes; import java.util.Iterator; import org.castor.xmlctf.xmldiff.xml.Location; /** * The base node for all XMLNode types. * * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a> * @version $Revision: 0000 $ $Date: 2007-01-11 00:00:00 -0600 (Thu, 11 Jan 2007) $ * @since Castor 1.1 */ public abstract class XMLNode { /** Node is a root node. */ public static final int ROOT = 1; /** Node is an element. */ public static final int ELEMENT = 2; /** Node is an attribute. */ public static final int ATTRIBUTE = 3; /** Node is a text node. */ public static final int TEXT = 4; /** Node is a processing instruction. */ public static final int PROCESSING_INSTRUCTION = 5; /** The localname (non-qualified) for this XMLNode. */ private final String _localName; /** The node type being created. */ private final int _nodeType; /** A reference for the parent node. */ private ParentNode _parent = null; /** The namespace to which this XMLNode belongs. */ private String _namespace = null; /** * Creates a new XMLNode * * @param namespace the namespace URI for this node. [May be null] * @param localName the local-name of this node. [May be null] * @param nodeType the node type being created */ XMLNode(final String namespace, final String localName, final int nodeType) { _namespace = namespace; _localName = localName; _nodeType = nodeType; } /** * Returns the type of this node. * @return The type of this node */ public final int getNodeType() { return _nodeType; } /** * Returns the local name of the node. Returns the local name of an element * or attribute, the prefix of a namespace node, the target of a processing * instruction, or null for all other node types. * * @return The local name of the node, or null if the node has no name */ public String getLocalName() { return _localName; } /** * Returns the namespace URI the node. Returns the namespace URI of an * element, attribute or namespace node, or null for all other node types. * * @return The namespace URI of the node, or null if the node has no * namespace URI */ public String getNamespaceURI() { return _namespace; } /** * Returns the parent node, or null if the node has no parent. This method * is valid on all node types except the root node. Attribute and namespace * nodes have the element as their parent node. * * @return The parent node, or null */ public ParentNode getParentNode() { return _parent; } /** * Returns the root node. * * @return The root node */ public XMLNode getRootNode() { return (_parent != null) ? _parent.getRootNode() : null; } /** * Returns the string value of the node. The string value of a text node or * an attribute node is its text value. The string value of an element or a * root node is the concatenation of the string value of all its child * nodes. The string value of a namespace node is its namespace URI. The * string value of a processing instruction is the instruction, and the * string value of a comment is the comment text. * * @return The string value of the node */ public abstract String getStringValue(); /** * Returns the namespace URI associated with this namespace prefix, as * defined in the context of this node. Returns null if the prefix is * undefined. Returns empty if the prefix is defined and associated with no * namespace. This method is valid only for element nodes. * * @param prefix The namespace prefix * @return The namespace URI, or null */ public String getNamespaceURI(final String prefix) { return (_parent != null) ? _parent.getNamespaceURI(prefix) : null; } /** * Sets the namespace URI for this XMLNode. * @param namespace the Namespace URI */ public void setNamespace(final String namespace) { _namespace = namespace; } /** * Sets the parent XMLNode. * * @param node the XMLNode which is the parent of this XMLNode */ void setParent(final ParentNode node) { _parent = node; } /** * Finds and returns the location of this node in its root's tree. * @return the location of this node in its root's tree. */ public String getNodeLocation() { int column = -1; int line = -1; String xpath = "XPATH: " + getXPath(); if (this instanceof Element) { Location loc = ((Element) this).getLocation(); if (loc != null) { line = loc.getLineNumber(); column = loc.getColumnNumber(); } } String location = null; if (line >= 0) { location = "[" + line + ", " + column + "] " + xpath; } else { location = xpath; } return location; } /** * Returns the XPath from the root node to this node. * @return the XPath from the root node to this node. */ protected String getXPath() { StringBuffer xpath = new StringBuffer(); switch (getNodeType()) { case XMLNode.ATTRIBUTE: xpath.append(getParentNode().getXPath() + "/@" + getLocalName()); break; case XMLNode.ELEMENT: String name = getLocalName(); xpath.append(getParentNode().getXPath() + "/" + name); // Do we have elements of the same type before us in our parent's list? int position = 1; Iterator i = getParentNode().getChildIterator(); while (i.hasNext()) { XMLNode sibling = (XMLNode) i.next(); if (sibling == this) { break; } if (name.equals(sibling.getLocalName())) { ++position; } } boolean usePosition = (position > 1); if (!usePosition) { // Do we have elements of the same type after us in our parent's list? while (i.hasNext()) { XMLNode sibling = (XMLNode) i.next(); if (name.equals(sibling.getLocalName())) { usePosition = true; break; } } } if (usePosition) { xpath.append("[" + position + "]"); } break; case XMLNode.TEXT: xpath.append(getParentNode().getXPath() + "/text()"); break; case XMLNode.ROOT: break; case XMLNode.PROCESSING_INSTRUCTION: xpath.append(getParentNode().getXPath() + "/pi()"); break; default: break; } return xpath.toString(); } }