View Javadoc

    1   /*
    2    * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/dom/NamespaceNode.java,v 1.20 2005/06/28 13:44:46 elharo Exp $
    3    * $Revision: 1.20 $
    4    * $Date: 2005/06/28 13:44:46 $
    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.20 2005/06/28 13:44:46 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