View Javadoc
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 }