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