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