Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 553   Methods: 33
NCLOC: 266   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ElementImpl.java 72.2% 80.6% 63.6% 76.2%
coverage coverage
 1    /*
 2    * Licensed to the Apache Software Foundation (ASF) under one or more
 3    * contributor license agreements. See the NOTICE file distributed with
 4    * this work for additional information regarding copyright ownership.
 5    * The ASF licenses this file to You under the Apache License, Version 2.0
 6    * (the "License"); you may not use this file except in compliance with
 7    * the License. You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    *
 17    * $Id: ElementImpl.java 571948 2007-09-02 10:51:37Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.xml.dom;
 21   
 22    import org.apache.commons.logging.Log;
 23    import org.apache.commons.logging.LogFactory;
 24    import org.apache.xindice.core.data.Key;
 25    import org.apache.xindice.util.ByteArrayInput;
 26    import org.apache.xindice.xml.NodeSource;
 27    import org.apache.xindice.xml.SymbolTable;
 28    import org.apache.xindice.xml.XMLCompressedInput;
 29   
 30    import org.w3c.dom.Attr;
 31    import org.w3c.dom.DOMException;
 32    import org.w3c.dom.Element;
 33    import org.w3c.dom.NamedNodeMap;
 34    import org.w3c.dom.Node;
 35    import org.w3c.dom.TypeInfo;
 36    import org.w3c.dom.UserDataHandler;
 37   
 38    import java.io.IOException;
 39    import java.util.HashSet;
 40   
 41    /**
 42    * ElementImpl
 43    *
 44    * @version $Revision: 571948 $, $Date: 2007-09-02 03:51:37 -0700 (Sun, 02 Sep 2007) $
 45    */
 46    public final class ElementImpl extends ContainerNodeImpl
 47    implements Element {
 48   
 49    private static final Log log = LogFactory.getLog(ElementImpl.class);
 50   
 51    // private static final String SRC_NS = XMLNS_PREFIX + ":src";
 52    // private static final String SRC_COL = "src:" + NodeSource.SOURCE_COL;
 53    // private static final String SRC_KEY = "src:" + NodeSource.SOURCE_KEY;
 54   
 55    private NamedNodeMapImpl attributes = new NamedNodeMapImpl(this);
 56    private short symbolID = -1;
 57   
 58   
 59  0 public ElementImpl() {
 60    }
 61   
 62  130981 public ElementImpl(NodeImpl parent, byte[] data, int pos, int len) {
 63  130981 super(parent, data, pos, len);
 64  130981 DocumentImpl doc = (DocumentImpl) getOwnerDocument();
 65  130981 try {
 66  130981 loadAttributes(doc.getSymbols());
 67    } catch (Exception e) {
 68  0 if (log.isWarnEnabled()) {
 69  0 log.warn("ignored exception", e);
 70    }
 71    }
 72    }
 73   
 74  0 public ElementImpl(NodeImpl parent, boolean dirty) {
 75  0 super(parent, dirty);
 76    }
 77   
 78  145696 public ElementImpl(NodeImpl parent, String nodeName) {
 79  145696 super(parent, true);
 80  145696 this.nodeName = nodeName;
 81    }
 82   
 83  236663 protected boolean isNodeTypeValid(short type) {
 84  236663 return type == Node.ELEMENT_NODE
 85    || type == Node.COMMENT_NODE
 86    || type == Node.TEXT_NODE
 87    || type == Node.CDATA_SECTION_NODE
 88    || type == Node.ENTITY_REFERENCE_NODE;
 89    }
 90   
 91  2772674 protected void checkLoaded() {
 92  2772675 if (loaded) {
 93  2503607 return;
 94    }
 95   
 96  269058 loaded = true;
 97  269058 try {
 98  269058 if (data != null) {
 99  130901 DocumentImpl doc = (DocumentImpl) getOwnerDocument();
 100  130901 SymbolTable st = doc.getSymbols();
 101   
 102  130901 ByteArrayInput bis = new ByteArrayInput(data, pos, len);
 103  130901 XMLCompressedInput in = new XMLCompressedInput(bis, st);
 104   
 105  130901 in.readSignature(); // Skip The Signature
 106  130901 in.readContentSize(); // Skip The Content Size
 107   
 108  130901 symbolID = in.readShort();
 109  130901 SymbolTable.SymbolInfo si = st.getSymbolInfo(symbolID);
 110  130901 nodeName = si.getQName();
 111  130901 nsURI = si.getNamespaceURI();
 112   
 113  130901 loadChildren(st);
 114    }
 115    } catch (IOException e) {
 116  0 if (log.isWarnEnabled()) {
 117  0 log.warn("ignored exception", e);
 118    }
 119    }
 120    }
 121   
 122  0 public short getSymbolID() {
 123  0 return symbolID;
 124    }
 125   
 126    /**
 127    * Add "src" and "col" attributes in {@link NodeSource#SOURCE_NS} namespace.
 128    */
 129  543 public void expandSource() {
 130  543 NodeSource src = getSource();
 131  543 if (src != null) {
 132  519 final String prefix = sourcePrefix("src", NodeSource.SOURCE_NS);
 133   
 134  519 setAttribute(XMLNS_PREFIX + ":" + prefix, NodeSource.SOURCE_NS);
 135  519 setAttribute(prefix + ":" + NodeSource.SOURCE_COL, src.getCollection().getCanonicalName());
 136  519 Key k = src.getKey();
 137  519 if (k != null) {
 138  519 setAttribute(prefix + ":" + NodeSource.SOURCE_KEY, k.toString());
 139    }
 140    }
 141    }
 142   
 143    /**
 144    * Choose unique prefix for a namespace.
 145    * Reuse existing prefix if namespace is already defined.
 146    */
 147  519 private String sourcePrefix(final String candidatePrefix, final String nsuri) {
 148  519 Element element = this;
 149  519 HashSet prefixes = new HashSet();
 150  519 while (element != null) {
 151  939 NamedNodeMap nm = element.getAttributes();
 152  939 for (int i = 0; i < nm.getLength(); i++) {
 153  846 final Attr a = (Attr) nm.item(i);
 154  846 final String name = a.getNodeName();
 155  846 if (name.startsWith("xmlns:")) {
 156  72 final String prefix = name.substring(6);
 157  72 if (nsuri.equals(a.getValue())) {
 158  63 return prefix;
 159    }
 160  9 prefixes.add(prefix);
 161    }
 162    }
 163  876 element = element.getParentNode().getNodeType() == ELEMENT_NODE ? (Element) element.getParentNode() : null;
 164    }
 165   
 166  456 String result = candidatePrefix;
 167  456 while (prefixes.contains(result)) {
 168  3 result = candidatePrefix + System.currentTimeMillis();
 169    }
 170   
 171  456 return result;
 172    }
 173   
 174  130981 protected void loadAttributes(SymbolTable st) throws IOException {
 175  130981 ByteArrayInput bis = new ByteArrayInput(data, pos, len);
 176  130981 XMLCompressedInput in = new XMLCompressedInput(bis, st);
 177   
 178  130981 in.readSignature();
 179  130981 in.readContentSize();
 180  130981 in.readShort(); // Some "elemSymbol" - symbol ID?
 181  130981 int attrCount = in.readAttributeCount();
 182   
 183  130981 for (int i = 0; i < attrCount; i++) {
 184  80745 short symbol = in.readShort();
 185  80745 short strLen = in.readShort();
 186  80745 byte[] b = new byte[strLen];
 187  80745 in.read(b);
 188  80745 SymbolTable.SymbolInfo si = st.getSymbolInfo(symbol);
 189  80745 String name = si.getQName();
 190  80745 String nsURI = si.getNamespaceURI();
 191  80745 AttrImpl attr = new AttrImpl(this, name, nsURI, symbol, new String(b, "UTF-8"));
 192  80745 attributes.setNamedItem(attr);
 193    }
 194    }
 195   
 196  6122300 public short getNodeType() {
 197  6122295 return Node.ELEMENT_NODE;
 198    }
 199   
 200    /**
 201    * A <code>NamedNodeMap</code> containing the attributes of this node (if it
 202    * is an <code>Element</code>) or <code>null</code> otherwise.
 203    */
 204  463501 public NamedNodeMap getAttributes() {
 205  463501 return attributes;
 206    }
 207   
 208  48 public boolean hasAttributes() {
 209  48 return attributes.size() > 0;
 210    }
 211   
 212  0 public boolean hasAttribute(String name) {
 213  0 return attributes.getNamedItem(name) != null;
 214    }
 215   
 216    /**
 217    * Removes an attribute by name. If the removed attribute has a
 218    * default value it is immediately replaced. If the named
 219    * attribute does not exist, this method has no effect.
 220    *
 221    * @param name The name of the attribute to remove.
 222    * @exception DOMException
 223    * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 224    **/
 225  0 public void removeAttribute(String name) throws DOMException {
 226  0 checkReadOnly();
 227  0 AttrImpl attr = (AttrImpl) attributes.removeNamedItem(name);
 228  0 if (attr != null) {
 229  0 attr.setParentNode((NodeImpl) this.getOwnerDocument());
 230    }
 231  0 setDirty();
 232    }
 233   
 234    /**
 235    * The name of the element. For example, in: &lt;elementExample
 236    * id="demo"&gt; ... &lt;/elementExample&gt; , <code>tagName</code> has
 237    * the value <code>"elementExample"</code>. Note that this is
 238    * case-preserving in XML, as are all of the operations of the DOM. The
 239    * HTML DOM returns the <code>tagName</code> of an HTML element in the
 240    * canonical uppercase form, regardless of the case in the source HTML
 241    * document.
 242    */
 243  18994 public String getTagName() {
 244  18994 return getNodeName();
 245    }
 246   
 247    /**
 248    * Retrieves an attribute value by name.
 249    * @param name The name of the attribute to retrieve.
 250    * @return The <code>Attr</code> value as a string, or the empty string if
 251    * that attribute does not have a specified or default value.
 252    */
 253  761499 public String getAttribute(String name) {
 254  761499 Attr attr = (Attr) attributes.getNamedItem(name);
 255  761500 return attr != null ? attr.getValue() : "";
 256    }
 257   
 258    /**
 259    * Adds a new attribute. If an attribute with that name is already present
 260    * in the element, it is replaced by the new one.
 261    * @param newAttr The <code>Attr</code> node to add to the attribute list.
 262    * @return If the <code>newAttr</code> attribute replaces an existing
 263    * attribute with the same name, the previously existing
 264    * <code>Attr</code> node is returned, otherwise <code>null</code> is
 265    * returned.
 266    * @exception DOMException
 267    * WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from a
 268    * different document than the one that created the element.
 269    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 270    * <br>INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already an
 271    * attribute of another <code>Element</code> object. The DOM user must
 272    * explicitly clone <code>Attr</code> nodes to re-use them in other
 273    * elements.
 274    */
 275  137282 public Attr setAttributeNode(Attr newAttr) throws DOMException {
 276  137282 checkReadOnly();
 277  137282 Attr oldAttr = (Attr) attributes.getNamedItem(newAttr.getName());
 278  137282 DocumentImpl doc = (DocumentImpl) getOwnerDocument();
 279  137282 if (newAttr.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
 280  0 throw EX_INUSE_ATTRIBUTE;
 281    }
 282  137282 if (doc != newAttr.getOwnerDocument()) {
 283  0 throw EX_WRONG_DOCUMENT;
 284    }
 285  137282 ((AttrImpl) newAttr).setParentNode(this);
 286  137282 attributes.setNamedItem(newAttr);
 287  137282 setDirty();
 288  137282 return oldAttr;
 289    }
 290   
 291    /**
 292    * Adds a new attribute. If an attribute with that name is already present
 293    * in the element, its value is changed to be that of the value parameter.
 294    * This value is a simple string, it is not parsed as it is being set. So
 295    * any markup (such as syntax to be recognized as an entity reference) is
 296    * treated as literal text, and needs to be appropriately escaped by the
 297    * implementation when it is written out. In order to assign an attribute
 298    * value that contains entity references, the user must create an
 299    * <code>Attr</code> node plus any <code>Text</code> and
 300    * <code>EntityReference</code> nodes, build the appropriate subtree, and
 301    * use <code>setAttributeNode</code> to assign it as the value of an
 302    * attribute.
 303    * @param name The name of the attribute to create or alter.
 304    * @param value Value to set in string form.
 305    * @exception DOMException
 306    * INVALID_CHARACTER_ERR: Raised if the specified name contains an
 307    * invalid character.
 308    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 309    */
 310  131154 public void setAttribute(String name, String value) throws DOMException {
 311  131154 Attr attr = getOwnerDocument().createAttribute(name);
 312  131154 attr.setValue(value);
 313  131154 setAttributeNode(attr);
 314    }
 315   
 316    /**
 317    * Retrieves an <code>Attr</code> node by name.
 318    * @param name The name of the attribute to retrieve.
 319    * @return The <code>Attr</code> node with the specified attribute name or
 320    * <code>null</code> if there is no such attribute.
 321    */
 322  4 public Attr getAttributeNode(String name) {
 323  4 return (Attr) attributes.getNamedItem(name);
 324    }
 325   
 326    /**
 327    * Removes the specified attribute.
 328    * @param oldAttr The <code>Attr</code> node to remove from the attribute
 329    * list. If the removed <code>Attr</code> has a default value it is
 330    * immediately replaced.
 331    * @return The <code>Attr</code> node that was removed.
 332    * @exception DOMException
 333    * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 334    * <br>NOT_FOUND_ERR: Raised if <code>oldAttr</code> is not an attribute
 335    * of the element.
 336    **/
 337  0 public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
 338  0 checkReadOnly();
 339  0 if (oldAttr.getParentNode() != this) {
 340  0 throw EX_NOT_FOUND;
 341    }
 342  0 attributes.remove(oldAttr.getName());
 343  0 ((AttrImpl) oldAttr).setParentNode((NodeImpl) this.getOwnerDocument());
 344  0 setDirty();
 345  0 return oldAttr;
 346    }
 347   
 348    /**
 349    * Retrieves an attribute value by local name and namespace URI.
 350    * HTML-only DOM implementations do not need to implement this method.
 351    * @param namespaceURI The namespace URI of the attribute to retrieve.
 352    * @param localName The local name of the attribute to retrieve.
 353    * @return The <code>Attr</code> value as a string, or the empty
 354    * string if that attribute does not have a specified or default
 355    * value.
 356    * @since DOM Level 2
 357    */
 358  354 public String getAttributeNS(String namespaceURI, String localName) {
 359  354 Attr attr = (Attr) attributes.getNamedItemNS(namespaceURI, localName);
 360  354 return attr != null ? attr.getValue() : "";
 361    }
 362   
 363    /**
 364    * Adds a new attribute. If an attribute with the same local name and
 365    * namespace URI is already present on the element, its prefix is changed
 366    * to be the prefix part of the <code>qualifiedName</code> , and its
 367    * value is changed to be the <code>value</code> parameter. This value is
 368    * a simple string; it is not parsed as it is being set. So any markup
 369    * (such as syntax to be recognized as an entity reference) is treated as
 370    * literal text, and needs to be appropriately escaped by the
 371    * implementation when it is written out. In order to assign an attribute
 372    * value that contains entity references, the user must create an
 373    * <code>Attr</code> node plus any <code>Text</code> and
 374    * <code>EntityReference</code> nodes, build the appropriate subtree, and
 375    * use <code>setAttributeNodeNS</code> or <code>setAttributeNode</code> to
 376    * assign it as the value of an attribute.
 377    * <br> HTML-only DOM implementations do not need to implement this method.
 378    * @param namespaceURI The namespace URI of the attribute to create or
 379    * alter.
 380    * @param qualifiedName The qualified name of the attribute to create or
 381    * alter.
 382    * @param value The value to set in string form.
 383    * @exception DOMException
 384    * INVALID_CHARACTER_ERR: Raised if the specified qualified name
 385    * contains an illegal character.
 386    * <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 387    * <br> NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is
 388    * malformed, if the <code>qualifiedName</code> has a prefix and the
 389    * <code>namespaceURI</code> is <code>null</code> or an empty string,
 390    * if the <code>qualifiedName</code> has a prefix that is "xml" and the
 391    * <code>namespaceURI</code> is different from
 392    * "http://www.w3.org/XML/1998/namespace", if the
 393    * <code>qualifiedName</code> has a prefix that is "xmlns" and the
 394    * <code>namespaceURI</code> is different from
 395    * "http://www.w3.org/2000/xmlns/", or if the <code>qualifiedName</code>
 396    * is "xmlns" and the <code>namespaceURI</code> is different from
 397    * "http://www.w3.org/2000/xmlns/".
 398    * @since DOM Level 2
 399    */
 400  986 public void setAttributeNS(String namespaceURI, String qualifiedName, String value) {
 401  986 Attr attr = getOwnerDocument().createAttributeNS(namespaceURI, qualifiedName);
 402  986 attr.setValue(value);
 403  986 setAttributeNodeNS(attr);
 404    }
 405   
 406    /**
 407    * Removes an attribute by local name and namespace URI. If the
 408    * removed attribute has a default value it is immediately
 409    * replaced. The replacing attribute has the same namespace URI
 410    * and local name, as well as the original prefix.
 411    *
 412    * <br> HTML-only DOM implementations do not need to implement this
 413    * method.
 414    *
 415    * @param namespaceURI The namespace URI of the attribute to remove.
 416    * @param localName The local name of the attribute to remove.
 417    * @exception DOMException
 418    * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 419    * @since DOM Level 2
 420    **/
 421  2 public void removeAttributeNS(String namespaceURI, String localName) {
 422  2 checkReadOnly();
 423  2 AttrImpl attr = (AttrImpl) attributes.removeNamedItemNS(namespaceURI, localName);
 424  2 if (attr != null) {
 425  0 attr.setParentNode((NodeImpl) this.getOwnerDocument());
 426    }
 427  2 setDirty();
 428    }
 429   
 430    /**
 431    * Retrieves an <code>Attr</code> node by local name and namespace URI.
 432    * HTML-only DOM implementations do not need to implement this method.
 433    * @param namespaceURI The namespace URI of the attribute to retrieve.
 434    * @param localName The local name of the attribute to retrieve.
 435    * @return The <code>Attr</code> node with the specified attribute local
 436    * name and namespace URI or <code>null</code> if there is no such
 437    * attribute.
 438    * @since DOM Level 2
 439    */
 440  0 public Attr getAttributeNodeNS(String namespaceURI, String localName) {
 441  0 return (Attr) attributes.getNamedItemNS(namespaceURI, localName);
 442    }
 443   
 444    /**
 445    * Adds a new attribute. If an attribute with that local name and that
 446    * namespace URI is already present in the element, it is replaced by the
 447    * new one.
 448    * <br> HTML-only DOM implementations do not need to implement this method.
 449    * @param newAttr The <code>Attr</code> node to add to the attribute list.
 450    * @return If the <code>newAttr</code> attribute replaces an existing
 451    * attribute with the same local name and namespace URI , the
 452    * replaced <code>Attr</code> node is returned, otherwise
 453    * <code>null</code> is returned.
 454    * @exception DOMException
 455    * WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from
 456    * a different document than the one that created the element.
 457    * <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 458    * <br> INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already
 459    * an attribute of another <code>Element</code> object. The DOM user
 460    * must explicitly clone <code>Attr</code> nodes to re-use them in
 461    * other elements.
 462    * @since DOM Level 2
 463    */
 464  986 public Attr setAttributeNodeNS(Attr newAttr) {
 465  986 return setAttributeNode(newAttr);
 466    }
 467   
 468    /**
 469    * @since DOM Level 2
 470    */
 471  0 public boolean hasAttributeNS(String namespaceURI, String localName) {
 472  0 return attributes.getNamedItemNS(namespaceURI, localName) != null;
 473    }
 474   
 475    //
 476    // DOM Level 3 Implementation
 477    //
 478   
 479    /**
 480    * @since DOM Level 3
 481    */
 482  0 public TypeInfo getSchemaTypeInfo() {
 483  0 return null;
 484    }
 485   
 486    /**
 487    * @since DOM Level 3
 488    */
 489  0 public void setIdAttribute(String name, boolean isId) throws DOMException {
 490    }
 491   
 492    /**
 493    * @since DOM Level 3
 494    */
 495  0 public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException {
 496    }
 497   
 498    /**
 499    * @since DOM Level 3
 500    */
 501  0 public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException {
 502    }
 503   
 504    /**
 505    * @since DOM Level 3
 506    */
 507  18 public boolean isEqualNode(Node other) {
 508  18 if (!super.isEqualNode(other)) {
 509  4 return false;
 510    }
 511   
 512    // Attributes of the nodes have to be the same, but can have different order
 513  14 if (!hasAttributes() && !other.hasAttributes()) {
 514  0 return true;
 515    }
 516  14 if (!hasAttributes() || !other.hasAttributes()) {
 517  0 return false;
 518    }
 519   
 520  14 NamedNodeMap attrMap = getAttributes();
 521  14 NamedNodeMap otherAttrMap = other.getAttributes();
 522  14 if (attrMap.getLength() != otherAttrMap.getLength()) {
 523  0 return false;
 524    }
 525   
 526  14 for (int i = 0; i < attrMap.getLength(); i++) {
 527  28 String name = attrMap.item(i).getNodeName();
 528  28 Node attr = otherAttrMap.getNamedItem(name);
 529  28 if (attr == null || !attrMap.item(i).getNodeValue().equals(attr.getNodeValue())) {
 530  2 return false;
 531    }
 532    }
 533   
 534  12 return true;
 535    }
 536   
 537  6 Node renameNode(String namespaceURI, String qualifiedName, String prefix) throws DOMException {
 538  6 checkLoaded();
 539  6 checkReadOnly();
 540   
 541  6 nodeName = qualifiedName;
 542  6 if (prefix != null) {
 543  4 setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + prefix, namespaceURI);
 544  4 nsURI = namespaceURI;
 545    } else {
 546  2 removeAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + nsURI);
 547  2 nsURI = null;
 548    }
 549   
 550  6 invokeHandlers(UserDataHandler.NODE_RENAMED, this, null);
 551  6 return this;
 552    }
 553    }