ElementImpl.java revision 9c075bb3e84c6eaafd016cbc1bf69a6e989eedf3
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 17package org.apache.harmony.xml.dom; 18 19import java.util.ArrayList; 20import java.util.List; 21import libcore.base.Objects; 22import org.w3c.dom.Attr; 23import org.w3c.dom.DOMException; 24import org.w3c.dom.Element; 25import org.w3c.dom.NamedNodeMap; 26import org.w3c.dom.Node; 27import org.w3c.dom.NodeList; 28import 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 */ 40public 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.equal(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.equal(namespaceURI, attr.getNamespaceURI()) 74 && Objects.equal(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 private void getElementsByTagName(NodeListImpl out, String name) { 168 for (NodeImpl node : children) { 169 if (node.getNodeType() == Node.ELEMENT_NODE) { 170 ElementImpl element = (ElementImpl) node; 171 if (matchesNameOrWildcard(name, element.getNodeName())) { 172 out.add(element); 173 } 174 element.getElementsByTagName(out, name); 175 } 176 } 177 } 178 179 public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { 180 NodeListImpl result = new NodeListImpl(); 181 getElementsByTagNameNS(result, namespaceURI, localName); 182 return result; 183 } 184 185 private void getElementsByTagNameNS(NodeListImpl out, String namespaceURI, String localName) { 186 for (NodeImpl node : children) { 187 if (node.getNodeType() == Node.ELEMENT_NODE) { 188 ElementImpl element = (ElementImpl) node; 189 if (matchesNameOrWildcard(namespaceURI, element.getNamespaceURI()) 190 && matchesNameOrWildcard(localName, element.getLocalName())) { 191 out.add(element); 192 } 193 element.getElementsByTagNameNS(out, namespaceURI, localName); 194 } 195 } 196 } 197 198 /** 199 * Returns true if {@code pattern} equals either "*" or {@code s}. Pattern 200 * may be {@code null}. 201 */ 202 private static boolean matchesNameOrWildcard(String pattern, String s) { 203 return "*".equals(pattern) || Objects.equal(pattern, s); 204 } 205 206 @Override 207 public String getLocalName() { 208 return namespaceAware ? localName : null; 209 } 210 211 @Override 212 public String getNamespaceURI() { 213 return namespaceURI; 214 } 215 216 @Override 217 public String getNodeName() { 218 return getTagName(); 219 } 220 221 public short getNodeType() { 222 return Node.ELEMENT_NODE; 223 } 224 225 @Override 226 public String getPrefix() { 227 return prefix; 228 } 229 230 public String getTagName() { 231 return prefix != null 232 ? prefix + ":" + localName 233 : localName; 234 } 235 236 public boolean hasAttribute(String name) { 237 return indexOfAttribute(name) != -1; 238 } 239 240 public boolean hasAttributeNS(String namespaceURI, String localName) { 241 return indexOfAttributeNS(namespaceURI, localName) != -1; 242 } 243 244 @Override 245 public boolean hasAttributes() { 246 return !attributes.isEmpty(); 247 } 248 249 public void removeAttribute(String name) throws DOMException { 250 int i = indexOfAttribute(name); 251 252 if (i != -1) { 253 attributes.remove(i); 254 } 255 } 256 257 public void removeAttributeNS(String namespaceURI, String localName) 258 throws DOMException { 259 int i = indexOfAttributeNS(namespaceURI, localName); 260 261 if (i != -1) { 262 attributes.remove(i); 263 } 264 } 265 266 public Attr removeAttributeNode(Attr oldAttr) throws DOMException { 267 AttrImpl oldAttrImpl = (AttrImpl) oldAttr; 268 269 if (oldAttrImpl.getOwnerElement() != this) { 270 throw new DOMException(DOMException.NOT_FOUND_ERR, null); 271 } 272 273 attributes.remove(oldAttrImpl); 274 oldAttrImpl.ownerElement = null; 275 276 return oldAttrImpl; 277 } 278 279 public void setAttribute(String name, String value) throws DOMException { 280 Attr attr = getAttributeNode(name); 281 282 if (attr == null) { 283 attr = document.createAttribute(name); 284 setAttributeNode(attr); 285 } 286 287 attr.setValue(value); 288 } 289 290 public void setAttributeNS(String namespaceURI, String qualifiedName, 291 String value) throws DOMException { 292 Attr attr = getAttributeNodeNS(namespaceURI, qualifiedName); 293 294 if (attr == null) { 295 attr = document.createAttributeNS(namespaceURI, qualifiedName); 296 setAttributeNodeNS(attr); 297 } 298 299 attr.setValue(value); 300 } 301 302 public Attr setAttributeNode(Attr newAttr) throws DOMException { 303 AttrImpl newAttrImpl = (AttrImpl) newAttr; 304 305 if (newAttrImpl.document != this.document) { 306 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); 307 } 308 309 if (newAttrImpl.getOwnerElement() != null) { 310 throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null); 311 } 312 313 AttrImpl oldAttrImpl = null; 314 315 int i = indexOfAttribute(newAttr.getName()); 316 if (i != -1) { 317 oldAttrImpl = attributes.get(i); 318 attributes.remove(i); 319 } 320 321 attributes.add(newAttrImpl); 322 newAttrImpl.ownerElement = this; 323 324 return oldAttrImpl; 325 } 326 327 public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { 328 AttrImpl newAttrImpl = (AttrImpl) newAttr; 329 330 if (newAttrImpl.document != this.document) { 331 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); 332 } 333 334 if (newAttrImpl.getOwnerElement() != null) { 335 throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, null); 336 } 337 338 AttrImpl oldAttrImpl = null; 339 340 int i = indexOfAttributeNS(newAttr.getNamespaceURI(), newAttr.getLocalName()); 341 if (i != -1) { 342 oldAttrImpl = attributes.get(i); 343 attributes.remove(i); 344 } 345 346 attributes.add(newAttrImpl); 347 newAttrImpl.ownerElement = this; 348 349 return oldAttrImpl; 350 } 351 352 @Override 353 public void setPrefix(String prefix) { 354 this.prefix = validatePrefix(prefix, namespaceAware, namespaceURI); 355 } 356 357 public class ElementAttrNamedNodeMapImpl implements NamedNodeMap { 358 359 public int getLength() { 360 return ElementImpl.this.attributes.size(); 361 } 362 363 private int indexOfItem(String name) { 364 return ElementImpl.this.indexOfAttribute(name); 365 } 366 367 private int indexOfItemNS(String namespaceURI, String localName) { 368 return ElementImpl.this.indexOfAttributeNS(namespaceURI, localName); 369 } 370 371 public Node getNamedItem(String name) { 372 return ElementImpl.this.getAttributeNode(name); 373 } 374 375 public Node getNamedItemNS(String namespaceURI, String localName) { 376 return ElementImpl.this.getAttributeNodeNS(namespaceURI, localName); 377 } 378 379 public Node item(int index) { 380 return ElementImpl.this.attributes.get(index); 381 } 382 383 public Node removeNamedItem(String name) throws DOMException { 384 int i = indexOfItem(name); 385 386 if (i == -1) { 387 throw new DOMException(DOMException.NOT_FOUND_ERR, null); 388 } 389 390 return ElementImpl.this.attributes.remove(i); 391 } 392 393 public Node removeNamedItemNS(String namespaceURI, String localName) 394 throws DOMException { 395 int i = indexOfItemNS(namespaceURI, localName); 396 397 if (i == -1) { 398 throw new DOMException(DOMException.NOT_FOUND_ERR, null); 399 } 400 401 return ElementImpl.this.attributes.remove(i); 402 } 403 404 public Node setNamedItem(Node arg) throws DOMException { 405 if (!(arg instanceof Attr)) { 406 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); 407 } 408 409 return ElementImpl.this.setAttributeNode((Attr)arg); 410 } 411 412 public Node setNamedItemNS(Node arg) throws DOMException { 413 if (!(arg instanceof Attr)) { 414 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); 415 } 416 417 return ElementImpl.this.setAttributeNodeNS((Attr)arg); 418 } 419 } 420 421 public TypeInfo getSchemaTypeInfo() { 422 // TODO: populate this when we support XML Schema 423 return NULL_TYPE_INFO; 424 } 425 426 public void setIdAttribute(String name, boolean isId) throws DOMException { 427 AttrImpl attr = getAttributeNode(name); 428 if (attr == null) { 429 throw new DOMException(DOMException.NOT_FOUND_ERR, 430 "No such attribute: " + name); 431 } 432 attr.isId = isId; 433 } 434 435 public void setIdAttributeNS(String namespaceURI, String localName, 436 boolean isId) throws DOMException { 437 AttrImpl attr = getAttributeNodeNS(namespaceURI, localName); 438 if (attr == null) { 439 throw new DOMException(DOMException.NOT_FOUND_ERR, 440 "No such attribute: " + namespaceURI + " " + localName); 441 } 442 attr.isId = isId; 443 } 444 445 public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { 446 ((AttrImpl) idAttr).isId = isId; 447 } 448} 449