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