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 libcore.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 ElementImpl(DocumentImpl document, String namespaceURI, String qualifiedName)49 ElementImpl(DocumentImpl document, String namespaceURI, String qualifiedName) { 50 super(document); 51 setNameNS(this, namespaceURI, qualifiedName); 52 } 53 ElementImpl(DocumentImpl document, String name)54 ElementImpl(DocumentImpl document, String name) { 55 super(document); 56 setName(this, name); 57 } 58 indexOfAttribute(String name)59 private int indexOfAttribute(String name) { 60 for (int i = 0; i < attributes.size(); i++) { 61 AttrImpl attr = attributes.get(i); 62 if (Objects.equal(name, attr.getNodeName())) { 63 return i; 64 } 65 } 66 67 return -1; 68 } 69 indexOfAttributeNS(String namespaceURI, String localName)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.equal(namespaceURI, attr.getNamespaceURI()) 74 && Objects.equal(localName, attr.getLocalName())) { 75 return i; 76 } 77 } 78 79 return -1; 80 } 81 getAttribute(String name)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 getAttributeNS(String namespaceURI, String localName)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 getAttributeNode(String name)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 getAttributeNodeNS(String namespaceURI, String localName)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 getAttributes()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 */ getElementById(String name)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 getElementsByTagName(String name)161 public NodeList getElementsByTagName(String name) { 162 NodeListImpl result = new NodeListImpl(); 163 getElementsByTagName(result, name); 164 return result; 165 } 166 getElementsByTagNameNS(String namespaceURI, String localName)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 getLocalName()174 public String getLocalName() { 175 return namespaceAware ? localName : null; 176 } 177 178 @Override getNamespaceURI()179 public String getNamespaceURI() { 180 return namespaceURI; 181 } 182 183 @Override getNodeName()184 public String getNodeName() { 185 return getTagName(); 186 } 187 getNodeType()188 public short getNodeType() { 189 return Node.ELEMENT_NODE; 190 } 191 192 @Override getPrefix()193 public String getPrefix() { 194 return prefix; 195 } 196 getTagName()197 public String getTagName() { 198 return prefix != null 199 ? prefix + ":" + localName 200 : localName; 201 } 202 hasAttribute(String name)203 public boolean hasAttribute(String name) { 204 return indexOfAttribute(name) != -1; 205 } 206 hasAttributeNS(String namespaceURI, String localName)207 public boolean hasAttributeNS(String namespaceURI, String localName) { 208 return indexOfAttributeNS(namespaceURI, localName) != -1; 209 } 210 211 @Override hasAttributes()212 public boolean hasAttributes() { 213 return !attributes.isEmpty(); 214 } 215 removeAttribute(String name)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 removeAttributeNS(String namespaceURI, String localName)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 removeAttributeNode(Attr oldAttr)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 setAttribute(String name, String value)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 setAttributeNS(String namespaceURI, String qualifiedName, String value)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 setAttributeNode(Attr newAttr)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 setAttributeNodeNS(Attr newAttr)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 setPrefix(String prefix)320 public void setPrefix(String prefix) { 321 this.prefix = validatePrefix(prefix, namespaceAware, namespaceURI); 322 } 323 324 public class ElementAttrNamedNodeMapImpl implements NamedNodeMap { 325 getLength()326 public int getLength() { 327 return ElementImpl.this.attributes.size(); 328 } 329 indexOfItem(String name)330 private int indexOfItem(String name) { 331 return ElementImpl.this.indexOfAttribute(name); 332 } 333 indexOfItemNS(String namespaceURI, String localName)334 private int indexOfItemNS(String namespaceURI, String localName) { 335 return ElementImpl.this.indexOfAttributeNS(namespaceURI, localName); 336 } 337 getNamedItem(String name)338 public Node getNamedItem(String name) { 339 return ElementImpl.this.getAttributeNode(name); 340 } 341 getNamedItemNS(String namespaceURI, String localName)342 public Node getNamedItemNS(String namespaceURI, String localName) { 343 return ElementImpl.this.getAttributeNodeNS(namespaceURI, localName); 344 } 345 item(int index)346 public Node item(int index) { 347 return ElementImpl.this.attributes.get(index); 348 } 349 removeNamedItem(String name)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 removeNamedItemNS(String namespaceURI, String localName)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 setNamedItem(Node arg)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 setNamedItemNS(Node arg)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 getSchemaTypeInfo()388 public TypeInfo getSchemaTypeInfo() { 389 // TODO: populate this when we support XML Schema 390 return NULL_TYPE_INFO; 391 } 392 setIdAttribute(String name, boolean isId)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 setIdAttributeNS(String namespaceURI, String localName, boolean isId)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 setIdAttributeNode(Attr idAttr, boolean isId)412 public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { 413 ((AttrImpl) idAttr).isId = isId; 414 } 415 } 416