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