View Javadoc
1 /* 2 * Copyright (C) 2002 Carsten Krebs (Team-Konzept GmbH & Co.KG) 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 package com.teamkonzept.dom4jb.saxon; 19 20 import java.util.HashMap; 21 22 import javax.xml.transform.TransformerException; 23 24 import org.w3c.dom.NamedNodeMap; 25 26 import com.icl.saxon.om.Axis; 27 import com.icl.saxon.om.AxisEnumeration; 28 import com.icl.saxon.om.DocumentInfo; 29 import com.icl.saxon.om.EmptyEnumeration; 30 import com.icl.saxon.om.NamePool; 31 import com.icl.saxon.om.NodeInfo; 32 import com.icl.saxon.om.SingletonEnumeration; 33 import com.icl.saxon.output.Outputter; 34 import com.icl.saxon.pattern.AnyNodeTest; 35 import com.icl.saxon.pattern.NodeTest; 36 import com.teamkonzept.dom4jb.dom.NodeList; 37 import com.teamkonzept.dom4jb.schema.ElementDescriptor; 38 39 public class Element 40 extends com.teamkonzept.dom4jb.dom.Element 41 implements NodeInfo, AxisNode { 42 43 private static final int LOW_20_MASK = 0xfffff; 44 private DescendantAxis axis; 45 private int position; 46 private Namespace[] namespaces; 47 private int nameCode; 48 49 public Element(final Document document, 50 final ElementDescriptor descriptor, 51 final Object bean) { 52 super(document, descriptor, bean); 53 this.axis = null; 54 this.position = -1; 55 this.namespaces = null; 56 this.nameCode = -1; 57 } 58 59 /*** Creates new Element */ 60 public Element(final Document document, 61 final String qualifiedName, final Object bean) { 62 super(document, qualifiedName, bean); 63 this.axis = null; 64 this.position = -1; 65 this.namespaces = null; 66 this.nameCode = -1; 67 } 68 69 public synchronized void attachAxis(final int position, 70 final DescendantAxis axis) { 71 this.axis = axis; 72 this.position = position; 73 } 74 75 public DescendantAxis getAxis() { 76 return axis; 77 } 78 79 public int getPosition() { 80 return position; 81 } 82 83 /*** 84 * Return the string value of the node. The interpretation of this depends 85 * on the type of node. For an element it is the accumulated character 86 * content of the element, including descendant elements. 87 * @return the string value of the node 88 */ 89 public String getStringValue() { 90 final StringBuffer sb = new StringBuffer(); 91 expandStringValue(sb); 92 return sb.toString(); 93 } 94 95 public void expandStringValue(final StringBuffer sb) { 96 final NodeList list = this._getChildNodes(); 97 int i = 0; 98 for (org.w3c.dom.Node node = list.item(i); 99 node != null; 100 node = list.item(++i)) { 101 ((AxisNode) node).expandStringValue(sb); 102 } 103 } 104 105 /*** 106 * Get the display name of this node. For elements and attributes this 107 * is [prefix:]localname. For unnamed nodes, it is an empty string. 108 * @return The display name of this node. 109 * For a node with no name, return an empty string. 110 */ 111 public String getDisplayName() { 112 return getNodeName(); 113 } 114 115 /*** 116 * Get the value of a given attribute of this node 117 * @param fingerprint The fingerprint of the attribute name 118 * @return the attribute value if it exists or null if not 119 */ 120 public String getAttributeValue(final int fingerprint) { 121 final NamedNodeMap attrs = getAttributes(); 122 final NamePool pool = ((Document) document).getNamePool(); 123 124 int i = 0; 125 for (org.w3c.dom.Node att = attrs.item(i); 126 att != null; 127 att = attrs.item(++i)) { 128 final String prefix = att.getPrefix(); 129 final String uri = att.getNamespaceURI(); 130 String name = att.getLocalName(); 131 if (name == null) { 132 name = att.getNodeName(); 133 } 134 int nameCode = 135 pool.allocate( 136 prefix == null ? "" : prefix.intern(), 137 uri == null ? "" : uri.intern(), 138 name == null ? "" : name.intern()); 139 if (fingerprint == (nameCode & LOW_20_MASK)) { 140 return att.getNodeValue(); 141 } 142 } 143 144 return null; 145 } 146 147 public Namespace[] getNamespaces() { 148 if (namespaces != null) { 149 return namespaces; 150 } 151 152 final Namespace[] parentNS = 153 parent != null 154 ? ((AxisNode) parent).getNamespaces() 155 : new Namespace[0]; 156 HashMap nslist = null; 157 158 if (parentNS.length > 0) { 159 nslist = new HashMap(); 160 for (int i = parentNS.length - 1; i >= 0; i--) { 161 final Namespace ns = parentNS[i]; 162 final String prefix = ns.getLocalName(); 163 nslist.put( 164 prefix, 165 new Namespace(prefix, ns.getStringValue(), this)); 166 } 167 } 168 169 final NamedNodeMap attrs = getAttributes(); 170 int i = 0; 171 for (org.w3c.dom.Node attr = attrs.item(i); 172 attr != null; 173 attr = attrs.item(++i)) { 174 final String attrPrefix = attr.getPrefix(); 175 if (attrPrefix.equals("xmlns")) { 176 final String prefix = attr.getLocalName(); 177 if (nslist == null) { 178 nslist = new HashMap(); 179 } 180 nslist.put( 181 prefix, 182 new Namespace(prefix, attr.getNodeValue(), this)); 183 } 184 } 185 186 if (nslist == null) { 187 namespaces = new Namespace[0]; 188 } else { 189 namespaces = 190 (Namespace[]) nslist.values().toArray( 191 new Namespace[nslist.size()]); 192 } 193 194 return namespaces; 195 } 196 197 /*** 198 * Find the value of a given attribute of this node. <BR> 199 * This method is defined on all nodes to meet XSL requirements, but 200 * for nodes other than elements it will always return null. 201 * @param uri the namespace uri of an attribute ("" if no namespace) 202 * @param localName the local name of the attribute 203 * @return the value of the attribute, if it exists, otherwise null 204 */ 205 public String getAttributeValue(final String uri, final String localName) { 206 final String val; 207 if (uri.equals("")) { 208 val = getAttribute(localName); 209 } else { 210 val = getAttributeNS(uri, localName); 211 } 212 return val == null ? "" : val; 213 } 214 215 /*** 216 * Get a character string that uniquely identifies this node.<br /> 217 * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b) 218 * @return a string that uniquely identifies this node, within this 219 * document. The calling code prepends information to make the result 220 * unique across all documents. 221 */ 222 public String generateId() { 223 final StringBuffer buf = new StringBuffer("j"); 224 buf.append(hashCode()); 225 return buf.toString(); 226 } 227 228 /*** 229 * Copy this node to a given outputter (deep copy) 230 */ 231 public void copy(final Outputter out) throws TransformerException { 232 final int nc = getNameCode(); 233 out.writeStartTag(nc); 234 235 // output the namespaces 236 outputNamespaceNodes(out, true); 237 238 // output the attributes 239 final AxisEnumeration attributes = 240 getEnumeration(Axis.ATTRIBUTE, AnyNodeTest.getInstance()); 241 while (attributes.hasMoreElements()) { 242 attributes.nextElement().copy(out); 243 } 244 245 // output the children 246 final AxisEnumeration children = 247 getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()); 248 while (children.hasMoreElements()) { 249 children.nextElement().copy(out); 250 } 251 252 // finally the end tag 253 out.writeEndTag(nc); 254 } 255 256 /*** 257 * Output all namespace nodes associated with this element. Does nothing if 258 * the node is not an element. 259 * @param out The relevant outputter 260 * @param includeAncestors True if namespaces declared on ancestor 261 * elements must be output; false if it is known that these are already 262 * on the result tree 263 */ 264 public void outputNamespaceNodes( 265 final Outputter out, 266 final boolean includeAncestors) 267 throws TransformerException { 268 // %TODO% includeAncestors wird z.Z. überhaupt nicht berücksichtigt! 269 final NamePool pool = ((Document) document).getNamePool(); 270 final Namespace[] nslist = getNamespaces(); 271 for (int i = nslist.length - 1; i >= 0; i--) { 272 final Namespace ns = nslist[i]; 273 final int nscode = 274 pool.allocateNamespaceCode(ns.getPrefix(), ns.getURI()); 275 out.writeNamespaceDeclaration(nscode); 276 } 277 } 278 279 // ------- GENERAL stuff ------- 280 281 /*** 282 * Get the System ID for the node. 283 * @return the System Identifier of the entity in the source document 284 * containing the node, or null if not known. Note this is not the same 285 * as the base URI: the base URI can be modified by xml:base, but the 286 * system ID cannot. 287 */ 288 public String getSystemId() { 289 return ((Document) document).getSystemId(); 290 } 291 292 public void setSystemId(final String uri) { 293 ((Document) document).setSystemId(uri); 294 } 295 296 /*** 297 * Get the Base URI for the node, that is, the URI used for resolving 298 * a relative URI contained in the node. This will be the same as the 299 * System ID unless xml:base has been used. 300 */ 301 public String getBaseURI() { 302 return getSystemId(); 303 } 304 305 /*** 306 * Get line number 307 * @return the line number of the node in its original source document; 308 * or -1 if not available 309 */ 310 public int getLineNumber() { 311 return -1; 312 } 313 314 /*** 315 * Get name code. The name code is a coded form of the node name: two nodes 316 * with the same name code have the same namespace URI, the same local name, 317 * and the same prefix. By masking the name code with &0xfffff, you get a 318 * fingerprint: two nodes with the same fingerprint have the same local name 319 * and namespace URI. 320 * @see com.icl.saxon.om.NamePool#allocate allocate 321 */ 322 public int getNameCode() { 323 if (nameCode == -1) { 324 nameCode = 325 ((Document) document).getNamePool().allocate( 326 getPrefix(), 327 getURI(), 328 getLocalName()); 329 } 330 return nameCode; 331 } 332 333 /*** 334 * Get the prefix part of the name of this node. This is the name before 335 * the ":" if any. 336 * @return the prefix part of the name. For an unnamed node, return an 337 * empty string. 338 */ 339 public String getPrefix() { 340 final String prefix = super.getPrefix(); 341 return prefix == null ? "" : prefix; 342 } 343 344 /*** 345 * Get fingerprint. The fingerprint is a coded form of the expanded name 346 * of the node: two nodes 347 * with the same name code have the same namespace URI and the same 348 * local name. A fingerprint of -1 should be returned for a node with 349 * no name. 350 */ 351 public int getFingerprint() { 352 return getNameCode() & LOW_20_MASK; 353 } 354 355 /*** 356 * Get the URI part of the name of this node. This is the URI corresponding 357 * to the prefix, or the URI of the default namespace if appropriate. 358 * @return The URI of the namespace of this node. For an unnamed node, 359 * return null. For a node with an empty prefix, return an empty string. 360 */ 361 public String getURI() { 362 if (super.getPrefix() == null) { 363 return ""; 364 } 365 final String uri = this.getNamespaceURI(); 366 return uri == null ? "" : uri; 367 } 368 369 /*** 370 * Get the prefix part of the name of this node. This is the name before 371 * the ":" if any. 372 * @return the prefix part of the name. For an unnamed node, return an 373 * empty string. 374 */ 375 public String getLocalName() { 376 final String localName = super.getLocalName(); 377 return localName == null ? getNodeName() : localName; 378 } 379 380 /*** 381 * Get the NodeInfo object representing the parent of this node 382 */ 383 public NodeInfo getParent() { 384 return (NodeInfo) getParentNode(); 385 } 386 387 /*** 388 * Get the root (document) node 389 * @return the DocumentInfo representing the containing document 390 */ 391 public DocumentInfo getDocumentRoot() { 392 return (DocumentInfo) document; 393 } 394 395 /*** 396 * Copy the string-value of this node to a given outputter 397 */ 398 public void copyStringValue(final Outputter out) 399 throws TransformerException { 400 out.writeContent(getStringValue()); 401 } 402 403 /*** 404 * Return an enumeration over the nodes reached by the given axis from 405 * this node 406 * @param nodeType the type(s) of node to be included, 407 * e.g. NodeInfo.ELEMENT, NodeInfo.TEXT. The value NodeInfo.NODE means 408 * include any type of node. 409 * @param nodeTest A pattern to be matched by the returned nodes 410 * @return a NodeEnumeration that scans the nodes reached by the axis 411 * in turn. 412 */ 413 public AxisEnumeration getEnumeration( 414 final byte axisNumber, 415 final NodeTest nodeTest) { 416 switch (axisNumber) { 417 case Axis.ANCESTOR : 418 return new FilterEnumeration( 419 new AncestorEnumeration(this, false), 420 nodeTest); 421 422 case Axis.ANCESTOR_OR_SELF : 423 return new FilterEnumeration( 424 new AncestorEnumeration(this, true), 425 nodeTest); 426 427 case Axis.ATTRIBUTE : 428 return new FilterEnumeration( 429 new AttributeEnumeration(this), 430 nodeTest); 431 432 case Axis.CHILD : 433 if (hasChildNodes()) { 434 return new FilterEnumeration( 435 new ChildEnumeration(this), 436 nodeTest); 437 } else { 438 return EmptyEnumeration.getInstance(); 439 } 440 441 case Axis.DESCENDANT : 442 if (hasChildNodes()) { 443 return new FilterEnumeration( 444 new DescendantEnumeration(this, false), 445 nodeTest); 446 } else { 447 return EmptyEnumeration.getInstance(); 448 } 449 450 case Axis.DESCENDANT_OR_SELF : 451 return new FilterEnumeration( 452 new DescendantEnumeration(this, true), 453 nodeTest); 454 455 case Axis.FOLLOWING : 456 return new FilterEnumeration( 457 new FollowingEnumeration(this), 458 nodeTest); 459 460 case Axis.FOLLOWING_SIBLING : 461 return new FilterEnumeration( 462 new SiblingEnumeration(this, true), 463 nodeTest); 464 465 case Axis.NAMESPACE : 466 return new FilterEnumeration( 467 new NamespaceEnumeration(this), 468 nodeTest); 469 470 case Axis.PARENT : 471 if (parent == null) 472 return EmptyEnumeration.getInstance(); 473 if (nodeTest.matches((NodeInfo) parent)) 474 return new SingletonEnumeration((NodeInfo) parent); 475 return EmptyEnumeration.getInstance(); 476 477 case Axis.PRECEDING : 478 return new FilterEnumeration( 479 new PrecedingEnumeration(this, false), 480 nodeTest); 481 482 case Axis.PRECEDING_SIBLING : 483 return new FilterEnumeration( 484 new SiblingEnumeration(this, false), 485 nodeTest); 486 487 case Axis.SELF : 488 if (nodeTest.matches(this)) 489 return new SingletonEnumeration(this); 490 return EmptyEnumeration.getInstance(); 491 492 case Axis.PRECEDING_OR_ANCESTOR : 493 return new FilterEnumeration( 494 new PrecedingEnumeration(this, true), 495 nodeTest); 496 497 default : 498 throw new IllegalArgumentException( 499 "Unknown axis number " + axisNumber); 500 } 501 } 502 503 /*** 504 * Determine the relative position of this node and another node, in 505 * document order. The other node will always be in the same document. 506 * @param other The other node, whose position is to be compared with this 507 * node 508 * @return -1 if this node precedes the other node, +1 if it follows the 509 * other node, or 0 if they are the same node. (In this case, 510 * isSameNode() will always return true, and the two nodes will produce 511 * the same result for generateId()) 512 */ 513 public int compareOrder(final NodeInfo other) { 514 // are they the same node? 515 if (this == other || this.isSameNode(other)) { 516 return 0; 517 } 518 519 // are they siblings (common case) 520 if (this.getParent().isSameNode(other.getParent())) { 521 return this.getChildIndex() 522 - ((com.teamkonzept.dom4jb.dom.Node) other).getChildIndex(); 523 } 524 525 // are they evaluated on the same descendant axis 526 DescendantAxis axis; 527 int axisIdx = 0; 528 synchronized (this) { 529 axis = this.getAxis(); 530 if (axis != null) { 531 axisIdx = this.getPosition(); 532 } 533 } 534 AxisNode node = (AxisNode) other; 535 if (axis != null) { 536 synchronized (node) { 537 final DescendantAxis otherAxis = node.getAxis(); 538 if (otherAxis == axis) { 539 return axisIdx - node.getPosition(); 540 } 541 } 542 } 543 544 // find the depths of both nodes in the tree 545 int depth1 = 0; 546 int depth2 = 0; 547 NodeInfo p1 = this; 548 NodeInfo p2 = other; 549 while (p1 != null) { 550 depth1++; 551 p1 = p1.getParent(); 552 } 553 while (p2 != null) { 554 depth2++; 555 p2 = p2.getParent(); 556 } 557 558 // move up one branch of the tree so we have two nodes on the same level 559 p1 = this; 560 while (depth1 > depth2) { 561 p1 = p1.getParent(); 562 depth1--; 563 } 564 565 p2 = other; 566 while (depth2 > depth1) { 567 p2 = p2.getParent(); 568 depth2--; 569 } 570 571 // now move up both branches in sync until we find a common parent 572 while (true) { 573 NodeInfo par1 = p1.getParent(); 574 NodeInfo par2 = p2.getParent(); 575 if (par1 == null || par2 == null) { 576 throw new NullPointerException( 577 "DOM tree compare" + " - internal error"); 578 } 579 if (par1.isSameNode(par2)) { 580 return ((com.teamkonzept.dom4jb.dom.Node) p1).getChildIndex() 581 - ((com.teamkonzept.dom4jb.dom.Node) p2).getChildIndex(); 582 } 583 p1 = par1; 584 p2 = par2; 585 } 586 } 587 588 /*** 589 * Determine whether this is the same node as another node. <br /> 590 * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b) 591 * @return true if this Node object and the supplied Node object represent 592 * the same node in the tree. 593 */ 594 public boolean isSameNode(final NodeInfo other) { 595 if (this == other) { 596 return true; 597 } 598 if (other == null 599 || this.getNodeType() != other.getNodeType() 600 || !(other instanceof com.teamkonzept.dom4jb.dom.Node) 601 || getChildIndex() 602 != ((com.teamkonzept.dom4jb.dom.Node) other).getChildIndex()) { 603 return false; 604 } 605 return this.equals(other); 606 } 607 608 }

This page was automatically generated by Maven