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