1 /* 2 * Copyright 2007 Edward Kuns 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 * 14 * $Id: Element.java 0000 2007-01-11 00:00:00Z ekuns $ 15 */ 16 package org.castor.xmlctf.xmldiff.xml.nodes; 17 18 import java.util.Iterator; 19 20 import org.castor.xmlctf.xmldiff.xml.Location; 21 22 /** 23 * The base node for all XMLNode types. 24 * 25 * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a> 26 * @version $Revision: 0000 $ $Date: 2007-01-11 00:00:00 -0600 (Thu, 11 Jan 2007) $ 27 * @since Castor 1.1 28 */ 29 public abstract class XMLNode { 30 31 /** Node is a root node. */ 32 public static final int ROOT = 1; 33 /** Node is an element. */ 34 public static final int ELEMENT = 2; 35 /** Node is an attribute. */ 36 public static final int ATTRIBUTE = 3; 37 /** Node is a text node. */ 38 public static final int TEXT = 4; 39 /** Node is a processing instruction. */ 40 public static final int PROCESSING_INSTRUCTION = 5; 41 42 /** The localname (non-qualified) for this XMLNode. */ 43 private final String _localName; 44 /** The node type being created. */ 45 private final int _nodeType; 46 47 /** A reference for the parent node. */ 48 private ParentNode _parent = null; 49 /** The namespace to which this XMLNode belongs. */ 50 private String _namespace = null; 51 52 /** 53 * Creates a new XMLNode 54 * 55 * @param namespace the namespace URI for this node. [May be null] 56 * @param localName the local-name of this node. [May be null] 57 * @param nodeType the node type being created 58 */ 59 XMLNode(final String namespace, final String localName, final int nodeType) { 60 _namespace = namespace; 61 _localName = localName; 62 _nodeType = nodeType; 63 } 64 65 /** 66 * Returns the type of this node. 67 * 68 * @return The type of this node 69 */ 70 public final int getNodeType() { 71 return _nodeType; 72 } 73 74 /** 75 * Returns the local name of the node. Returns the local name of an element or attribute, the 76 * prefix of a namespace node, the target of a processing instruction, or null for all other node 77 * types. 78 * 79 * @return The local name of the node, or null if the node has no name 80 */ 81 public String getLocalName() { 82 return _localName; 83 } 84 85 /** 86 * Returns the namespace URI the node. Returns the namespace URI of an element, attribute or 87 * namespace node, or null for all other node types. 88 * 89 * @return The namespace URI of the node, or null if the node has no namespace URI 90 */ 91 public String getNamespaceURI() { 92 return _namespace; 93 } 94 95 /** 96 * Returns the parent node, or null if the node has no parent. This method is valid on all node 97 * types except the root node. Attribute and namespace nodes have the element as their parent 98 * node. 99 * 100 * @return The parent node, or null 101 */ 102 public ParentNode getParentNode() { 103 return _parent; 104 } 105 106 /** 107 * Returns the root node. 108 * 109 * @return The root node 110 */ 111 public XMLNode getRootNode() { 112 return (_parent != null) ? _parent.getRootNode() : null; 113 } 114 115 /** 116 * Returns the string value of the node. The string value of a text node or an attribute node is 117 * its text value. The string value of an element or a root node is the concatenation of the 118 * string value of all its child nodes. The string value of a namespace node is its namespace URI. 119 * The string value of a processing instruction is the instruction, and the string value of a 120 * comment is the comment text. 121 * 122 * @return The string value of the node 123 */ 124 public abstract String getStringValue(); 125 126 /** 127 * Returns the namespace URI associated with this namespace prefix, as defined in the context of 128 * this node. Returns null if the prefix is undefined. Returns empty if the prefix is defined and 129 * associated with no namespace. This method is valid only for element nodes. 130 * 131 * @param prefix The namespace prefix 132 * @return The namespace URI, or null 133 */ 134 public String getNamespaceURI(final String prefix) { 135 return (_parent != null) ? _parent.getNamespaceURI(prefix) : null; 136 } 137 138 /** 139 * Sets the namespace URI for this XMLNode. 140 * 141 * @param namespace the Namespace URI 142 */ 143 public void setNamespace(final String namespace) { 144 _namespace = namespace; 145 } 146 147 /** 148 * Sets the parent XMLNode. 149 * 150 * @param node the XMLNode which is the parent of this XMLNode 151 */ 152 void setParent(final ParentNode node) { 153 _parent = node; 154 } 155 156 /** 157 * Finds and returns the location of this node in its root's tree. 158 * 159 * @return the location of this node in its root's tree. 160 */ 161 public String getNodeLocation() { 162 int column = -1; 163 int line = -1; 164 165 String xpath = "XPATH: " + getXPath(); 166 167 if (this instanceof Element) { 168 Location loc = ((Element) this).getLocation(); 169 if (loc != null) { 170 line = loc.getLineNumber(); 171 column = loc.getColumnNumber(); 172 } 173 } 174 175 String location = null; 176 if (line >= 0) { 177 location = "[" + line + ", " + column + "] " + xpath; 178 } else { 179 location = xpath; 180 } 181 182 return location; 183 } 184 185 /** 186 * Returns the XPath from the root node to this node. 187 * 188 * @return the XPath from the root node to this node. 189 */ 190 protected String getXPath() { 191 StringBuffer xpath = new StringBuffer(); 192 193 switch (getNodeType()) { 194 case XMLNode.ATTRIBUTE: 195 xpath.append(getParentNode().getXPath() + "/@" + getLocalName()); 196 break; 197 case XMLNode.ELEMENT: 198 String name = getLocalName(); 199 xpath.append(getParentNode().getXPath() + "/" + name); 200 201 // Do we have elements of the same type before us in our parent's list? 202 int position = 1; 203 Iterator i = getParentNode().getChildIterator(); 204 while (i.hasNext()) { 205 XMLNode sibling = (XMLNode) i.next(); 206 if (sibling == this) { 207 break; 208 } 209 if (name.equals(sibling.getLocalName())) { 210 ++position; 211 } 212 } 213 214 boolean usePosition = (position > 1); 215 if (!usePosition) { 216 // Do we have elements of the same type after us in our parent's list? 217 while (i.hasNext()) { 218 XMLNode sibling = (XMLNode) i.next(); 219 if (name.equals(sibling.getLocalName())) { 220 usePosition = true; 221 break; 222 } 223 } 224 } 225 if (usePosition) { 226 xpath.append("[" + position + "]"); 227 } 228 break; 229 case XMLNode.TEXT: 230 xpath.append(getParentNode().getXPath() + "/text()"); 231 break; 232 case XMLNode.ROOT: 233 break; 234 case XMLNode.PROCESSING_INSTRUCTION: 235 xpath.append(getParentNode().getXPath() + "/pi()"); 236 break; 237 default: 238 break; 239 } 240 241 return xpath.toString(); 242 } 243 244 }