Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      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 
     17 package org.apache.harmony.xml.dom;
     18 
     19 import java.util.ArrayList;
     20 import java.util.List;
     21 import java.util.Objects;
     22 import org.w3c.dom.Attr;
     23 import org.w3c.dom.DOMException;
     24 import org.w3c.dom.Element;
     25 import org.w3c.dom.NamedNodeMap;
     26 import org.w3c.dom.Node;
     27 import org.w3c.dom.NodeList;
     28 import org.w3c.dom.TypeInfo;
     29 
     30 /**
     31  * Provides a straightforward implementation of the corresponding W3C DOM
     32  * interface. The class is used internally only, thus only notable members that
     33  * are not in the original interface are documented (the W3C docs are quite
     34  * extensive). Hope that's ok.
     35  * <p>
     36  * Some of the fields may have package visibility, so other classes belonging to
     37  * the DOM implementation can easily access them while maintaining the DOM tree
     38  * structure.
     39  */
     40 public class ElementImpl extends InnerNodeImpl implements Element {
     41 
     42     boolean namespaceAware;
     43     String namespaceURI;
     44     String prefix;
     45     String localName;
     46 
     47     private List<AttrImpl> attributes = new ArrayList<AttrImpl>();
     48 
     49     ElementImpl(DocumentImpl document, String namespaceURI, String qualifiedName) {
     50         super(document);
     51         setNameNS(this, namespaceURI, qualifiedName);
     52     }
     53 
     54     ElementImpl(DocumentImpl document, String name) {
     55         super(document);
     56         setName(this, name);
     57     }
     58 
     59     private int indexOfAttribute(String name) {
     60         for (int i = 0; i < attributes.size(); i++) {
     61             AttrImpl attr = attributes.get(i);
     62             if (Objects.equals(name, attr.getNodeName())) {
     63                 return i;
     64             }
     65         }
     66 
     67         return -1;
     68     }
     69 
     70     private int indexOfAttributeNS(String namespaceURI, String localName) {
     71         for (int i = 0; i < attributes.size(); i++) {
     72             AttrImpl attr = attributes.get(i);
     73             if (Objects.equals(namespaceURI, attr.getNamespaceURI())
     74                     && Objects.equals(localName, attr.getLocalName())) {
     75                 return i;
     76             }
     77         }
     78 
     79         return -1;
     80     }
     81 
     82     public String getAttribute(String name) {
     83         Attr attr = getAttributeNode(name);
     84 
     85         if (attr == null) {
     86             return "";
     87         }
     88 
     89         return attr.getValue();
     90     }
     91 
     92     public String getAttributeNS(String namespaceURI, String localName) {
     93         Attr attr = getAttributeNodeNS(namespaceURI, localName);
     94 
     95         if (attr == null) {
     96             return "";
     97         }
     98 
     99         return attr.getValue();
    100     }
    101 
    102     public AttrImpl getAttributeNode(String name) {
    103         int i = indexOfAttribute(name);
    104 
    105         if (i == -1) {
    106             return null;
    107         }
    108 
    109         return attributes.get(i);
    110     }
    111 
    112     public AttrImpl getAttributeNodeNS(String namespaceURI, String localName) {
    113         int i = indexOfAttributeNS(namespaceURI, localName);
    114 
    115         if (i == -1) {
    116             return null;
    117         }
    118 
    119         return attributes.get(i);
    120     }
    121 
    122     @Override
    123     public NamedNodeMap getAttributes() {
    124         return new ElementAttrNamedNodeMapImpl();
    125     }
    126 
    127     /**
    128      * This implementation walks the entire document looking for an element
    129      * with the given ID attribute. We should consider adding an index to speed
    130      * navigation of large documents.
    131      */
    132     Element getElementById(String name) {
    133         for (Attr attr : attributes) {
    134             if (attr.isId() && name.equals(attr.getValue())) {
    135                 return this;
    136             }
    137         }
    138 
    139         /*
    140          * TODO: Remove this behavior.
    141          * The spec explicitly says that this is a bad idea. From
    142          * Document.getElementById(): "Attributes with the name "ID"
    143          * or "id" are not of type ID unless so defined.
    144          */
    145         if (name.equals(getAttribute("id"))) {
    146             return this;
    147         }
    148 
    149         for (NodeImpl node : children) {
    150             if (node.getNodeType() == Node.ELEMENT_NODE) {
    151                 Element element = ((ElementImpl) node).getElementById(name);
    152                 if (element != null) {
    153                     return element;
    154                 }
    155             }
    156         }
    157 
    158         return null;
    159     }
    160 
    161     public NodeList getElementsByTagName(String name) {
    162         NodeListImpl result = new NodeListImpl();
    163         getElementsByTagName(result, name);
    164         return result;
    165     }
    166 
    167     public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
    168         NodeListImpl result = new NodeListImpl();
    169         getElementsByTagNameNS(result, namespaceURI, localName);
    170         return result;
    171     }
    172 
    173     @Override
    174     public String getLocalName() {
    175         return namespaceAware ? localName : null;
    176     }
    177 
    178     @Override
    179     public String getNamespaceURI() {
    180         return namespaceURI;
    181     }
    182 
    183     @Override
    184     public String getNodeName() {
    185         return getTagName();
    186     }
    187 
    188     public short getNodeType() {
    189         return Node.ELEMENT_NODE;
    190     }
    191 
    192     @Override
    193     public String getPrefix() {
    194         return prefix;
    195     }
    196 
    197     public String getTagName() {
    198         return prefix != null
    199                 ? prefix + ":" + localName
    200                 : localName;
    201     }
    202 
    203     public boolean hasAttribute(String name) {
    204         return indexOfAttribute(name) != -1;
    205     }
    206 
    207     public boolean hasAttributeNS(String namespaceURI, String localName) {
    208         return indexOfAttributeNS(namespaceURI, localName) != -1;
    209     }
    210 
    211     @Override
    212     public boolean hasAttributes() {
    213         return !attributes.isEmpty();
    214     }
    215 
    216     public void removeAttribute(String name) throws DOMException {
    217         int i = indexOfAttribute(name);
    218 
    219         if (i != -1) {
    220             attributes.remove(i);
    221         }
    222     }
    223 
    224     public void removeAttributeNS(String namespaceURI, String localName)
    225             throws DOMException {
    226         int i = indexOfAttributeNS(namespaceURI, localName);
    227 
    228         if (i != -1) {
    229             attributes.remove(i);
    230         }
    231     }
    232 
    233     public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
    234         AttrImpl oldAttrImpl = (AttrImpl) oldAttr;
    235 
    236         if (oldAttrImpl.getOwnerElement() != this) {
    237             throw new DOMException(DOMException.NOT_FOUND_ERR, null);
    238         }
    239 
    240         attributes.remove(oldAttrImpl);
    241         oldAttrImpl.ownerElement = null;
    242 
    243         return oldAttrImpl;
    244     }
    245 
    246     public void setAttribute(String name, String value) throws DOMException {
    247         Attr attr = getAttributeNode(name);
    248 
    249         if (attr == null) {
    250             attr = document.createAttribute(name);
    251             setAttributeNode(attr);
    252         }
    253 
    254         attr.setValue(value);
    255     }
    256 
    257     public void setAttributeNS(String namespaceURI, String qualifiedName,
    258             String value) throws DOMException {
    259         Attr attr = getAttributeNodeNS(namespaceURI, qualifiedName);
    260 
    261         if (attr == null) {
    262             attr = document.createAttributeNS(namespaceURI, qualifiedName);
    263             setAttributeNodeNS(attr);
    264         }
    265 
    266         attr.setValue(value);
    267     }
    268 
    269     public Attr setAttributeNode(Attr newAttr) throws DOMException {
    270         AttrImpl newAttrImpl = (AttrImpl) newAttr;
    271 
    272         if (newAttrImpl.document != this.document) {
    273             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
    274         }
    275 
    276         if (newAttrImpl.getOwnerElement() != null) {
    277             throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null);
    278         }
    279 
    280         AttrImpl oldAttrImpl = null;
    281 
    282         int i = indexOfAttribute(newAttr.getName());
    283         if (i != -1) {
    284             oldAttrImpl = attributes.get(i);
    285             attributes.remove(i);
    286         }
    287 
    288         attributes.add(newAttrImpl);
    289         newAttrImpl.ownerElement = this;
    290 
    291         return oldAttrImpl;
    292     }
    293 
    294     public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
    295         AttrImpl newAttrImpl = (AttrImpl) newAttr;
    296 
    297         if (newAttrImpl.document != this.document) {
    298             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
    299         }
    300 
    301         if (newAttrImpl.getOwnerElement() != null) {
    302             throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null);
    303         }
    304 
    305         AttrImpl oldAttrImpl = null;
    306 
    307         int i = indexOfAttributeNS(newAttr.getNamespaceURI(), newAttr.getLocalName());
    308         if (i != -1) {
    309             oldAttrImpl = attributes.get(i);
    310             attributes.remove(i);
    311         }
    312 
    313         attributes.add(newAttrImpl);
    314         newAttrImpl.ownerElement = this;
    315 
    316         return oldAttrImpl;
    317     }
    318 
    319     @Override
    320     public void setPrefix(String prefix) {
    321         this.prefix = validatePrefix(prefix, namespaceAware, namespaceURI);
    322     }
    323 
    324     public class ElementAttrNamedNodeMapImpl implements NamedNodeMap {
    325 
    326         public int getLength() {
    327             return ElementImpl.this.attributes.size();
    328         }
    329 
    330         private int indexOfItem(String name) {
    331             return ElementImpl.this.indexOfAttribute(name);
    332         }
    333 
    334         private int indexOfItemNS(String namespaceURI, String localName) {
    335             return ElementImpl.this.indexOfAttributeNS(namespaceURI, localName);
    336         }
    337 
    338         public Node getNamedItem(String name) {
    339             return ElementImpl.this.getAttributeNode(name);
    340         }
    341 
    342         public Node getNamedItemNS(String namespaceURI, String localName) {
    343             return ElementImpl.this.getAttributeNodeNS(namespaceURI, localName);
    344         }
    345 
    346         public Node item(int index) {
    347             return ElementImpl.this.attributes.get(index);
    348         }
    349 
    350         public Node removeNamedItem(String name) throws DOMException {
    351             int i = indexOfItem(name);
    352 
    353             if (i == -1) {
    354                 throw new DOMException(DOMException.NOT_FOUND_ERR, null);
    355             }
    356 
    357             return ElementImpl.this.attributes.remove(i);
    358         }
    359 
    360         public Node removeNamedItemNS(String namespaceURI, String localName)
    361                 throws DOMException {
    362             int i = indexOfItemNS(namespaceURI, localName);
    363 
    364             if (i == -1) {
    365                 throw new DOMException(DOMException.NOT_FOUND_ERR, null);
    366             }
    367 
    368             return ElementImpl.this.attributes.remove(i);
    369         }
    370 
    371         public Node setNamedItem(Node arg) throws DOMException {
    372             if (!(arg instanceof Attr)) {
    373                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
    374             }
    375 
    376             return ElementImpl.this.setAttributeNode((Attr)arg);
    377         }
    378 
    379         public Node setNamedItemNS(Node arg) throws DOMException {
    380             if (!(arg instanceof Attr)) {
    381                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
    382             }
    383 
    384             return ElementImpl.this.setAttributeNodeNS((Attr)arg);
    385         }
    386     }
    387 
    388     public TypeInfo getSchemaTypeInfo() {
    389         // TODO: populate this when we support XML Schema
    390         return NULL_TYPE_INFO;
    391     }
    392 
    393     public void setIdAttribute(String name, boolean isId) throws DOMException {
    394         AttrImpl attr = getAttributeNode(name);
    395         if (attr == null) {
    396             throw new DOMException(DOMException.NOT_FOUND_ERR,
    397                     "No such attribute: " + name);
    398         }
    399         attr.isId = isId;
    400     }
    401 
    402     public void setIdAttributeNS(String namespaceURI, String localName,
    403             boolean isId) throws DOMException {
    404         AttrImpl attr = getAttributeNodeNS(namespaceURI, localName);
    405         if (attr == null) {
    406             throw new DOMException(DOMException.NOT_FOUND_ERR,
    407                     "No such attribute: " + namespaceURI +  " " + localName);
    408         }
    409         attr.isId = isId;
    410     }
    411 
    412     public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException {
    413         ((AttrImpl) idAttr).isId = isId;
    414     }
    415 }
    416