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 }