1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 1999-2002,2004,2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.dom;
  22 
  23 import com.sun.org.apache.xerces.internal.xs.XSSimpleTypeDefinition;
  24 import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
  25 import com.sun.org.apache.xerces.internal.impl.dv.xs.XSSimpleTypeDecl;
  26 import com.sun.org.apache.xerces.internal.impl.xs.XSComplexTypeDecl;
  27 import com.sun.org.apache.xerces.internal.util.URI;
  28 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  29 import org.w3c.dom.Attr;
  30 import org.w3c.dom.DOMException;
  31 
  32 
  33 
  34 /**
  35  * ElementNSImpl inherits from ElementImpl and adds namespace support.
  36  * <P>
  37  * The qualified name is the node name, and we store localName which is also
  38  * used in all queries. On the other hand we recompute the prefix when
  39  * necessary.
  40  *
  41  * @xerces.internal
  42  *
  43  * @author Elena litani, IBM
  44  * @author Neeraj Bajaj, Sun Microsystems
  45  */
  46 public class ElementNSImpl
  47     extends ElementImpl {
  48 
  49     //
  50     // Constants
  51     //
  52 
  53     /** Serialization version. */
  54     static final long serialVersionUID = -9142310625494392642L;
  55     static final String xmlURI = "http://www.w3.org/XML/1998/namespace";
  56 
  57     //
  58     // Data
  59     //
  60 
  61     /** DOM2: Namespace URI. */
  62     protected String namespaceURI;
  63 
  64     /** DOM2: localName. */
  65     protected String localName;
  66 
  67     /** DOM3: type information */
  68     // REVISIT: we are losing the type information in DOM during serialization
  69     transient XSTypeDefinition type;
  70 
  71     protected ElementNSImpl() {
  72         super();
  73     }
  74     /**
  75      * DOM2: Constructor for Namespace implementation.
  76      */
  77     protected ElementNSImpl(CoreDocumentImpl ownerDocument,
  78                             String namespaceURI,
  79                             String qualifiedName)
  80         throws DOMException
  81     {
  82         super(ownerDocument, qualifiedName);
  83         setName(namespaceURI, qualifiedName);
  84     }
  85 
  86         private void setName(String namespaceURI, String qname) {
  87 
  88                 String prefix;
  89                 // DOM Level 3: namespace URI is never empty string.
  90                 this.namespaceURI = namespaceURI;
  91                 if (namespaceURI != null) {
  92             //convert the empty string to 'null'
  93                         this.namespaceURI =     (namespaceURI.length() == 0) ? null : namespaceURI;
  94                 }
  95 
  96         int colon1, colon2 ;
  97 
  98         //NAMESPACE_ERR:
  99         //1. if the qualified name is 'null' it is malformed.
 100         //2. or if the qualifiedName is null and the namespaceURI is different from null,
 101         // We dont need to check for namespaceURI != null, if qualified name is null throw DOMException.
 102         if(qname == null){
 103                                 String msg =
 104                                         DOMMessageFormatter.formatMessage(
 105                                                 DOMMessageFormatter.DOM_DOMAIN,
 106                                                 "NAMESPACE_ERR",
 107                                                 null);
 108                                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
 109         }
 110         else{
 111                     colon1 = qname.indexOf(':');
 112                     colon2 = qname.lastIndexOf(':');
 113         }
 114 
 115                 ownerDocument.checkNamespaceWF(qname, colon1, colon2);
 116                 if (colon1 < 0) {
 117                         // there is no prefix
 118                         localName = qname;
 119                         if (ownerDocument.errorChecking) {
 120                             ownerDocument.checkQName(null, localName);
 121                             if (qname.equals("xmlns")
 122                                 && (namespaceURI == null
 123                                 || !namespaceURI.equals(NamespaceContext.XMLNS_URI))
 124                                 || (namespaceURI!=null && namespaceURI.equals(NamespaceContext.XMLNS_URI)
 125                                 && !qname.equals("xmlns"))) {
 126                                 String msg =
 127                                     DOMMessageFormatter.formatMessage(
 128                                             DOMMessageFormatter.DOM_DOMAIN,
 129                                             "NAMESPACE_ERR",
 130                                             null);
 131                                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
 132                             }
 133                         }
 134                 }//there is a prefix
 135                 else {
 136                     prefix = qname.substring(0, colon1);
 137                     localName = qname.substring(colon2 + 1);
 138 
 139                     //NAMESPACE_ERR:
 140                     //1. if the qualifiedName has a prefix and the namespaceURI is null,
 141 
 142                     //2. or if the qualifiedName has a prefix that is "xml" and the namespaceURI
 143                     //is different from " http://www.w3.org/XML/1998/namespace"
 144 
 145                     if (ownerDocument.errorChecking) {
 146                         if( namespaceURI == null || ( prefix.equals("xml") && !namespaceURI.equals(NamespaceContext.XML_URI) )){
 147                             String msg =
 148                                 DOMMessageFormatter.formatMessage(
 149                                         DOMMessageFormatter.DOM_DOMAIN,
 150                                         "NAMESPACE_ERR",
 151                                         null);
 152                             throw new DOMException(DOMException.NAMESPACE_ERR, msg);
 153                         }
 154 
 155                         ownerDocument.checkQName(prefix, localName);
 156                         ownerDocument.checkDOMNSErr(prefix, namespaceURI);
 157                     }
 158                 }
 159         }
 160 
 161     // when local name is known
 162     protected ElementNSImpl(CoreDocumentImpl ownerDocument,
 163                             String namespaceURI, String qualifiedName,
 164                             String localName)
 165         throws DOMException
 166     {
 167         super(ownerDocument, qualifiedName);
 168 
 169         this.localName = localName;
 170         this.namespaceURI = namespaceURI;
 171     }
 172 
 173     // for DeferredElementImpl
 174     protected ElementNSImpl(CoreDocumentImpl ownerDocument,
 175                             String value) {
 176         super(ownerDocument, value);
 177     }
 178 
 179     // Support for DOM Level 3 renameNode method.
 180     // Note: This only deals with part of the pb. CoreDocumentImpl
 181     // does all the work.
 182     void rename(String namespaceURI, String qualifiedName)
 183     {
 184         if (needsSyncData()) {
 185             synchronizeData();
 186         }
 187                 this.name = qualifiedName;
 188         setName(namespaceURI, qualifiedName);
 189         reconcileDefaultAttributes();
 190     }
 191 
 192     /**
 193      * NON-DOM: resets this node and sets specified values for the node
 194      *
 195      * @param ownerDocument
 196      * @param namespaceURI
 197      * @param qualifiedName
 198      * @param localName
 199      */
 200     protected void setValues (CoreDocumentImpl ownerDocument,
 201                             String namespaceURI, String qualifiedName,
 202                             String localName){
 203 
 204         // remove children first
 205         firstChild = null;
 206         previousSibling = null;
 207         nextSibling = null;
 208         fNodeListCache = null;
 209 
 210         // set owner document
 211         attributes = null;
 212         super.flags = 0;
 213         setOwnerDocument(ownerDocument);
 214 
 215         // synchronizeData will initialize attributes
 216         needsSyncData(true);
 217         super.name = qualifiedName;
 218         this.localName = localName;
 219         this.namespaceURI = namespaceURI;
 220 
 221     }
 222 
 223     //
 224     // Node methods
 225     //
 226 
 227 
 228 
 229     //
 230     //DOM2: Namespace methods.
 231     //
 232 
 233     /**
 234      * Introduced in DOM Level 2. <p>
 235      *
 236      * The namespace URI of this node, or null if it is unspecified.<p>
 237      *
 238      * This is not a computed value that is the result of a namespace lookup based on
 239      * an examination of the namespace declarations in scope. It is merely the
 240      * namespace URI given at creation time.<p>
 241      *
 242      * For nodes created with a DOM Level 1 method, such as createElement
 243      * from the Document interface, this is null.
 244      * @since WD-DOM-Level-2-19990923
 245      */
 246     public String getNamespaceURI()
 247     {
 248         if (needsSyncData()) {
 249             synchronizeData();
 250         }
 251         return namespaceURI;
 252     }
 253 
 254     /**
 255      * Introduced in DOM Level 2. <p>
 256      *
 257      * The namespace prefix of this node, or null if it is unspecified. <p>
 258      *
 259      * For nodes created with a DOM Level 1 method, such as createElement
 260      * from the Document interface, this is null. <p>
 261      *
 262      * @since WD-DOM-Level-2-19990923
 263      */
 264     public String getPrefix()
 265     {
 266 
 267         if (needsSyncData()) {
 268             synchronizeData();
 269         }
 270         int index = name.indexOf(':');
 271         return index < 0 ? null : name.substring(0, index);
 272     }
 273 
 274     /**
 275      * Introduced in DOM Level 2. <p>
 276      *
 277      * Note that setting this attribute changes the nodeName attribute, which holds the
 278      * qualified name, as well as the tagName and name attributes of the Element
 279      * and Attr interfaces, when applicable.<p>
 280      *
 281      * @param prefix The namespace prefix of this node, or null(empty string) if it is unspecified.
 282      *
 283      * @exception INVALID_CHARACTER_ERR
 284      *                   Raised if the specified
 285      *                   prefix contains an invalid character.
 286      * @exception DOMException
 287      * @since WD-DOM-Level-2-19990923
 288      */
 289     public void setPrefix(String prefix)
 290         throws DOMException
 291     {
 292         if (needsSyncData()) {
 293             synchronizeData();
 294         }
 295         if (ownerDocument.errorChecking) {
 296             if (isReadOnly()) {
 297                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
 298                 throw new DOMException(
 299                                      DOMException.NO_MODIFICATION_ALLOWED_ERR,
 300                                      msg);
 301             }
 302             if (prefix != null && prefix.length() != 0) {
 303                 if (!CoreDocumentImpl.isXMLName(prefix,ownerDocument.isXML11Version())) {
 304                     String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
 305                     throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
 306                 }
 307                 if (namespaceURI == null || prefix.indexOf(':') >=0) {
 308                     String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NAMESPACE_ERR", null);
 309                     throw new DOMException(DOMException.NAMESPACE_ERR, msg);
 310                 } else if (prefix.equals("xml")) {
 311                      if (!namespaceURI.equals(xmlURI)) {
 312                          String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NAMESPACE_ERR", null);
 313                          throw new DOMException(DOMException.NAMESPACE_ERR, msg);
 314                      }
 315                 }
 316             }
 317 
 318         }
 319         // update node name with new qualifiedName
 320         if (prefix !=null && prefix.length() != 0) {
 321             name = prefix + ":" + localName;
 322         }
 323         else {
 324             name = localName;
 325         }
 326     }
 327 
 328     /**
 329      * Introduced in DOM Level 2. <p>
 330      *
 331      * Returns the local part of the qualified name of this node.
 332      * @since WD-DOM-Level-2-19990923
 333      */
 334     public String getLocalName()
 335     {
 336         if (needsSyncData()) {
 337             synchronizeData();
 338         }
 339         return localName;
 340     }
 341 
 342 
 343    /**
 344      * DOM Level 3 WD - Experimental.
 345      * Retrieve baseURI
 346      */
 347     public String getBaseURI() {
 348 
 349         if (needsSyncData()) {
 350             synchronizeData();
 351         }
 352         // Absolute base URI is computed according to XML Base (http://www.w3.org/TR/xmlbase/#granularity)
 353 
 354         // 1.  the base URI specified by an xml:base attribute on the element, if one exists
 355 
 356         if (attributes != null) {
 357             Attr attrNode = (Attr)attributes.getNamedItemNS("http://www.w3.org/XML/1998/namespace", "base");
 358             if (attrNode != null) {
 359                 String uri =  attrNode.getNodeValue();
 360                 if (uri.length() != 0 ) {// attribute value is always empty string
 361                     try {
 362                         uri = new URI(uri).toString();
 363                     }
 364                     catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e) {
 365                         // This may be a relative URI.
 366 
 367                         // Start from the base URI of the parent, or if this node has no parent, the owner node.
 368                         NodeImpl parentOrOwner = (parentNode() != null) ? parentNode() : ownerNode;
 369 
 370                         // Make any parentURI into a URI object to use with the URI(URI, String) constructor.
 371                         String parentBaseURI = (parentOrOwner != null) ? parentOrOwner.getBaseURI() : null;
 372 
 373                         if (parentBaseURI != null) {
 374                             try {
 375                                 uri = new URI(new URI(parentBaseURI), uri).toString();
 376                             }
 377                             catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException ex){
 378                                 // This should never happen: parent should have checked the URI and returned null if invalid.
 379                                 return null;
 380                             }
 381                             return uri;
 382                         }
 383                         // REVISIT: what should happen in this case?
 384                         return null;
 385                     }
 386                     return uri;
 387                 }
 388             }
 389         }
 390 
 391         //2.the base URI of the element's parent element within the document or external entity,
 392         //if one exists
 393         String parentElementBaseURI = (this.parentNode() != null) ? this.parentNode().getBaseURI() : null ;
 394         //base URI of parent element is not null
 395         if(parentElementBaseURI != null){
 396             try {
 397                 //return valid absolute base URI
 398                return new URI(parentElementBaseURI).toString();
 399             }
 400             catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
 401                 // REVISIT: what should happen in this case?
 402                 return null;
 403             }
 404         }
 405         //3. the base URI of the document entity or external entity containing the element
 406 
 407         String baseURI = (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null ;
 408 
 409         if(baseURI != null){
 410             try {
 411                 //return valid absolute base URI
 412                return new URI(baseURI).toString();
 413             }
 414             catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
 415                 // REVISIT: what should happen in this case?
 416                 return null;
 417             }
 418         }
 419 
 420         return null;
 421 
 422     }
 423 
 424 
 425     /**
 426      * @see org.w3c.dom.TypeInfo#getTypeName()
 427      */
 428     public String getTypeName() {
 429         if (type !=null){
 430             if (type instanceof XSSimpleTypeDecl) {
 431                 return ((XSSimpleTypeDecl) type).getTypeName();
 432             } else if (type instanceof XSComplexTypeDecl) {
 433                 return ((XSComplexTypeDecl) type).getTypeName();
 434             }
 435         }
 436         return null;
 437     }
 438 
 439     /**
 440      * @see org.w3c.dom.TypeInfo#getTypeNamespace()
 441      */
 442     public String getTypeNamespace() {
 443         if (type !=null){
 444             return type.getNamespace();
 445         }
 446         return null;
 447     }
 448 
 449     /**
 450      * Introduced in DOM Level 2. <p>
 451      * Checks if a type is derived from another by restriction. See:
 452      * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom
 453      *
 454      * @param ancestorNS
 455      *        The namspace of the ancestor type declaration
 456      * @param ancestorName
 457      *        The name of the ancestor type declaration
 458      * @param type
 459      *        The reference type definition
 460      *
 461      * @return boolean True if the type is derived by restriciton for the
 462      *         reference type
 463      */
 464     public boolean isDerivedFrom(String typeNamespaceArg, String typeNameArg,
 465             int derivationMethod) {
 466         if(needsSyncData()) {
 467             synchronizeData();
 468         }
 469         if (type != null) {
 470             if (type instanceof XSSimpleTypeDecl) {
 471                 return ((XSSimpleTypeDecl) type).isDOMDerivedFrom(
 472                         typeNamespaceArg, typeNameArg, derivationMethod);
 473             } else if (type instanceof XSComplexTypeDecl) {
 474                 return ((XSComplexTypeDecl) type).isDOMDerivedFrom(
 475                         typeNamespaceArg, typeNameArg, derivationMethod);
 476             }
 477         }
 478         return false;
 479     }
 480 
 481     /**
 482      * NON-DOM: setting type used by the DOM parser
 483      * @see NodeImpl#setReadOnly
 484      */
 485     public void setType(XSTypeDefinition type) {
 486         this.type = type;
 487     }
 488 }