View Javadoc

1   /*
2    * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/dom/NamespaceNode.java,v 1.19 2005/06/26 16:13:33 elharo Exp $
3    * $Revision: 1.19 $
4    * $Date: 2005/06/26 16:13:33 $
5    *
6    * ====================================================================
7    *
8    * Copyright (C) 2000-2002 bob mcwhirter & James Strachan.
9    * All rights reserved.
10   *
11   * Redistribution and use in source and binary forms, with or without
12   * modification, are permitted provided that the following conditions
13   * are met:
14   * 
15   * 1. Redistributions of source code must retain the above copyright
16   *    notice, this list of conditions, and the following disclaimer.
17   *
18   * 2. Redistributions in binary form must reproduce the above copyright
19   *    notice, this list of conditions, and the disclaimer that follows 
20   *    these conditions in the documentation and/or other materials 
21   *    provided with the distribution.
22   *
23   * 3. The name "Jaxen" must not be used to endorse or promote products
24   *    derived from this software without prior written permission.  For
25   *    written permission, please contact license@jaxen.org.
26   * 
27   * 4. Products derived from this software may not be called "Jaxen", nor
28   *    may "Jaxen" appear in their name, without prior written permission
29   *    from the Jaxen Project Management (pm@jaxen.org).
30   * 
31   * In addition, we request (but do not require) that you include in the 
32   * end-user documentation provided with the redistribution and/or in the 
33   * software itself an acknowledgement equivalent to the following:
34   *     "This product includes software developed by the
35   *      Jaxen Project (http://www.jaxen.org/)."
36   * Alternatively, the acknowledgment may be graphical using the logos 
37   * available at http://www.jaxen.org/
38   *
39   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42   * DISCLAIMED.  IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
43   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50   * SUCH DAMAGE.
51   *
52   * ====================================================================
53   * This software consists of voluntary contributions made by many 
54   * individuals on behalf of the Jaxen Project and was originally 
55   * created by bob mcwhirter <bob@werken.com> and 
56   * James Strachan <jstrachan@apache.org>.  For more information on the 
57   * Jaxen Project, please see <http://www.jaxen.org/>.
58   * 
59   * $Id: NamespaceNode.java,v 1.19 2005/06/26 16:13:33 elharo Exp $
60   */
61  
62  ////////////////////////////////////////////////////////////////////
63  // Inner class for a Namespace node.
64  ////////////////////////////////////////////////////////////////////
65  
66  package org.jaxen.dom;
67  
68  import java.util.HashMap;
69  
70  import org.jaxen.pattern.Pattern;
71  import org.w3c.dom.DOMException;
72  import org.w3c.dom.Document;
73  import org.w3c.dom.NamedNodeMap;
74  import org.w3c.dom.Node;
75  import org.w3c.dom.NodeList;
76  import org.w3c.dom.UserDataHandler;
77  
78  
79  /***
80   * Extension DOM2/DOm3 node type for a namespace node.
81   *
82   * <p>This class implements the DOM2 and DOM3 {@link Node} interface
83   * to allow namespace nodes to be included in the result
84   * set of an XPath selectNodes operation, even though DOM does
85   * not model namespaces in scope as separate nodes.</p>
86   *
87   * <p>
88   * While all of the DOM2 methods are implemented with reasonable
89   * defaults, there will be some unexpected surprises, so users are
90   * advised to test for NamespaceNodes and filter them out from the
91   * result sets as early as possible.
92    * </p>
93   *
94   * <ol>
95   *
96   * <li>The {@link #getNodeType} method returns {@link #NAMESPACE_NODE},
97   * which is not one of the usual DOM2 node types.  Generic code may
98   * fall unexpectedly out of switch statements, for example.</li>
99   *
100  * <li>The {@link #getOwnerDocument} method returns the owner document
101  * of the parent node, but that owner document will know nothing about
102  * the namespace node.</p>
103  *
104  * <li>The {@link #isSupported} method always returns false.</li>
105  *
106  * <li> The DOM3 methods sometimes throw UnsupportedOperationException.
107  *      They're here only to allow this class to be compiled with Java 1.5.
108  *       Do not call or rely on them.</li>
109  * </ol>
110  *
111  * <p>All attempts to modify a <code>NamespaceNode</code> will fail with a {@link
112  * DOMException} ({@link
113  * DOMException#NO_MODIFICATION_ALLOWED_ERR}).</p>
114  *
115  * @author David Megginson
116  * @author Elliotte Rusty Harold
117  * @see DocumentNavigator
118  */
119 public class NamespaceNode implements Node
120 {
121 
122 
123     ////////////////////////////////////////////////////////////////////
124     // Constants.
125     ////////////////////////////////////////////////////////////////////
126 
127     /***
128      * Constant: this is a NamespaceNode.
129      *
130      * @see #getNodeType
131      */
132     public final static short NAMESPACE_NODE = Pattern.NAMESPACE_NODE;
133 
134     // FIXME "Note: Numeric codes up to 200 are reserved to W3C for possible future use."
135     // Ww should be using higher codes.
136 
137     ////////////////////////////////////////////////////////////////////
138     // Protected Constructors.
139     ////////////////////////////////////////////////////////////////////
140 
141 
142     /***
143      * Create a new NamespaceNode.
144      *
145      * @param parent the DOM node to which the namespace is attached
146      * @param name the namespace prefix
147      * @param value the namespace URI
148      */
149     public NamespaceNode (Node parent, String name, String value)
150     {
151         this.parent = parent;
152         this.name = name;
153         this.value = value;
154     }
155 
156 
157     /***
158      * Constructor.
159      *
160      * @param parent the DOM node to which the namespace is attached
161      * @param attribute the DOM attribute object containing the
162      *        namespace declaration
163      */
164     NamespaceNode (Node parent, Node attribute)
165     {
166         String attributeName = attribute.getNodeName();
167     
168         if (attributeName.equals("xmlns")) {
169             this.name = "";
170         }
171         else if (attributeName.startsWith("xmlns:")) {
172             this.name = attributeName.substring(6); // the part after "xmlns:"
173         }
174         else { // workaround for Crimson bug; Crimson incorrectly reports the prefix as the node name
175             this.name = attributeName;
176         }
177         this.parent = parent;
178         this.value = attribute.getNodeValue();
179     }
180 
181 
182 
183     ////////////////////////////////////////////////////////////////////
184     // Implementation of org.w3c.dom.Node.
185     ////////////////////////////////////////////////////////////////////
186 
187 
188     /***
189      * Get the namespace prefix.
190      *
191      * @return the namespace prefix, or "" for the default namespace
192      */
193     public String getNodeName ()
194     {
195         return name;
196     }
197 
198 
199     /***
200      * Get the namespace URI.
201      *
202      * @return the namespace URI
203      */
204     public String getNodeValue ()
205     {
206         return value;
207     }
208 
209 
210     /***
211      * Change the namespace URI (always fails).
212      *
213      * @param value the new URI
214      * @throws DOMException always
215      */
216     public void setNodeValue (String value) throws DOMException
217     {
218         disallowModification();
219     }
220 
221 
222     /***
223      * Get the node type.
224      *
225      * @return always {@link #NAMESPACE_NODE}.
226      */
227     public short getNodeType ()
228     {
229         return NAMESPACE_NODE;
230     }
231 
232 
233     /***
234      * Get the parent node.
235      *
236      * <p>This method returns the element that was queried for Namespaces
237      * in effect, <em>not</em> necessarily the actual element containing
238      * the Namespace declaration.</p>
239      *
240      * @return the parent node (not null)
241      */
242     public Node getParentNode ()
243     {
244         return parent;
245     }
246 
247 
248     /***
249      * Get the list of child nodes.
250      *
251      * @return an empty node list
252      */
253     public NodeList getChildNodes ()
254     {
255         return new EmptyNodeList();
256     }
257 
258 
259     /***
260      * Get the first child node.
261      *
262      * @return null
263      */
264     public Node getFirstChild ()
265     {
266         return null;
267     }
268 
269 
270     /***
271      * Get the last child node.
272      *
273      * @return null
274      */
275     public Node getLastChild ()
276     {
277         return null;
278     }
279 
280 
281     /***
282      * Get the previous sibling node.
283      *
284      * @return null
285      */
286     public Node getPreviousSibling ()
287     {
288         return null;
289     }
290 
291 
292     /***
293      * Get the next sibling node.
294      *
295      * @return null
296      */
297     public Node getNextSibling ()
298     {
299         return null;
300     }
301 
302 
303     /***
304      * Get the attribute nodes.
305      *
306      * @return null
307      */
308     public NamedNodeMap getAttributes ()
309     {
310         return null;
311     }
312 
313 
314     /***
315      * Get the owner document.
316      *
317      * @return the owner document <em>of the parent node</em>
318      */
319     public Document getOwnerDocument ()
320     {
321         if (parent == null) return null;
322         return parent.getOwnerDocument();
323     }
324 
325 
326     /***
327      * Insert a new child node (always fails).
328      * 
329      * @param newChild the node to add
330      * @param refChild ignored
331      * @return never
332      * @throws DOMException always
333      * @see Node#insertBefore
334      */
335     public Node insertBefore (Node newChild, Node refChild)
336     throws DOMException
337     {
338         disallowModification();
339         return null;
340     }
341 
342 
343     /***
344      * Replace a child node (always fails).
345      *
346      * @param newChild the node to add
347      * @param oldChild the child node to replace
348      * @return never
349      * @throws DOMException always
350      * @see Node#replaceChild
351      */
352     public Node replaceChild (Node newChild, Node oldChild)
353     throws DOMException
354     {
355         disallowModification();
356         return null;
357     }
358 
359 
360     /***
361      * Remove a child node (always fails).
362      *
363      * @param oldChild the child node to remove
364      * @return never
365      * @throws DOMException always
366      * @see Node#removeChild
367      */
368     public Node removeChild (Node oldChild)
369     throws DOMException
370     {
371         disallowModification();
372         return null;
373     }
374 
375 
376     /***
377      * Append a new child node (always fails).
378      *
379      * @param newChild the node to add
380      * @return never
381      * @throws DOMException always
382      * @see Node#appendChild
383      */
384     public Node appendChild (Node newChild)
385     throws DOMException
386     {
387         disallowModification();
388         return null;
389     }
390 
391 
392     /***
393      * Test for child nodes.
394      *
395      * @return false
396      */
397     public boolean hasChildNodes ()
398     {
399         return false;
400     }
401 
402 
403     /***
404      * Create a copy of this node.
405      *
406      * @param deep make a deep copy (no effect, since namespace nodes
407      *        don't have children).
408      * @return a new copy of this namespace node
409      */
410     public Node cloneNode (boolean deep)
411     {
412         return new NamespaceNode(parent, name, value);
413     }
414 
415 
416     /***
417      * Normalize the text descendants of this node.
418      *
419      * <p>This method has no effect, since namespace nodes have no
420      * descendants.</p>
421      */
422     public void normalize ()
423     {
424     // no op
425     }
426 
427 
428     /***
429      * Test if a DOM2 feature is supported.
430      *
431      * @param feature the feature name
432      * @param version the feature version
433      * @return false
434      */
435     public boolean isSupported (String feature, String version)
436     {
437         return false;
438     }
439 
440 
441     /***
442      * Get the namespace URI of this node.
443      *
444      * <p>Namespace declarations are not themselves
445      * Namespace-qualified.</p>
446      *
447      * @return null
448      */
449     public String getNamespaceURI ()
450     {
451        return null;
452     }
453 
454 
455     /***
456      * Get the namespace prefix of this node.
457      *
458      * <p>Namespace declarations are not themselves
459      * namespace-qualified.</p>
460      *
461      * @return null
462      * @see #getLocalName
463      */
464     public String getPrefix ()
465     {
466         return null;
467     }
468 
469 
470     /***
471      * Change the namespace prefix of this node (always fails).
472      *
473      * @param prefix the new prefix
474      * @throws DOMException always thrown
475      */
476     public void setPrefix (String prefix)
477     throws DOMException
478     {
479         disallowModification();
480     }
481 
482 
483     /***
484      * Get the XPath name of the namespace node;; i.e. the
485      * namespace prefix.
486      *
487      * @return the namespace prefix
488      */
489     public String getLocalName ()
490     {
491         return name;
492     }
493 
494 
495     /***
496      * Test if this node has attributes.
497      *
498      * @return false
499      */
500     public boolean hasAttributes ()
501     {
502         return false;
503     }
504 
505 
506     /***
507      * Throw a NO_MODIFICATION_ALLOWED_ERR DOMException.
508      *
509      * @throws DOMException always thrown
510      */
511     private void disallowModification () throws DOMException
512     {
513         throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
514                    "Namespace node may not be modified");
515     }
516 
517 
518 
519     ////////////////////////////////////////////////////////////////////
520     // Override default methods from java.lang.Object.
521     ////////////////////////////////////////////////////////////////////
522 
523 
524     /***
525      * Generate a hash code for a namespace node.
526      *
527      * @return a hash code for this node
528      */
529     public int hashCode ()
530     {
531     return hashCode(parent) + hashCode(name) + hashCode(value);
532     }
533 
534 
535     /***
536      * Test for equivalence with another object.
537      *
538      * <p>Two Namespace nodes are considered equivalent if their parents,
539      * names, and values are equal.</p>
540      *
541      * @param o the object to test for equality
542      * @return true if the object is equivalent to this node, false
543      *         otherwise
544      */
545     public boolean equals (Object o)
546     {
547         if (o == this) return true;
548         else if (o == null) return false;
549         else if (o instanceof NamespaceNode) {
550             NamespaceNode ns = (NamespaceNode)o;
551             return (equals(parent, ns.getParentNode()) &&
552                 equals(name, ns.getNodeName()) &&
553                 equals(value, ns.getNodeValue()));
554         } else {
555             return false;
556         }
557     }
558 
559 
560     /***
561      * Helper method for generating a hash code.
562      *
563      * @param o the object for generating a hash code (possibly null)
564      * @return the object's hash code, or 0 if the object is null
565      * @see java.lang.Object#hashCode
566      */
567     private int hashCode (Object o)
568     {
569     return (o == null ? 0 : o.hashCode());
570     }
571 
572 
573     /***
574      * Helper method for comparing two objects.
575      *
576      * @param a the first object to compare (possibly null)
577      * @param b the second object to compare (possibly null)
578      * @return true if the objects are equivalent or are both null
579      * @see java.lang.Object#equals
580      */
581     private boolean equals (Object a, Object b)
582     {
583         return ((a == null && b == null) ||
584           (a != null && a.equals(b)));
585     }
586 
587 
588     ////////////////////////////////////////////////////////////////////
589     // Internal state.
590     ////////////////////////////////////////////////////////////////////
591 
592     private Node parent;
593     private String name;
594     private String value;
595 
596 
597 
598     ////////////////////////////////////////////////////////////////////
599     // Inner class: empty node list.
600     ////////////////////////////////////////////////////////////////////
601 
602 
603     /***
604      * A node list with no members.
605      *
606      * <p>This class is necessary for the {@link Node#getChildNodes}
607      * method, which must return an empty node list rather than
608      * null when there are no children.</p>
609      */
610     private static class EmptyNodeList implements NodeList
611     {
612 
613         /***
614          * @see NodeList#getLength
615          */
616         public int getLength ()
617         {
618             return 0;
619         }
620     
621     
622         /***
623          * @see NodeList#item
624          */
625         public Node item(int index)
626         {
627             return null;
628         }
629     
630     }
631 
632     ////////////////////////////////////////////////////////////////////
633     // DOM Level 3 methods
634     ////////////////////////////////////////////////////////////////////
635 
636     /***
637      * Return the base URI of the document containing this node (always fails).
638      *
639      * @return null
640      */
641     public String getBaseURI() {
642         // XXX we could use reflection in DOM Level 3 to get this value
643         return null;
644     }
645 
646 
647     /***
648      * Compare relative position of this node to another nbode. (Always fails).
649      *
650      * @return never
651      * @throws DOMException NOT_SUPPORTED_ERR
652      */
653     public short compareDocumentPosition(Node other) throws DOMException {
654         DOMException ex = new DOMException(
655           DOMException.NOT_SUPPORTED_ERR,
656           "DOM level 3 interfaces are not fully implemented in Jaxen's NamespaceNode class"
657         );
658         throw ex;
659     }
660 
661 
662     /***
663      * Return the namespace URI.
664      *
665      * @return the namespace URI
666      * @see #getNodeValue
667      */
668     public String getTextContent() {
669         return value;
670     }
671 
672 
673     /***
674      * Change the value of this node (always fails).
675      *
676      * @param textContent the new content
677      * @throws DOMException always
678      */
679     public void setTextContent(String textContent) throws DOMException {
680         disallowModification();
681     }
682 
683 
684     /***
685      * Returns true if and only if this object represents the same XPath namespace node
686      * as the argument; that is, they have the same parent, the same prefix, and the
687      * same URI.
688      * 
689      * @param other the node to compare to
690      * @return true if this object represents the same XPath namespace node
691      *     as other; false otherwise
692      */
693     public boolean isSameNode(Node other) {
694         return this.isEqualNode(other) 
695           // a bit flaky (should really be this.getParentNode().isEqual(other.getParentNode())
696           // but we want this to compile in Java 1.4 without problems
697           && this.getParentNode() == other.getParentNode();
698     }
699 
700 
701     /***
702      * Return the prefix bound to this namespace URI within the scope
703      * of this node (always fails).
704      *
705      * @return never
706      * @throws UnsupportedOperationException always
707      */
708     public String lookupPrefix(String namespaceURI) {
709         // XXX This could be implemented. See
710         // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespaceURIAlgo
711         // It hardly seems worth the effort though.
712         throw new UnsupportedOperationException("Changing interfaces in a JDK blows chunks!");
713     }
714 
715 
716     /***
717      * Return true if the specified URI is the default namespace in
718      * scope (always fails).
719      *
720      * @return never
721      * @throws UnsupportedOperationException always
722      */
723     public boolean isDefaultNamespace(String namespaceURI) {
724         return namespaceURI.equals(this.lookupNamespaceURI(null));
725     }
726 
727 
728     /***
729      * Return the namespace URI mapped to the specified
730      * prefix within the scope of this namespace node (always fails).
731      *
732      * @return never
733      * @throws UnsupportedOperationException always
734      */
735     public String lookupNamespaceURI(String prefix) {
736         // XXX This could be implemented. See
737         // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespaceURIAlgo
738         // It hardly seems worth the effort though.
739         throw new UnsupportedOperationException("Changing interfaces in a JDK blows chunks!");
740     }
741 
742 
743     /***
744      * Returns true if this object binds the same prefix to the same URI.
745      * That is, this object has the same prefix and URI as the argument.
746      * 
747      * @param arg the node to compare to
748      * @return true if this object has the same prefix and URI as the argument; false otherwise
749      */
750     public boolean isEqualNode(Node arg) {
751         if (arg.getNodeType() == this.getNodeType()) {
752             NamespaceNode other = (NamespaceNode) arg;
753             if (other.name == null && this.name != null) return false;
754             else if (other.name != null && this.name == null) return false;
755             else if (other.value == null && this.value != null) return false;
756             else if (other.value != null && this.value == null) return false;
757             else if (other.name == null && this.name == null) {
758                 return other.value.equals(this.value);
759             }
760 
761             return other.name.equals(this.name) && other.value.equals(this.value);
762         }
763         return false;
764     }
765 
766 
767     /***
768      * Returns the value of the requested feature. Always returns null.
769      * 
770      * @return null
771      */
772     public Object getFeature(String feature, String version) {
773         return null;
774     }
775 
776     
777     // XXX userdata needs testing
778     private HashMap userData = new HashMap();
779 
780     /***
781      * Associates an object with a key. 
782      * 
783      * @param key the key by which the data will be retrieved
784      * @param data the object to store with the key
785      * @param handler ignored since nnamespace ndoes cannot be imported, cloned, or renamed
786      * 
787      * @return the value previously associated with this key; or null
788      *     if there isn't any such previous value
789      */
790     public Object setUserData(String key, Object data, UserDataHandler handler) {
791         Object oldValue = getUserData(key);
792         userData.put(key, data);
793         return oldValue;
794     }
795 
796 
797     /***
798      * Returns the user data associated with the given key. 
799      * 
800      * @return the object associated with the key; or null if no such object is available
801      */
802     public Object getUserData(String key) {
803         return userData.get(key);
804     }
805     
806 }
807 
808 // end of Namespace.java