1 package net.sf.saxon.dom; 2 import net.sf.saxon.Configuration; 3 import net.sf.saxon.event.Receiver; 4 import net.sf.saxon.om.*; 5 import net.sf.saxon.pattern.NameTest; 6 import net.sf.saxon.pattern.NodeTest; 7 import net.sf.saxon.style.StandardNames; 8 import net.sf.saxon.trans.XPathException; 9 import net.sf.saxon.type.Type; 10 import net.sf.saxon.value.UntypedAtomicValue; 11 import net.sf.saxon.value.Value; 12 import org.w3c.dom.*; 13 14 import java.lang.reflect.InvocationTargetException ; 15 import java.lang.reflect.Method ; 16 import java.util.ArrayList ; 17 18 19 23 24 public class NodeWrapper implements NodeInfo, VirtualNode, SiblingCountingNode { 25 26 protected Node node; 27 private int namecode = -1; 28 protected short nodeKind; 29 private NodeWrapper parent; protected DocumentWrapper docWrapper; 31 protected int index; protected int span = 1; 35 42 protected NodeWrapper(Node node, NodeWrapper parent, int index) { 43 this.node = node; 44 this.parent = parent; 45 this.index = index; 46 } 47 48 56 protected NodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper) { 57 if (node == null) { 58 throw new NullPointerException ("NodeWrapper#makeWrapper: Node must not be null"); 59 } 60 if (docWrapper == null) { 61 throw new NullPointerException ("NodeWrapper#makeWrapper: DocumentWrapper must not be null"); 62 } 63 return makeWrapper(node, docWrapper, null, -1); 64 } 65 66 75 76 protected NodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper, 77 NodeWrapper parent, int index) { 78 NodeWrapper wrapper; 79 switch (node.getNodeType()) { 80 case Node.DOCUMENT_NODE: 81 return docWrapper; 82 case Node.ELEMENT_NODE: 83 wrapper = new NodeWrapper(node, parent, index); 84 wrapper.nodeKind = Type.ELEMENT; 85 break; 86 case Node.ATTRIBUTE_NODE: 87 wrapper = new NodeWrapper(node, parent, index); 88 wrapper.nodeKind = Type.ATTRIBUTE; 89 break; 90 case Node.TEXT_NODE: 91 wrapper = new NodeWrapper(node, parent, index); 92 wrapper.nodeKind = Type.TEXT; 93 break; 94 case Node.CDATA_SECTION_NODE: 95 wrapper = new NodeWrapper(node, parent, index); 96 wrapper.nodeKind = Type.TEXT; 97 break; 98 case Node.COMMENT_NODE: 99 wrapper = new NodeWrapper(node, parent, index); 100 wrapper.nodeKind = Type.COMMENT; 101 break; 102 case Node.PROCESSING_INSTRUCTION_NODE: 103 wrapper = new NodeWrapper(node, parent, index); 104 wrapper.nodeKind = Type.PROCESSING_INSTRUCTION; 105 break; 106 default: 107 throw new IllegalArgumentException ("Unsupported node type in DOM! " + node.getNodeType() + " instance " + node.toString()); 108 } 109 wrapper.docWrapper = docWrapper; 110 return wrapper; 111 } 112 113 116 117 public Object getUnderlyingNode() { 118 return node; 119 } 120 121 124 125 public Configuration getConfiguration() { 126 return docWrapper.getConfiguration(); 127 } 128 129 133 134 public NamePool getNamePool() { 135 return docWrapper.getNamePool(); 136 } 137 138 142 143 public int getNodeKind() { 144 return nodeKind; 145 } 146 147 150 151 public SequenceIterator getTypedValue() { 152 return SingletonIterator.makeIterator(new UntypedAtomicValue(getStringValueCS())); 153 } 154 155 165 166 public Value atomize() throws XPathException { 167 return new UntypedAtomicValue(getStringValueCS()); 168 } 169 170 174 175 public int getTypeAnnotation() { 176 return -1; 177 } 178 179 185 186 public boolean isSameNodeInfo(NodeInfo other) { 187 190 if (!(other instanceof NodeWrapper)) { 191 return false; 192 } 193 194 if (docWrapper.level3) { 196 try { 197 Class [] argClasses = {Node.class}; 198 Method isSameNode = Node.class.getMethod("isSameNode", argClasses); 199 Object [] args = {((NodeWrapper)other).node}; 200 Boolean b = (Boolean )(isSameNode.invoke(node, args)); 201 return b.booleanValue(); 202 } catch (NoSuchMethodException e) { 203 } catch (IllegalAccessException e) { 205 } catch (InvocationTargetException e) { 207 } 209 } 210 NodeWrapper ow = (NodeWrapper)other; 211 return getNodeKind()==ow.getNodeKind() && 212 getNameCode()==ow.getNameCode() && getSiblingPosition()==ow.getSiblingPosition() && 214 getParent().isSameNodeInfo(ow.getParent()); 215 } 219 220 226 227 public String getSystemId() { 228 return docWrapper.baseURI; 229 } 230 231 public void setSystemId(String uri) { 232 docWrapper.baseURI = uri; 233 } 234 235 239 240 public String getBaseURI() { 241 NodeInfo n = this; 242 if (getNodeKind() != Type.ELEMENT) { 243 n = getParent(); 244 } 245 while (n != null) { 247 String xmlbase = n.getAttributeValue(StandardNames.XML_BASE); 248 if (xmlbase != null) { 249 return xmlbase; 250 } 251 n = n.getParent(); 252 } 253 return docWrapper.baseURI; 255 } 256 257 261 262 public int getLineNumber() { 263 return -1; 264 } 265 266 274 275 public int compareOrder(NodeInfo other) { 276 if (docWrapper.level3 && other instanceof NodeWrapper) { 278 if (isSameNodeInfo(other)) { 279 return 0; 280 } 281 try { 282 Class [] argClasses = {Node.class}; 283 Method compareDocumentPosition = Node.class.getMethod("compareDocumentPosition", argClasses); 284 Object [] args = {((NodeWrapper)other).node}; 285 Short i = (Short )(compareDocumentPosition.invoke(node, args)); 286 switch (i.shortValue()) { 287 case 2: case 8: return +1; 291 case 4: case 16: return -1; 294 default: 295 } 297 } catch (NoSuchMethodException e) { 298 } catch (IllegalAccessException e) { 300 } catch (InvocationTargetException e) { 302 } catch (DOMException e) { 304 } 306 } 307 308 if (other instanceof SiblingCountingNode) { 309 return Navigator.compareOrder(this, (SiblingCountingNode)other); 310 } else { 311 return -other.compareOrder(this); 313 } 314 } 315 316 322 323 public String getStringValue() { 324 return getStringValueCS().toString(); 325 326 } 327 328 332 333 public CharSequence getStringValueCS() { 334 switch (nodeKind) { 336 case Type.DOCUMENT: 337 case Type.ELEMENT: 338 NodeList children1 = node.getChildNodes(); 339 StringBuffer sb1 = new StringBuffer (16); 340 expandStringValue(children1, sb1); 341 return sb1; 342 343 case Type.ATTRIBUTE: 344 return ((Attr)node).getValue(); 345 346 case Type.TEXT: 347 if (span == 1) { 348 return node.getNodeValue(); 349 } else { 350 FastStringBuffer fsb = new FastStringBuffer(100); 351 Node textNode = node; 352 for (int i=0; i<span; i++) { 353 fsb.append(textNode.getNodeValue()); 354 textNode = textNode.getNextSibling(); 355 } 356 return fsb.condense(); 357 } 358 359 case Type.COMMENT: 360 case Type.PROCESSING_INSTRUCTION: 361 return node.getNodeValue(); 362 363 default: 364 return ""; 365 } 366 } 367 368 374 375 private static CharSequence getStringValue(Node node, int nodeKind) { 376 switch (nodeKind) { 377 case Type.DOCUMENT: 378 case Type.ELEMENT: 379 NodeList children1 = node.getChildNodes(); 380 StringBuffer sb1 = new StringBuffer (16); 381 expandStringValue(children1, sb1); 382 return sb1; 383 384 case Type.ATTRIBUTE: 385 return ((Attr)node).getValue(); 386 387 case Type.TEXT: 388 case Node.CDATA_SECTION_NODE: 389 return node.getNodeValue(); 390 391 case Type.COMMENT: 392 case Type.PROCESSING_INSTRUCTION: 393 return node.getNodeValue(); 394 395 default: 396 return ""; 397 } 398 } 399 400 private static void expandStringValue(NodeList list, StringBuffer sb) { 401 for (int i = 0; i < list.getLength(); i++) { 402 Node child = list.item(i); 403 switch (child.getNodeType()) { 404 case Node.ELEMENT_NODE: 405 expandStringValue(child.getChildNodes(), sb); 406 break; 407 default: 408 sb.append(child.getNodeValue()); 409 } 410 } 411 } 412 413 421 422 public int getNameCode() { 423 if (namecode != -1) { 424 return namecode; 426 } 427 int nodeKind = getNodeKind(); 428 if (nodeKind == Type.ELEMENT || nodeKind == Type.ATTRIBUTE) { 429 String prefix = node.getPrefix(); 430 if (prefix==null) { 431 prefix = ""; 432 } 433 namecode = docWrapper.getNamePool().allocate(prefix, getURI(), getLocalPart()); 434 return namecode; 435 } else if (nodeKind == Type.PROCESSING_INSTRUCTION ) { 436 namecode = docWrapper.getNamePool().allocate("", "", getLocalPart()); 437 return namecode; 438 } else { 439 return -1; 440 } 441 } 442 443 449 450 public int getFingerprint() { 451 int nc = getNameCode(); 452 if (nc == -1) { 453 return -1; 454 } 455 return nc&0xfffff; 456 } 457 458 463 464 public String getLocalPart() { 465 String s = node.getLocalName(); 466 if (s == null) { 467 String n = getDisplayName(); 469 int colon = n.indexOf(':'); 470 if (colon >= 0) { 471 return n.substring(colon+1); 472 } 473 return n; 474 } else { 475 return s; 476 } 477 } 478 479 486 487 public String getURI() { 488 NodeInfo element; 489 if (nodeKind == Type.ELEMENT) { 490 element = this; 491 } else if (nodeKind == Type.ATTRIBUTE) { 492 element = parent; 493 } else { 494 return ""; 495 } 496 497 505 508 String uri = node.getNamespaceURI(); 509 if (uri != null) { 510 return uri; 511 } 512 513 515 if (node.getNodeName().startsWith("xml:")) { 516 return NamespaceConstant.XML; 517 } 518 519 String [] parts; 520 try { 521 parts = Name.getQNameParts(node.getNodeName()); 522 } catch (QNameException e) { 523 throw new IllegalStateException ("Invalid QName in DOM node. " + e); 524 } 525 526 if (nodeKind == Type.ATTRIBUTE && parts[0].equals("")) { 527 uri = ""; 529 } else { 530 AxisIterator nsiter = element.iterateAxis(Axis.NAMESPACE); 531 while (true) { 532 NodeInfo ns = (NodeInfo)nsiter.next(); 533 if (ns == null) break; 534 if (ns.getLocalPart().equals(parts[0])) { 535 uri = ns.getStringValue(); 536 break; 537 } 538 } 539 if (uri == null) { 540 if (parts[0].equals("")) { 541 uri = ""; 542 } else { 543 throw new IllegalStateException ("Undeclared namespace prefix in DOM input: " + parts[0]); 544 } 545 } 546 } 547 return uri; 548 } 549 550 557 558 public String getPrefix() { 559 return node.getPrefix(); 560 } 561 562 568 569 public String getDisplayName() { 570 switch (nodeKind) { 571 case Type.ELEMENT: 572 case Type.ATTRIBUTE: 573 case Type.PROCESSING_INSTRUCTION: 574 return node.getNodeName(); 575 default: 576 return ""; 577 578 } 579 } 580 581 584 585 public NodeInfo getParent() { 586 if (parent==null) { 587 switch (getNodeKind()) { 588 case Type.ATTRIBUTE: 589 parent = makeWrapper(((Attr)node).getOwnerElement(), docWrapper); 590 break; 591 default: 592 Node p = node.getParentNode(); 593 if (p==null) { 594 return null; 595 } else { 596 parent = makeWrapper(p, docWrapper); 597 } 598 } 599 } 600 return parent; 601 } 602 603 611 612 public int getSiblingPosition() { 613 if (index == -1) { 614 switch (nodeKind) { 615 case Type.ELEMENT: 616 case Type.TEXT: 617 case Type.COMMENT: 618 case Type.PROCESSING_INSTRUCTION: 619 int ix = 0; 620 Node start = node; 621 while (true) { 622 start = start.getPreviousSibling(); 623 if (start == null) { 624 index = ix; 625 return ix; 626 } 627 ix++; 628 } 629 case Type.ATTRIBUTE: 630 ix = 0; 631 int fp = getFingerprint(); 632 AxisIterator iter = parent.iterateAxis(Axis.ATTRIBUTE); 633 while (true) { 634 NodeInfo n = (NodeInfo)iter.next(); 635 if (n==null || n.getFingerprint()==fp) { 636 index = ix; 637 return ix; 638 } 639 ix++; 640 } 641 642 case Type.NAMESPACE: 643 ix = 0; 644 fp = getFingerprint(); 645 iter = parent.iterateAxis(Axis.NAMESPACE); 646 while (true) { 647 NodeInfo n = (NodeInfo)iter.next(); 648 if (n==null || n.getFingerprint()==fp) { 649 index = ix; 650 return ix; 651 } 652 ix++; 653 } 654 default: 655 index = 0; 656 return index; 657 } 658 } 659 return index; 660 } 661 662 667 668 public AxisIterator iterateAxis(byte axisNumber) { 669 switch (axisNumber) { 670 case Axis.ANCESTOR: 671 if (nodeKind==Type.DOCUMENT) return EmptyIterator.getInstance(); 672 return new Navigator.AncestorEnumeration(this, false); 673 674 case Axis.ANCESTOR_OR_SELF: 675 if (nodeKind==Type.DOCUMENT) return EmptyIterator.getInstance(); 676 return new Navigator.AncestorEnumeration(this, true); 677 678 case Axis.ATTRIBUTE: 679 if (nodeKind!=Type.ELEMENT) return EmptyIterator.getInstance(); 680 return new AttributeEnumeration(this); 681 682 case Axis.CHILD: 683 if (hasChildNodes()) { 684 return new ChildEnumeration(this, true, true); 685 } else { 686 return EmptyIterator.getInstance(); 687 } 688 689 case Axis.DESCENDANT: 690 if (hasChildNodes()) { 691 return new Navigator.DescendantEnumeration(this, false, true); 692 } else { 693 return EmptyIterator.getInstance(); 694 } 695 696 case Axis.DESCENDANT_OR_SELF: 697 return new Navigator.DescendantEnumeration(this, true, true); 698 699 case Axis.FOLLOWING: 700 return new Navigator.FollowingEnumeration(this); 701 702 case Axis.FOLLOWING_SIBLING: 703 switch (nodeKind) { 704 case Type.DOCUMENT: 705 case Type.ATTRIBUTE: 706 case Type.NAMESPACE: 707 return EmptyIterator.getInstance(); 708 default: 709 return new ChildEnumeration(this, false, true); 710 } 711 712 case Axis.NAMESPACE: 713 if (nodeKind!=Type.ELEMENT) { 714 return EmptyIterator.getInstance(); 715 } 716 return new NamespaceIterator(this, null); 717 719 case Axis.PARENT: 720 getParent(); 721 return SingletonIterator.makeIterator(parent); 722 723 case Axis.PRECEDING: 724 return new Navigator.PrecedingEnumeration(this, false); 725 726 case Axis.PRECEDING_SIBLING: 727 switch (nodeKind) { 728 case Type.DOCUMENT: 729 case Type.ATTRIBUTE: 730 case Type.NAMESPACE: 731 return EmptyIterator.getInstance(); 732 default: 733 return new ChildEnumeration(this, false, false); 734 } 735 736 case Axis.SELF: 737 return SingletonIterator.makeIterator(this); 738 739 case Axis.PRECEDING_OR_ANCESTOR: 740 return new Navigator.PrecedingEnumeration(this, true); 741 742 default: 743 throw new IllegalArgumentException ("Unknown axis number " + axisNumber); 744 } 745 } 746 747 753 754 public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) { 755 return new Navigator.AxisFilter(iterateAxis(axisNumber), nodeTest); 756 } 757 758 763 764 public String getAttributeValue(int fingerprint) { 765 NameTest test = new NameTest(Type.ATTRIBUTE, fingerprint, getNamePool()); 766 AxisIterator iterator = iterateAxis(Axis.ATTRIBUTE, test); 767 NodeInfo attribute = (NodeInfo)iterator.next(); 768 if (attribute == null) { 769 return null; 770 } else { 771 return attribute.getStringValue(); 772 } 773 } 774 775 779 780 public NodeInfo getRoot() { 781 return docWrapper; 782 } 783 784 788 789 public DocumentInfo getDocumentRoot() { 790 return docWrapper; 791 } 792 793 798 799 public boolean hasChildNodes() { 800 if (node.getNodeType() == Node.ATTRIBUTE_NODE) { 802 return false; 803 } 804 return node.hasChildNodes(); 805 } 806 807 813 814 public String generateId() { 815 return Navigator.getSequentialKey(this); 816 } 817 818 822 823 public int getDocumentNumber() { 824 return getDocumentRoot().getDocumentNumber(); 825 } 826 827 830 831 public void copy(Receiver out, int whichNamespaces, boolean copyAnnotations, int locationId) throws XPathException { 832 Navigator.copy(this, out, docWrapper.getNamePool(), whichNamespaces, copyAnnotations, locationId); 833 } 834 835 841 842 public void sendNamespaceDeclarations(Receiver out, boolean includeAncestors) 843 throws XPathException { 844 Navigator.sendNamespaceDeclarations(this, out, includeAncestors); 845 } 846 847 862 863 public int[] getDeclaredNamespaces(int[] buffer) { 864 if (node.getNodeType() == Node.ELEMENT_NODE) { 865 Element elem = (Element)node; 866 NamedNodeMap atts = elem.getAttributes(); 867 868 if (atts == null) { 869 return EMPTY_NAMESPACE_LIST; 870 } 871 int count = 0; 872 for (int i=0; i<atts.getLength(); i++) { 873 Attr att = (Attr)atts.item(i); 874 String attName = att.getName(); 875 if (attName.equals("xmlns")) { 876 count++; 877 } else if (attName.startsWith("xmlns:")) { 878 count++; 879 } 880 } 881 if (count == 0) { 882 return EMPTY_NAMESPACE_LIST; 883 } else { 884 int[] result = (count > buffer.length ? new int[count] : buffer); 885 NamePool pool = getNamePool(); 886 int n = 0; 887 for (int i=0; i<atts.getLength(); i++) { 888 Attr att = (Attr)atts.item(i); 889 String attName = att.getName(); 890 if (attName.equals("xmlns")) { 891 String prefix = ""; 892 String uri = att.getValue(); 893 result[n++] = pool.allocateNamespaceCode(prefix, uri); 894 } else if (attName.startsWith("xmlns:")) { 895 String prefix = attName.substring(6); 896 String uri = att.getValue(); 897 result[n++] = pool.allocateNamespaceCode(prefix, uri); 898 } 899 } 900 if (count < result.length) { 901 result[count] = -1; 902 } 903 return result; 904 } 905 } else { 906 return null; 907 } 908 } 909 910 911 private final class AttributeEnumeration implements AxisIterator, LookaheadIterator { 912 913 private ArrayList attList = new ArrayList (10); 914 private int ix = 0; 915 private NodeWrapper start; 916 private NodeWrapper current; 917 918 public AttributeEnumeration(NodeWrapper start) { 919 this.start = start; 920 NamedNodeMap atts = start.node.getAttributes(); 921 if (atts != null) { 922 for (int i=0; i<atts.getLength(); i++) { 923 String name = atts.item(i).getNodeName(); 924 if (!(name.startsWith("xmlns") && 925 (name.length() == 5 || name.charAt(5) == ':'))) { 926 attList.add(atts.item(i)); 927 } 928 } 929 } 930 ix = 0; 931 } 932 933 public boolean hasNext() { 934 return ix < attList.size(); 935 } 936 937 public Item next() { 938 if (ix >= attList.size()) { 939 return null; 940 } 941 current = start.makeWrapper( 942 (Attr)attList.get(ix), docWrapper, start, ix); 943 ix++; 944 return current; 945 } 946 947 public Item current() { 948 return current; 949 } 950 951 public int position() { 952 return ix+1; 953 } 954 955 public SequenceIterator getAnother() { 956 return new AttributeEnumeration(start); 957 } 958 959 968 969 public int getProperties() { 970 return LOOKAHEAD; 971 } 972 } 973 974 975 981 982 private final class ChildEnumeration extends AxisIteratorImpl implements LookaheadIterator{ 983 984 private NodeWrapper start; 985 private NodeWrapper commonParent; 986 private ArrayList items = new ArrayList (20); 987 private int ix = 0; 988 private boolean downwards; private boolean forwards; 991 public ChildEnumeration(NodeWrapper start, 992 boolean downwards, boolean forwards) { 993 this.start = start; 994 this.downwards = downwards; 995 this.forwards = forwards; 996 position = 0; 997 998 if (downwards) { 999 commonParent = start; 1000 } else { 1001 commonParent = (NodeWrapper)start.getParent(); 1002 } 1003 1004 NodeList childNodes = commonParent.node.getChildNodes(); 1005 if (downwards) { 1006 if (!forwards) { 1007 ix = childNodes.getLength() - 1; 1009 } 1010 } else { 1011 ix = start.getSiblingPosition() + (forwards ? span : -1); 1012 } 1013 1014 if (forwards) { 1015 boolean previousText = false; 1016 for (int i=ix; i<childNodes.getLength(); i++) { 1017 boolean thisText = false; 1018 Node node = childNodes.item(i); 1019 switch (node.getNodeType()) { 1020 case Node.DOCUMENT_TYPE_NODE: 1021 break; 1022 case Node.TEXT_NODE: 1023 case Node.CDATA_SECTION_NODE: 1024 thisText = true; 1025 if (previousText) { 1026 if (isAtomizing()) { 1027 UntypedAtomicValue old = (UntypedAtomicValue)(items.get(items.size()-1)); 1028 String newval = old.getStringValue() + getStringValue(node, node.getNodeType()); 1029 items.set(items.size()-1, new UntypedAtomicValue(newval)); 1030 } else { 1031 NodeWrapper old = ((NodeWrapper)items.get(items.size()-1)); 1032 old.span++; 1033 } 1034 break; 1035 } 1036 default: 1038 previousText = thisText; 1039 if (isAtomizing()) { 1040 items.add(new UntypedAtomicValue( 1041 getStringValue(node, node.getNodeType()))); 1042 } else { 1043 items.add(makeWrapper(node, docWrapper, commonParent, i)); 1044 } 1045 } 1046 } 1047 } else { 1048 boolean previousText = false; 1049 for (int i=ix; i>=0; i--) { 1050 boolean thisText = false; 1051 Node node = childNodes.item(i); 1052 switch (node.getNodeType()) { 1053 case Node.DOCUMENT_TYPE_NODE: 1054 break; 1055 case Node.TEXT_NODE: 1056 case Node.CDATA_SECTION_NODE: 1057 thisText = true; 1058 if (previousText) { 1059 if (isAtomizing()) { 1060 UntypedAtomicValue old = (UntypedAtomicValue)(items.get(items.size()-1)); 1061 String newval = old.getStringValue() + getStringValue(node, node.getNodeType()); 1062 items.set(items.size()-1, new UntypedAtomicValue(newval)); 1063 } else { 1064 NodeWrapper old = ((NodeWrapper)items.get(items.size()-1)); 1065 old.node = node; 1066 old.span++; 1067 } 1068 break; 1069 } 1070 default: 1072 previousText = thisText; 1073 if (isAtomizing()) { 1074 items.add(new UntypedAtomicValue( 1075 getStringValue(node, node.getNodeType()))); 1076 } else { 1077 items.add(makeWrapper(node, docWrapper, commonParent, i)); 1078 } 1079 } 1080 } 1081 } 1082 } 1083 1084 public boolean hasNext() { 1085 return position < items.size(); 1086 } 1087 1088 public Item next() { 1089 if (position < items.size()) { 1090 current = (Item)items.get(position++); 1091 return current; 1092 } else { 1093 return null; 1094 } 1095 } 1096 1097 public SequenceIterator getAnother() { 1098 return new ChildEnumeration(start, downwards, forwards); 1099 } 1100 1101 1110 1111 public int getProperties() { 1112 return LOOKAHEAD; 1113 } 1114 1115 } 1117 1118} 1119 1120 | Popular Tags |