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