1 /******************************************************************************* 2 3 Copyright: Copyright (C) 2007 Aaron Craelius and Kris Bell 4 All rights reserved. 5 6 License: BSD style: $(LICENSE) 7 8 version: Initial release: February 2008 9 10 Authors: Aaron, Kris 11 12 *******************************************************************************/ 13 14 module tango.text.xml.Document; 15 16 package import tango.text.xml.PullParser; 17 18 version(Clear) 19 extern (C) void* memset(void* s, int c, size_t n); 20 21 version=discrete; 22 23 /******************************************************************************* 24 25 Implements a DOM atop the XML parser, supporting document 26 parsing, tree traversal and ad-hoc tree manipulation. 27 28 The DOM API is non-conformant, yet simple and functional in 29 style - locate a tree node of interest and operate upon or 30 around it. In all cases you will need a document instance to 31 begin, whereupon it may be populated either by parsing an 32 existing document or via API manipulation. 33 34 This particular DOM employs a simple free-list to allocate 35 each of the tree nodes, making it quite efficient at parsing 36 XML documents. The tradeoff with such a scheme is that copying 37 nodes from one document to another requires a little more care 38 than otherwise. We felt this was a reasonable tradeoff, given 39 the throughput gains vs the relative infrequency of grafting 40 operations. For grafting within or across documents, please 41 use the move() and copy() methods. 42 43 Another simplification is related to entity transcoding. This 44 is not performed internally, and becomes the responsibility 45 of the client. That is, the client should perform appropriate 46 entity transcoding as necessary. Paying the (high) transcoding 47 cost for all documents doesn't seem appropriate. 48 49 Parse example 50 --- 51 auto doc = new Document!(char); 52 doc.parse (content); 53 54 auto print = new DocPrinter!(char); 55 Stdout(print(doc)).newline; 56 --- 57 58 API example 59 --- 60 auto doc = new Document!(char); 61 62 // attach an xml header 63 doc.header; 64 65 // attach an element with some attributes, plus 66 // a child element with an attached data value 67 doc.tree.element (null, "element") 68 .attribute (null, "attrib1", "value") 69 .attribute (null, "attrib2") 70 .element (null, "child", "value"); 71 72 auto print = new DocPrinter!(char); 73 Stdout(print(doc)).newline; 74 --- 75 76 Note that the document tree() includes all nodes in the tree, 77 and not just elements. Use doc.elements to address the topmost 78 element instead. For example, adding an interior sibling to 79 the prior illustration 80 --- 81 doc.elements.element (null, "sibling"); 82 --- 83 84 Printing the name of the topmost (root) element: 85 --- 86 Stdout.formatln ("first element is '{}'", doc.elements.name); 87 --- 88 89 XPath examples: 90 --- 91 auto doc = new Document!(char); 92 93 // attach an element with some attributes, plus 94 // a child element with an attached data value 95 doc.tree.element (null, "element") 96 .attribute (null, "attrib1", "value") 97 .attribute (null, "attrib2") 98 .element (null, "child", "value"); 99 100 // select named-elements 101 auto set = doc.query["element"]["child"]; 102 103 // select all attributes named "attrib1" 104 set = doc.query.descendant.attribute("attrib1"); 105 106 // select elements with one parent and a matching text value 107 set = doc.query[].filter((doc.Node n) {return n.children.hasData("value");}); 108 --- 109 110 Note that path queries are temporal - they do not retain content 111 across mulitple queries. That is, the lifetime of a query result 112 is limited unless you explicitly copy it. For example, this will 113 fail 114 --- 115 auto elements = doc.query["element"]; 116 auto children = elements["child"]; 117 --- 118 119 The above will lose elements because the associated document reuses 120 node space for subsequent queries. In order to retain results, do this 121 --- 122 auto elements = doc.query["element"].dup; 123 auto children = elements["child"]; 124 --- 125 126 The above .dup is generally very small (a set of pointers only). On 127 the other hand, recursive queries are fully supported 128 --- 129 set = doc.query[].filter((doc.Node n) {return n.query[].count > 1;}); 130 --- 131 132 Typical usage tends to follow the following pattern, Where each query 133 result is processed before another is initiated 134 --- 135 foreach (node; doc.query.child("element")) 136 { 137 // do something with each node 138 } 139 --- 140 141 Note that the parser is templated for char, wchar or dchar. 142 143 *******************************************************************************/ 144 145 class Document(T) : PullParser!(T) 146 { 147 public alias NodeImpl* Node; 148 149 private Node root; 150 private NodeImpl[] list; 151 private NodeImpl[][] lists; 152 private ptrdiff_t index, 153 chunks, 154 freelists; 155 private XmlPath!(T) xpath; 156 157 /*********************************************************************** 158 159 Construct a DOM instance. The optional parameter indicates 160 the initial number of nodes assigned to the freelist 161 162 ***********************************************************************/ 163 164 this (size_t nodes = 1000) 165 { 166 assert (nodes > 50); 167 super (null); 168 xpath = new XmlPath!(T); 169 170 chunks = nodes; 171 newlist(); 172 root = allocate(); 173 root.id = XmlNodeType.Document; 174 } 175 176 /*********************************************************************** 177 178 Return an xpath handle to query this document. This starts 179 at the document root. 180 181 See also Node.query 182 183 ***********************************************************************/ 184 185 final XmlPath!(T).NodeSet query () 186 { 187 return xpath.start (root); 188 } 189 190 /*********************************************************************** 191 192 Return the root document node, from which all other nodes 193 are descended. 194 195 Returns null where there are no nodes in the document 196 197 ***********************************************************************/ 198 199 @property final Node tree () 200 { 201 return root; 202 } 203 204 /*********************************************************************** 205 206 Return the topmost element node, which is generally the 207 root of the element tree. 208 209 Returns null where there are no top-level element nodes 210 211 ***********************************************************************/ 212 213 @property final Node elements () 214 { 215 if (root) 216 { 217 auto node = root.lastChild; 218 while (node) 219 if (node.id is XmlNodeType.Element) 220 return node; 221 else 222 node = node.prev; 223 } 224 return null; 225 } 226 227 /*********************************************************************** 228 229 Reset the freelist. Subsequent allocation of document nodes 230 will overwrite prior instances. 231 232 ***********************************************************************/ 233 234 final Document reset () 235 { 236 root.lastChild = root.firstChild = null; 237 version(Clear) 238 { 239 while (freelists) 240 { 241 auto list = lists[--freelists]; 242 memset (list.ptr, 0, NodeImpl.sizeof * list.length); 243 } 244 } 245 else 246 { 247 freelists = 0; 248 } 249 newlist(); 250 index = 1; 251 version(d) 252 { 253 freelists = 0; // needed to align the codegen! 254 } 255 return this; 256 } 257 258 /*********************************************************************** 259 260 Prepend an XML header to the document tree 261 262 ***********************************************************************/ 263 264 final Document header (const(T)[] encoding = null) 265 { 266 if (encoding.length) 267 encoding = `xml version="1.0" encoding="`~encoding~`"`; 268 else 269 encoding = `xml version="1.0" encoding="UTF-8"`; 270 271 root.prepend (root.create(XmlNodeType.PI, encoding)); 272 return this; 273 } 274 275 /*********************************************************************** 276 277 Parse the given xml content, which will reuse any existing 278 node within this document. The resultant tree is retrieved 279 via the document 'tree' attribute 280 281 ***********************************************************************/ 282 283 final void parse(const(T[]) xml) 284 { 285 assert (xml); 286 reset(); 287 super.reset (xml); 288 auto cur = root; 289 size_t defNamespace; 290 291 while (true) 292 { 293 auto p = text.point; 294 switch (super.next) 295 { 296 case XmlTokenType.EndElement: 297 case XmlTokenType.EndEmptyElement: 298 assert (cur.host); 299 cur.end = text.point; 300 cur = cur.host; 301 break; 302 303 case XmlTokenType.Data: 304 version (discrete) 305 { 306 auto node = allocate(); 307 node.rawValue = super.rawValue; 308 node.id = XmlNodeType.Data; 309 cur.append (node); 310 } 311 else 312 { 313 if (cur.rawValue.length is 0) 314 cur.rawValue = super.rawValue; 315 else 316 // multiple data sections 317 cur.data (super.rawValue); 318 } 319 break; 320 321 case XmlTokenType.StartElement: 322 auto node = allocate(); 323 node.host = cur; 324 node.prefixed = super.prefix; 325 node.id = XmlNodeType.Element; 326 node.localName = super.localName; 327 node.start = p; 328 329 // inline append 330 if (cur.lastChild) 331 { 332 cur.lastChild.nextSibling = node; 333 node.prevSibling = cur.lastChild; 334 cur.lastChild = node; 335 } 336 else 337 { 338 cur.firstChild = node; 339 cur.lastChild = node; 340 } 341 cur = node; 342 break; 343 344 case XmlTokenType.Attribute: 345 auto attr = allocate(); 346 attr.prefixed = super.prefix; 347 attr.rawValue = super.rawValue; 348 attr.localName = super.localName; 349 attr.id = XmlNodeType.Attribute; 350 cur.attrib (attr); 351 break; 352 353 case XmlTokenType.PI: 354 cur.pi_ (super.rawValue, p[0..text.point-p]); 355 break; 356 357 case XmlTokenType.Comment: 358 cur.comment_ (super.rawValue); 359 break; 360 361 case XmlTokenType.CData: 362 cur.cdata_ (super.rawValue); 363 break; 364 365 case XmlTokenType.Doctype: 366 cur.doctype_ (super.rawValue); 367 break; 368 369 case XmlTokenType.Done: 370 return; 371 372 default: 373 break; 374 } 375 } 376 } 377 378 /*********************************************************************** 379 380 allocate a node from the freelist 381 382 ***********************************************************************/ 383 384 private final Node allocate () 385 { 386 if (index >= list.length) 387 newlist(); 388 389 auto p = &list[index++]; 390 p.doc = this; 391 version(Clear){} 392 else 393 { 394 p.start = p.end = null; 395 p.host = 396 p.prevSibling = 397 p.nextSibling = 398 p.firstChild = 399 p.lastChild = 400 p.firstAttr = 401 p.lastAttr = null; 402 p.rawValue = 403 p.localName = 404 p.prefixed = null; 405 } 406 return p; 407 } 408 409 /*********************************************************************** 410 411 allocate a node from the freelist 412 413 ***********************************************************************/ 414 415 private final void newlist () 416 { 417 index = 0; 418 if (freelists >= lists.length) 419 { 420 lists.length = lists.length + 1; 421 lists[$-1] = new NodeImpl [chunks]; 422 } 423 list = lists[freelists++]; 424 } 425 426 /*********************************************************************** 427 428 foreach support for visiting and selecting nodes. 429 430 A fruct is a low-overhead mechanism for capturing context 431 relating to an opApply, and we use it here to sweep nodes 432 when testing for various relationships. 433 434 See Node.attributes and Node.children 435 436 ***********************************************************************/ 437 438 private struct Visitor 439 { 440 private Node node; 441 442 public alias value data; 443 public alias hasValue hasData; 444 445 /*************************************************************** 446 447 Is there anything to visit here? 448 449 Time complexity: O(1) 450 451 ***************************************************************/ 452 453 bool exist () 454 { 455 return node != null; 456 } 457 458 /*************************************************************** 459 460 traverse sibling nodes 461 462 ***************************************************************/ 463 464 int opApply (scope int delegate(ref Node) dg) 465 { 466 int ret; 467 468 for (auto n=node; n; n = n.nextSibling) 469 if ((ret = dg(n)) != 0) 470 break; 471 return ret; 472 } 473 474 /*************************************************************** 475 476 Locate a node with a matching name and/or prefix, 477 and which passes an optional filter. Each of the 478 arguments will be ignored where they are null. 479 480 Time complexity: O(n) 481 482 ***************************************************************/ 483 484 Node name (const(T[]) prefix, const(T[]) local, scope bool delegate(Node) dg=null) 485 { 486 for (auto n=node; n; n = n.nextSibling) 487 { 488 if (local.ptr && local != n.localName) 489 continue; 490 491 if (prefix.ptr && prefix != n.prefixed) 492 continue; 493 494 if (dg !is null && dg(n) is false) 495 continue; 496 497 return n; 498 } 499 return null; 500 } 501 502 /*************************************************************** 503 504 Scan nodes for a matching name and/or prefix. Each 505 of the arguments will be ignored where they are null. 506 507 Time complexity: O(n) 508 509 ***************************************************************/ 510 511 bool hasName (const(T[]) prefix, const(T[]) local) 512 { 513 return name (prefix, local) != null; 514 } 515 516 /*************************************************************** 517 518 Locate a node with a matching name and/or prefix, 519 and which matches a specified value. Each of the 520 arguments will be ignored where they are null. 521 522 Time complexity: O(n) 523 524 ***************************************************************/ 525 version (Filter) 526 { 527 Node value (const(T[]) prefix, const(T[]) local, const(T[]) value) 528 { 529 if (value.ptr) 530 return name (prefix, local, (Node n){return value == n.rawValue;}); 531 return name (prefix, local); 532 } 533 } 534 /*************************************************************** 535 536 Sweep nodes looking for a match, and returns either 537 a node or null. See value(x,y,z) or name(x,y,z) for 538 additional filtering. 539 540 Time complexity: O(n) 541 542 ***************************************************************/ 543 544 Node value (const(T[]) match) 545 { 546 if (match.ptr) 547 for (auto n=node; n; n = n.nextSibling) 548 if (match == n.rawValue) 549 return n; 550 return null; 551 } 552 553 /*************************************************************** 554 555 Sweep the nodes looking for a value match. Returns 556 true if found. See value(x,y,z) or name(x,y,z) for 557 additional filtering. 558 559 Time complexity: O(n) 560 561 ***************************************************************/ 562 563 bool hasValue (const(T[]) match) 564 { 565 return value(match) != null; 566 } 567 } 568 569 570 /*********************************************************************** 571 572 The node implementation 573 574 ***********************************************************************/ 575 576 private struct NodeImpl 577 { 578 public void* user; /// open for usage 579 package Document doc; // owning document 580 package XmlNodeType id; // node type 581 package const(T)[] prefixed; // namespace 582 package const(T)[] localName; // name 583 package const(T)[] rawValue; // data value 584 585 package Node host, // parent node 586 prevSibling, // prior 587 nextSibling, // next 588 firstChild, // head 589 lastChild, // tail 590 firstAttr, // head 591 lastAttr; // tail 592 593 package const(T)* end, // slice of the ... 594 start; // original xml text 595 596 /*************************************************************** 597 598 Return the hosting document 599 600 ***************************************************************/ 601 602 @property Document document () 603 { 604 return doc; 605 } 606 607 /*************************************************************** 608 609 Return the node type-id 610 611 ***************************************************************/ 612 613 @property XmlNodeType type () 614 { 615 return id; 616 } 617 618 /*************************************************************** 619 620 Return the parent, which may be null 621 622 ***************************************************************/ 623 624 @property Node parent () 625 { 626 return host; 627 } 628 629 /*************************************************************** 630 631 Return the first child, which may be null 632 633 ***************************************************************/ 634 635 @property Node child () 636 { 637 return firstChild; 638 } 639 640 /*************************************************************** 641 642 Return the last child, which may be null 643 644 Deprecated: exposes too much implementation detail. 645 Please file a ticket if you really need 646 this functionality 647 648 ***************************************************************/ 649 650 deprecated Node childTail () 651 { 652 return lastChild; 653 } 654 655 /*************************************************************** 656 657 Return the prior sibling, which may be null 658 659 ***************************************************************/ 660 661 @property Node prev () 662 { 663 return prevSibling; 664 } 665 666 /*************************************************************** 667 668 Return the next sibling, which may be null 669 670 ***************************************************************/ 671 672 @property Node next () 673 { 674 return nextSibling; 675 } 676 677 /*************************************************************** 678 679 Return the namespace prefix of this node (may be null) 680 681 ***************************************************************/ 682 683 @property const(T[]) prefix () 684 { 685 return prefixed; 686 } 687 688 /*************************************************************** 689 690 Set the namespace prefix of this node (may be null) 691 692 ***************************************************************/ 693 694 @property Node prefix (const(T[]) replace) 695 { 696 prefixed = replace; 697 return &this; 698 } 699 700 /*************************************************************** 701 702 Return the vanilla node name (sans prefix) 703 704 ***************************************************************/ 705 706 @property const(T[]) name () 707 { 708 return localName; 709 } 710 711 /*************************************************************** 712 713 Set the vanilla node name (sans prefix) 714 715 ***************************************************************/ 716 717 @property Node name (const(T[]) replace) 718 { 719 localName = replace; 720 return &this; 721 } 722 723 /*************************************************************** 724 725 Return the data content, which may be null 726 727 ***************************************************************/ 728 729 @property const(T[]) value () 730 { 731 version(discrete) 732 { 733 if (type is XmlNodeType.Element) 734 foreach (child; children) 735 if (child.id is XmlNodeType.Data || 736 child.id is XmlNodeType.CData) 737 return child.rawValue; 738 } 739 return rawValue; 740 } 741 742 /*************************************************************** 743 744 Set the raw data content, which may be null 745 746 ***************************************************************/ 747 748 @property void value (const(T[]) val) 749 { 750 version(discrete) 751 { 752 if (type is XmlNodeType.Element) 753 foreach (child; children) 754 if (child.id is XmlNodeType.Data) 755 return child.value (val); 756 } 757 rawValue = val; 758 mutate(); 759 } 760 761 /*************************************************************** 762 763 Return the full node name, which is a combination 764 of the prefix & local names. Nodes without a prefix 765 will return local-name only 766 767 ***************************************************************/ 768 769 const(T[]) toString (T[] output = null) 770 { 771 if (prefixed.length) 772 { 773 auto len = prefixed.length + localName.length + 1; 774 775 // is the prefix already attached to the name? 776 if (prefixed.ptr + prefixed.length + 1 is localName.ptr && 777 ':' is *(localName.ptr-1)) 778 return prefixed.ptr [0 .. len]; 779 780 // nope, copy the discrete segments into output 781 if (output.length < len) 782 output.length = len; 783 output[0..prefixed.length] = prefixed[]; 784 output[prefixed.length] = ':'; 785 output[prefixed.length+1 .. len] = localName[]; 786 return output[0..len]; 787 } 788 789 return localName; 790 } 791 792 /*************************************************************** 793 794 Return the index of this node, or how many 795 prior siblings it has. 796 797 Time complexity: O(n) 798 799 ***************************************************************/ 800 801 @property size_t position () 802 { 803 auto count = 0; 804 auto prior = prevSibling; 805 while (prior) 806 ++count, prior = prior.prevSibling; 807 return count; 808 } 809 810 /*************************************************************** 811 812 Detach this node from its parent and siblings 813 814 ***************************************************************/ 815 816 Node detach () 817 { 818 return remove(); 819 } 820 821 /*************************************************************** 822 823 Return an xpath handle to query this node 824 825 See also Document.query 826 827 ***************************************************************/ 828 829 final XmlPath!(T).NodeSet query () 830 { 831 return doc.xpath.start (&this); 832 } 833 834 /*************************************************************** 835 836 Return a foreach iterator for node children 837 838 ***************************************************************/ 839 840 @property Visitor children () 841 { 842 Visitor v = {firstChild}; 843 return v; 844 } 845 846 /*************************************************************** 847 848 Return a foreach iterator for node attributes 849 850 ***************************************************************/ 851 852 @property Visitor attributes () 853 { 854 Visitor v = {firstAttr}; 855 return v; 856 } 857 858 /*************************************************************** 859 860 Returns whether there are attributes present or not 861 862 Deprecated: use node.attributes.exist instead 863 864 ***************************************************************/ 865 866 deprecated bool hasAttributes () 867 { 868 return firstAttr !is null; 869 } 870 871 /*************************************************************** 872 873 Returns whether there are children present or nor 874 875 Deprecated: use node.child or node.children.exist 876 instead 877 878 ***************************************************************/ 879 880 deprecated bool hasChildren () 881 { 882 return firstChild !is null; 883 } 884 885 /*************************************************************** 886 887 Duplicate the given sub-tree into place as a child 888 of this node. 889 890 Returns a reference to the subtree 891 892 ***************************************************************/ 893 894 Node copy (Node tree) 895 { 896 assert (tree); 897 tree = tree.clone(); 898 tree.migrate (document); 899 900 if (tree.id is XmlNodeType.Attribute) 901 attrib (tree); 902 else 903 append (tree); 904 return tree; 905 } 906 907 /*************************************************************** 908 909 Relocate the given sub-tree into place as a child 910 of this node. 911 912 Returns a reference to the subtree 913 914 ***************************************************************/ 915 916 Node move (Node tree) 917 { 918 tree.detach(); 919 if (tree.doc is doc) 920 { 921 if (tree.id is XmlNodeType.Attribute) 922 attrib (tree); 923 else 924 append (tree); 925 } 926 else 927 tree = copy (tree); 928 return tree; 929 } 930 931 /*************************************************************** 932 933 Appends a new (child) Element and returns a reference 934 to it. 935 936 ***************************************************************/ 937 938 Node element (const(T[]) prefix, const(T[]) local, const(T[]) value = null) 939 { 940 return element_ (prefix, local, value).mutate(); 941 } 942 943 /*************************************************************** 944 945 Attaches an Attribute and returns this, the host 946 947 ***************************************************************/ 948 949 Node attribute (const(T[]) prefix, const(T[]) local, const(T[]) value = null) 950 { 951 return attribute_ (prefix, local, value).mutate(); 952 } 953 954 /*************************************************************** 955 956 Attaches a Data node and returns this, the host 957 958 ***************************************************************/ 959 960 Node data (const(T[]) data) 961 { 962 return data_ (data).mutate(); 963 } 964 965 /*************************************************************** 966 967 Attaches a CData node and returns this, the host 968 969 ***************************************************************/ 970 971 Node cdata (const(T[]) cdata) 972 { 973 return cdata_ (cdata).mutate(); 974 } 975 976 /*************************************************************** 977 978 Attaches a Comment node and returns this, the host 979 980 ***************************************************************/ 981 982 Node comment (const(T[]) comment) 983 { 984 return comment_ (comment).mutate(); 985 } 986 987 /*************************************************************** 988 989 Attaches a Doctype node and returns this, the host 990 991 ***************************************************************/ 992 993 Node doctype (const(T[]) doctype) 994 { 995 return doctype_ (doctype).mutate(); 996 } 997 998 /*************************************************************** 999 1000 Attaches a PI node and returns this, the host 1001 1002 ***************************************************************/ 1003 1004 Node pi (const(T[]) pi) 1005 { 1006 return pi_ (pi, null).mutate(); 1007 } 1008 1009 /*************************************************************** 1010 1011 Attaches a child Element, and returns a reference 1012 to the child 1013 1014 ***************************************************************/ 1015 1016 private Node element_ (const(T[]) prefix, const(T[]) local, const(T[]) value = null) 1017 { 1018 auto node = create (XmlNodeType.Element, null); 1019 append (node.set (prefix, local)); 1020 version(discrete) 1021 { 1022 if (value.length) 1023 node.data_ (value); 1024 } 1025 else 1026 { 1027 node.rawValue = value; 1028 } 1029 return node; 1030 } 1031 1032 /*************************************************************** 1033 1034 Attaches an Attribute, and returns the host 1035 1036 ***************************************************************/ 1037 1038 private Node attribute_ (const(T[]) prefix, const(T[]) local, const(T[]) value = null) 1039 { 1040 auto node = create (XmlNodeType.Attribute, value); 1041 attrib (node.set (prefix, local)); 1042 return &this; 1043 } 1044 1045 /*************************************************************** 1046 1047 Attaches a Data node, and returns the host 1048 1049 ***************************************************************/ 1050 1051 private Node data_ (const(T[]) data) 1052 { 1053 append (create (XmlNodeType.Data, data)); 1054 return &this; 1055 } 1056 1057 /*************************************************************** 1058 1059 Attaches a CData node, and returns the host 1060 1061 ***************************************************************/ 1062 1063 private Node cdata_ (const(T[]) cdata) 1064 { 1065 append (create (XmlNodeType.CData, cdata)); 1066 return &this; 1067 } 1068 1069 /*************************************************************** 1070 1071 Attaches a Comment node, and returns the host 1072 1073 ***************************************************************/ 1074 1075 private Node comment_ (const(T[]) comment) 1076 { 1077 append (create (XmlNodeType.Comment, comment)); 1078 return &this; 1079 } 1080 1081 /*************************************************************** 1082 1083 Attaches a PI node, and returns the host 1084 1085 ***************************************************************/ 1086 1087 private Node pi_ (const(T[]) pi, const(T[]) patch) 1088 { 1089 append (create(XmlNodeType.PI, pi).patch(patch)); 1090 return &this; 1091 } 1092 1093 /*************************************************************** 1094 1095 Attaches a Doctype node, and returns the host 1096 1097 ***************************************************************/ 1098 1099 private Node doctype_ (const(T[]) doctype) 1100 { 1101 append (create (XmlNodeType.Doctype, doctype)); 1102 return &this; 1103 } 1104 1105 /*************************************************************** 1106 1107 Append an attribute to this node, The given attribute 1108 cannot have an existing parent. 1109 1110 ***************************************************************/ 1111 1112 private void attrib (Node node) 1113 { 1114 assert (node.parent is null); 1115 node.host = &this; 1116 if (lastAttr) 1117 { 1118 lastAttr.nextSibling = node; 1119 node.prevSibling = lastAttr; 1120 lastAttr = node; 1121 } 1122 else 1123 firstAttr = lastAttr = node; 1124 } 1125 1126 /*************************************************************** 1127 1128 Append a node to this one. The given node cannot 1129 have an existing parent. 1130 1131 ***************************************************************/ 1132 1133 private void append (Node node) 1134 { 1135 assert (node.parent is null); 1136 node.host = &this; 1137 if (lastChild) 1138 { 1139 lastChild.nextSibling = node; 1140 node.prevSibling = lastChild; 1141 lastChild = node; 1142 } 1143 else 1144 firstChild = lastChild = node; 1145 } 1146 1147 /*************************************************************** 1148 1149 Prepend a node to this one. The given node cannot 1150 have an existing parent. 1151 1152 ***************************************************************/ 1153 1154 private void prepend (Node node) 1155 { 1156 assert (node.parent is null); 1157 node.host = &this; 1158 if (firstChild) 1159 { 1160 firstChild.prevSibling = node; 1161 node.nextSibling = firstChild; 1162 firstChild = node; 1163 } 1164 else 1165 firstChild = lastChild = node; 1166 } 1167 1168 /*************************************************************** 1169 1170 Configure node values 1171 1172 ***************************************************************/ 1173 1174 private Node set (const(T[]) prefix, const(T[]) local) 1175 { 1176 this.localName = local; 1177 this.prefixed = prefix; 1178 return &this; 1179 } 1180 1181 /*************************************************************** 1182 1183 Creates and returns a child Element node 1184 1185 ***************************************************************/ 1186 1187 private Node create (XmlNodeType type, const(T[]) value) 1188 { 1189 auto node = document.allocate(); 1190 node.rawValue = value; 1191 node.id = type; 1192 return node; 1193 } 1194 1195 /*************************************************************** 1196 1197 Detach this node from its parent and siblings 1198 1199 ***************************************************************/ 1200 1201 private Node remove() 1202 { 1203 if (! host) 1204 return &this; 1205 1206 mutate(); 1207 if (prevSibling && nextSibling) 1208 { 1209 prevSibling.nextSibling = nextSibling; 1210 nextSibling.prevSibling = prevSibling; 1211 prevSibling = null; 1212 nextSibling = null; 1213 host = null; 1214 } 1215 else 1216 if (nextSibling) 1217 { 1218 debug assert(host.firstChild == &this); 1219 parent.firstChild = nextSibling; 1220 nextSibling.prevSibling = null; 1221 nextSibling = null; 1222 host = null; 1223 } 1224 else 1225 if (type != XmlNodeType.Attribute) 1226 { 1227 if (prevSibling) 1228 { 1229 debug assert(host.lastChild == &this); 1230 host.lastChild = prevSibling; 1231 prevSibling.nextSibling = null; 1232 prevSibling = null; 1233 host = null; 1234 } 1235 else 1236 { 1237 debug assert(host.firstChild == &this); 1238 debug assert(host.lastChild == &this); 1239 host.firstChild = null; 1240 host.lastChild = null; 1241 host = null; 1242 } 1243 } 1244 else 1245 { 1246 if (prevSibling) 1247 { 1248 debug assert(host.lastAttr == &this); 1249 host.lastAttr = prevSibling; 1250 prevSibling.nextSibling = null; 1251 prevSibling = null; 1252 host = null; 1253 } 1254 else 1255 { 1256 debug assert(host.firstAttr == &this); 1257 debug assert(host.lastAttr == &this); 1258 host.firstAttr = null; 1259 host.lastAttr = null; 1260 host = null; 1261 } 1262 } 1263 1264 return &this; 1265 } 1266 1267 /*************************************************************** 1268 1269 Patch the serialization text, causing DocPrinter 1270 to ignore the subtree of this node, and instead 1271 emit the provided text as raw XML output. 1272 1273 Warning: this function does *not* copy the provided 1274 text, and may be removed from future revisions 1275 1276 ***************************************************************/ 1277 1278 private Node patch (const(T[]) text) 1279 { 1280 end = text.ptr + text.length; 1281 start = text.ptr; 1282 return &this; 1283 } 1284 1285 /*************************************************************** 1286 1287 purge serialization cache for this node and its 1288 ancestors 1289 1290 ***************************************************************/ 1291 1292 private Node mutate () 1293 { 1294 auto node = &this; 1295 do { 1296 node.end = null; 1297 } while ((node = node.host) !is null); 1298 1299 return &this; 1300 } 1301 1302 /*************************************************************** 1303 1304 Duplicate a single node 1305 1306 ***************************************************************/ 1307 1308 @property private Node dup () 1309 { 1310 return create(type, rawValue.dup).set(prefixed.dup, localName.dup); 1311 } 1312 1313 /*************************************************************** 1314 1315 Duplicate a subtree 1316 1317 ***************************************************************/ 1318 1319 private Node clone () 1320 { 1321 auto p = dup; 1322 1323 foreach (attr; attributes) 1324 p.attrib (attr.dup); 1325 foreach (child; children) 1326 p.append (child.clone()); 1327 return p; 1328 } 1329 1330 /*************************************************************** 1331 1332 Reset the document host for this subtree 1333 1334 ***************************************************************/ 1335 1336 private void migrate (Document host) 1337 { 1338 this.doc = host; 1339 foreach (attr; attributes) 1340 attr.migrate (host); 1341 foreach (child; children) 1342 child.migrate (host); 1343 } 1344 } 1345 } 1346 1347 1348 /******************************************************************************* 1349 1350 XPath support 1351 1352 Provides support for common XPath axis and filtering functions, 1353 via a native-D interface instead of typical interpreted notation. 1354 1355 The general idea here is to generate a NodeSet consisting of those 1356 tree-nodes which satisfy a filtering function. The direction, or 1357 axis, of tree traversal is governed by one of several predefined 1358 operations. All methods facilitiate call-chaining, where each step 1359 returns a new NodeSet instance to be operated upon. 1360 1361 The set of nodes themselves are collected in a freelist, avoiding 1362 heap-activity and making good use of D array-slicing facilities. 1363 1364 XPath examples 1365 --- 1366 auto doc = new Document!(char); 1367 1368 // attach an element with some attributes, plus 1369 // a child element with an attached data value 1370 doc.tree.element (null, "element") 1371 .attribute (null, "attrib1", "value") 1372 .attribute (null, "attrib2") 1373 .element (null, "child", "value"); 1374 1375 // select named-elements 1376 auto set = doc.query["element"]["child"]; 1377 1378 // select all attributes named "attrib1" 1379 set = doc.query.descendant.attribute("attrib1"); 1380 1381 // select elements with one parent and a matching text value 1382 set = doc.query[].filter((doc.Node n) {return n.children.hasData("value");}); 1383 --- 1384 1385 Note that path queries are temporal - they do not retain content 1386 across mulitple queries. That is, the lifetime of a query result 1387 is limited unless you explicitly copy it. For example, this will 1388 fail to operate as one might expect 1389 --- 1390 auto elements = doc.query["element"]; 1391 auto children = elements["child"]; 1392 --- 1393 1394 The above will lose elements, because the associated document reuses 1395 node space for subsequent queries. In order to retain results, do this 1396 --- 1397 auto elements = doc.query["element"].dup; 1398 auto children = elements["child"]; 1399 --- 1400 1401 The above .dup is generally very small (a set of pointers only). On 1402 the other hand, recursive queries are fully supported 1403 --- 1404 set = doc.query[].filter((doc.Node n) {return n.query[].count > 1;}); 1405 --- 1406 1407 Typical usage tends to exhibit the following pattern, Where each query 1408 result is processed before another is initiated 1409 --- 1410 foreach (node; doc.query.child("element")) 1411 { 1412 // do something with each node 1413 } 1414 --- 1415 1416 Supported axis include: 1417 --- 1418 .child immediate children 1419 .parent immediate parent 1420 .next following siblings 1421 .prev prior siblings 1422 .ancestor all parents 1423 .descendant all descendants 1424 .data text children 1425 .cdata cdata children 1426 .attribute attribute children 1427 --- 1428 1429 Each of the above accept an optional string, which is used in an 1430 axis-specific way to filter nodes. For instance, a .child("food") 1431 will filter <food> child elements. These variants are shortcuts 1432 to using a filter to post-process a result. Each of the above also 1433 have variants which accept a delegate instead. 1434 1435 In general, you traverse an axis and operate upon the results. The 1436 operation applied may be another axis traversal, or a filtering 1437 step. All steps can be, and generally should be chained together. 1438 Filters are implemented via a delegate mechanism 1439 --- 1440 .filter (bool delegate(Node)) 1441 --- 1442 1443 Where the delegate returns true if the node passes the filter. An 1444 example might be selecting all nodes with a specific attribute 1445 --- 1446 auto set = doc.query.descendant.filter ( 1447 (doc.Node n){return n.attributes.hasName (null, "test");} 1448 ); 1449 --- 1450 1451 Obviously this is not as clean and tidy as true XPath notation, but 1452 that can be wrapped atop this API instead. The benefit here is one 1453 of raw throughput - important for some applications. 1454 1455 Note that every operation returns a discrete result. Methods first() 1456 and last() also return a set of one or zero elements. Some language 1457 specific extensions are provided for too 1458 --- 1459 * .child() can be substituted with [] notation instead 1460 1461 * [] notation can be used to index a specific element, like .nth() 1462 1463 * the .nodes attribute exposes an underlying Node[], which may be 1464 sliced or traversed in the usual D manner 1465 --- 1466 1467 Other (query result) utility methods include 1468 --- 1469 .dup 1470 .first 1471 .last 1472 .opIndex 1473 .nth 1474 .count 1475 .opApply 1476 --- 1477 1478 XmlPath itself needs to be a class in order to avoid forward-ref issues. 1479 1480 *******************************************************************************/ 1481 1482 private class XmlPath(T) 1483 { 1484 public alias Document!(T) Doc; /// the typed document 1485 public alias Doc.Node Node; /// generic document node 1486 1487 private Node[] freelist; 1488 private size_t freeIndex, 1489 markIndex; 1490 private size_t recursion; 1491 1492 /*********************************************************************** 1493 1494 Prime a query 1495 1496 Returns a NodeSet containing just the given node, which 1497 can then be used to cascade results into subsequent NodeSet 1498 instances. 1499 1500 ***********************************************************************/ 1501 1502 final NodeSet start (Node root) 1503 { 1504 // we have to support recursion which may occur within 1505 // a filter callback 1506 if (recursion is 0) 1507 { 1508 if (freelist.length is 0) 1509 freelist.length = 256; 1510 freeIndex = 0; 1511 } 1512 1513 NodeSet set = {this}; 1514 auto mark = freeIndex; 1515 allocate(root); 1516 return set.assign (mark); 1517 } 1518 1519 /*********************************************************************** 1520 1521 This is the meat of XPath support. All of the NodeSet 1522 operators exist here, in order to enable call-chaining. 1523 1524 Note that some of the axis do double-duty as a filter 1525 also. This is just a convenience factor, and doesn't 1526 change the underlying mechanisms. 1527 1528 ***********************************************************************/ 1529 1530 struct NodeSet 1531 { 1532 private XmlPath host; 1533 public Node[] nodes; /// array of selected nodes 1534 1535 /*************************************************************** 1536 1537 Return a duplicate NodeSet 1538 1539 ***************************************************************/ 1540 1541 @property NodeSet dup () 1542 { 1543 NodeSet copy = {host}; 1544 copy.nodes = nodes.dup; 1545 return copy; 1546 } 1547 1548 /*************************************************************** 1549 1550 Return the number of selected nodes in the set 1551 1552 ***************************************************************/ 1553 1554 @property size_t count () 1555 { 1556 return nodes.length; 1557 } 1558 1559 /*************************************************************** 1560 1561 Return a set containing just the first node of 1562 the current set 1563 1564 ***************************************************************/ 1565 1566 NodeSet first () 1567 { 1568 return nth (0); 1569 } 1570 1571 /*************************************************************** 1572 1573 Return a set containing just the last node of 1574 the current set 1575 1576 ***************************************************************/ 1577 1578 NodeSet last () 1579 { 1580 auto i = nodes.length; 1581 if (i > 0) 1582 --i; 1583 return nth (i); 1584 } 1585 1586 /*************************************************************** 1587 1588 Return a set containing just the nth node of 1589 the current set 1590 1591 ***************************************************************/ 1592 1593 NodeSet opIndex (size_t i) 1594 { 1595 return nth (i); 1596 } 1597 1598 /*************************************************************** 1599 1600 Return a set containing just the nth node of 1601 the current set 1602 1603 ***************************************************************/ 1604 1605 NodeSet nth (size_t index) 1606 { 1607 NodeSet set = {host}; 1608 auto mark = host.mark(); 1609 if (index < nodes.length) 1610 host.allocate (nodes [index]); 1611 return set.assign (mark); 1612 } 1613 1614 /*************************************************************** 1615 1616 Return a set containing all child elements of the 1617 nodes within this set 1618 1619 ***************************************************************/ 1620 1621 NodeSet opSlice () 1622 { 1623 return child(); 1624 } 1625 1626 /*************************************************************** 1627 1628 Return a set containing all child elements of the 1629 nodes within this set, which match the given name 1630 1631 ***************************************************************/ 1632 1633 NodeSet opIndex (const(T[]) name) 1634 { 1635 return child (name); 1636 } 1637 1638 /*************************************************************** 1639 1640 Return a set containing all parent elements of the 1641 nodes within this set, which match the optional name 1642 1643 ***************************************************************/ 1644 1645 NodeSet parent (const(T[]) name = null) 1646 { 1647 if (name.ptr) 1648 return parent ((Node node){return node.name == name;}); 1649 return parent (&always); 1650 } 1651 1652 /*************************************************************** 1653 1654 Return a set containing all data nodes of the 1655 nodes within this set, which match the optional 1656 value 1657 1658 ***************************************************************/ 1659 1660 NodeSet data (const(T[]) value = null) 1661 { 1662 if (value.ptr) 1663 return child ((Node node){return node.value == value;}, 1664 XmlNodeType.Data); 1665 return child (&always, XmlNodeType.Data); 1666 } 1667 1668 /*************************************************************** 1669 1670 Return a set containing all cdata nodes of the 1671 nodes within this set, which match the optional 1672 value 1673 1674 ***************************************************************/ 1675 1676 NodeSet cdata (const(T[]) value = null) 1677 { 1678 if (value.ptr) 1679 return child ((Node node){return node.value == value;}, 1680 XmlNodeType.CData); 1681 return child (&always, XmlNodeType.CData); 1682 } 1683 1684 /*************************************************************** 1685 1686 Return a set containing all attributes of the 1687 nodes within this set, which match the optional 1688 name 1689 1690 ***************************************************************/ 1691 1692 NodeSet attribute (const(T[]) name = null) 1693 { 1694 if (name.ptr) 1695 return attribute ((Node node){return node.name == name;}); 1696 return attribute (&always); 1697 } 1698 1699 /*************************************************************** 1700 1701 Return a set containing all descendant elements of 1702 the nodes within this set, which match the given name 1703 1704 ***************************************************************/ 1705 1706 NodeSet descendant (const(T[]) name = null) 1707 { 1708 if (name.ptr) 1709 return descendant ((Node node){return node.name == name;}); 1710 return descendant (&always); 1711 } 1712 1713 /*************************************************************** 1714 1715 Return a set containing all child elements of the 1716 nodes within this set, which match the optional name 1717 1718 ***************************************************************/ 1719 1720 NodeSet child (const(T[]) name = null) 1721 { 1722 if (name.ptr) 1723 return child ((Node node){return node.name == name;}); 1724 return child (&always); 1725 } 1726 1727 /*************************************************************** 1728 1729 Return a set containing all ancestor elements of 1730 the nodes within this set, which match the optional 1731 name 1732 1733 ***************************************************************/ 1734 1735 NodeSet ancestor (const(T[]) name = null) 1736 { 1737 if (name.ptr) 1738 return ancestor ((Node node){return node.name == name;}); 1739 return ancestor (&always); 1740 } 1741 1742 /*************************************************************** 1743 1744 Return a set containing all prior sibling elements of 1745 the nodes within this set, which match the optional 1746 name 1747 1748 ***************************************************************/ 1749 1750 NodeSet prev (const(T[]) name = null) 1751 { 1752 if (name.ptr) 1753 return prev ((Node node){return node.name == name;}); 1754 return prev (&always); 1755 } 1756 1757 /*************************************************************** 1758 1759 Return a set containing all subsequent sibling 1760 elements of the nodes within this set, which 1761 match the optional name 1762 1763 ***************************************************************/ 1764 1765 NodeSet next (const(T[]) name = null) 1766 { 1767 if (name.ptr) 1768 return next ((Node node){return node.name == name;}); 1769 return next (&always); 1770 } 1771 1772 /*************************************************************** 1773 1774 Return a set containing all nodes within this set 1775 which pass the filtering test 1776 1777 ***************************************************************/ 1778 1779 NodeSet filter (scope bool delegate(Node) filter) 1780 { 1781 NodeSet set = {host}; 1782 auto mark = host.mark(); 1783 1784 foreach (node; nodes) 1785 test (filter, node); 1786 return set.assign (mark); 1787 } 1788 1789 /*************************************************************** 1790 1791 Return a set containing all child nodes of 1792 the nodes within this set which pass the 1793 filtering test 1794 1795 ***************************************************************/ 1796 1797 NodeSet child (scope bool delegate(Node) filter, 1798 XmlNodeType type = XmlNodeType.Element) 1799 { 1800 NodeSet set = {host}; 1801 auto mark = host.mark(); 1802 1803 foreach (parent; nodes) 1804 foreach (child; parent.children) 1805 if (child.id is type) 1806 test (filter, child); 1807 return set.assign (mark); 1808 } 1809 1810 /*************************************************************** 1811 1812 Return a set containing all attribute nodes of 1813 the nodes within this set which pass the given 1814 filtering test 1815 1816 ***************************************************************/ 1817 1818 NodeSet attribute (scope bool delegate(Node) filter) 1819 { 1820 NodeSet set = {host}; 1821 auto mark = host.mark(); 1822 1823 foreach (node; nodes) 1824 foreach (attr; node.attributes) 1825 test (filter, attr); 1826 return set.assign (mark); 1827 } 1828 1829 /*************************************************************** 1830 1831 Return a set containing all descendant nodes of 1832 the nodes within this set, which pass the given 1833 filtering test 1834 1835 ***************************************************************/ 1836 1837 NodeSet descendant (scope bool delegate(Node) filter, 1838 XmlNodeType type = XmlNodeType.Element) 1839 { 1840 void traverse (Node parent) 1841 { 1842 foreach (child; parent.children) 1843 { 1844 if (child.id is type) 1845 test (filter, child); 1846 if (child.firstChild) 1847 traverse (child); 1848 } 1849 } 1850 1851 NodeSet set = {host}; 1852 auto mark = host.mark(); 1853 1854 foreach (node; nodes) 1855 traverse (node); 1856 return set.assign (mark); 1857 } 1858 1859 /*************************************************************** 1860 1861 Return a set containing all parent nodes of 1862 the nodes within this set which pass the given 1863 filtering test 1864 1865 ***************************************************************/ 1866 1867 NodeSet parent (scope bool delegate(Node) filter) 1868 { 1869 NodeSet set = {host}; 1870 auto mark = host.mark(); 1871 1872 foreach (node; nodes) 1873 { 1874 auto p = node.parent; 1875 if (p && p.id != XmlNodeType.Document && !set.has(p)) 1876 { 1877 test (filter, p); 1878 // continually update our set of nodes, so 1879 // that set.has() can see a prior entry. 1880 // Ideally we'd avoid invoking test() on 1881 // prior nodes, but I don't feel the added 1882 // complexity is warranted 1883 set.nodes = host.slice (mark); 1884 } 1885 } 1886 return set.assign (mark); 1887 } 1888 1889 /*************************************************************** 1890 1891 Return a set containing all ancestor nodes of 1892 the nodes within this set, which pass the given 1893 filtering test 1894 1895 ***************************************************************/ 1896 1897 NodeSet ancestor (scope bool delegate(Node) filter) 1898 { 1899 NodeSet set = {host}; 1900 auto mark = host.mark(); 1901 1902 void traverse (Node child) 1903 { 1904 auto p = child.host; 1905 if (p && p.id != XmlNodeType.Document && !set.has(p)) 1906 { 1907 test (filter, p); 1908 // continually update our set of nodes, so 1909 // that set.has() can see a prior entry. 1910 // Ideally we'd avoid invoking test() on 1911 // prior nodes, but I don't feel the added 1912 // complexity is warranted 1913 set.nodes = host.slice (mark); 1914 traverse (p); 1915 } 1916 } 1917 1918 foreach (node; nodes) 1919 traverse (node); 1920 return set.assign (mark); 1921 } 1922 1923 /*************************************************************** 1924 1925 Return a set containing all following siblings 1926 of the ones within this set, which pass the given 1927 filtering test 1928 1929 ***************************************************************/ 1930 1931 NodeSet next (scope bool delegate(Node) filter, 1932 XmlNodeType type = XmlNodeType.Element) 1933 { 1934 NodeSet set = {host}; 1935 auto mark = host.mark(); 1936 1937 foreach (node; nodes) 1938 { 1939 auto p = node.nextSibling; 1940 while (p) 1941 { 1942 if (p.id is type) 1943 test (filter, p); 1944 p = p.nextSibling; 1945 } 1946 } 1947 return set.assign (mark); 1948 } 1949 1950 /*************************************************************** 1951 1952 Return a set containing all prior sibling nodes 1953 of the ones within this set, which pass the given 1954 filtering test 1955 1956 ***************************************************************/ 1957 1958 NodeSet prev (scope bool delegate(Node) filter, 1959 XmlNodeType type = XmlNodeType.Element) 1960 { 1961 NodeSet set = {host}; 1962 auto mark = host.mark(); 1963 1964 foreach (node; nodes) 1965 { 1966 auto p = node.prevSibling; 1967 while (p) 1968 { 1969 if (p.id is type) 1970 test (filter, p); 1971 p = p.prevSibling; 1972 } 1973 } 1974 return set.assign (mark); 1975 } 1976 1977 /*************************************************************** 1978 1979 Traverse the nodes of this set 1980 1981 ***************************************************************/ 1982 1983 int opApply (scope int delegate(ref Node) dg) 1984 { 1985 int ret; 1986 1987 foreach (node; nodes) 1988 if ((ret = dg (node)) != 0) 1989 break; 1990 return ret; 1991 } 1992 1993 /*************************************************************** 1994 1995 Common predicate 1996 1997 ***************************************************************/ 1998 1999 private bool always (Node node) 2000 { 2001 return true; 2002 } 2003 2004 /*************************************************************** 2005 2006 Assign a slice of the freelist to this NodeSet 2007 2008 ***************************************************************/ 2009 2010 private NodeSet assign (size_t mark) 2011 { 2012 nodes = host.slice (mark); 2013 return this; 2014 } 2015 2016 /*************************************************************** 2017 2018 Execute a filter on the given node. We have to 2019 deal with potential query recusion, so we set 2020 all kinda crap to recover from that 2021 2022 ***************************************************************/ 2023 2024 private void test (scope bool delegate(Node) filter, Node node) 2025 { 2026 auto pop = host.push(); 2027 auto add = filter (node); 2028 host.pop (pop); 2029 if (add) 2030 host.allocate (node); 2031 } 2032 2033 /*************************************************************** 2034 2035 We typically need to filter ancestors in order 2036 to avoid duplicates, so this is used for those 2037 purposes 2038 2039 ***************************************************************/ 2040 2041 private bool has (Node p) 2042 { 2043 foreach (node; nodes) 2044 if (node is p) 2045 return true; 2046 return false; 2047 } 2048 } 2049 2050 /*********************************************************************** 2051 2052 Return the current freelist index 2053 2054 ***********************************************************************/ 2055 2056 private size_t mark () 2057 { 2058 return freeIndex; 2059 } 2060 2061 /*********************************************************************** 2062 2063 Recurse and save the current state 2064 2065 ***********************************************************************/ 2066 2067 private size_t push () 2068 { 2069 ++recursion; 2070 return freeIndex; 2071 } 2072 2073 /*********************************************************************** 2074 2075 Restore prior state 2076 2077 ***********************************************************************/ 2078 2079 private void pop (size_t prior) 2080 { 2081 freeIndex = prior; 2082 --recursion; 2083 } 2084 2085 /*********************************************************************** 2086 2087 Return a slice of the freelist 2088 2089 ***********************************************************************/ 2090 2091 private Node[] slice (size_t mark) 2092 { 2093 assert (mark <= freeIndex); 2094 return freelist [mark .. freeIndex]; 2095 } 2096 2097 /*********************************************************************** 2098 2099 Allocate an entry in the freelist, expanding as necessary 2100 2101 ***********************************************************************/ 2102 2103 private size_t allocate (Node node) 2104 { 2105 if (freeIndex >= freelist.length) 2106 freelist.length = freelist.length + freelist.length / 2; 2107 2108 freelist[freeIndex] = node; 2109 return ++freeIndex; 2110 } 2111 } 2112 2113 2114 version (Old) 2115 { 2116 /******************************************************************************* 2117 2118 Specification for an XML serializer 2119 2120 *******************************************************************************/ 2121 2122 interface IXmlPrinter(T) 2123 { 2124 public alias Document!(T) Doc; /// the typed document 2125 public alias Doc.Node Node; /// generic document node 2126 public alias print opCall; /// alias for print method 2127 2128 /*********************************************************************** 2129 2130 Generate a text representation of the document tree 2131 2132 ***********************************************************************/ 2133 2134 T[] print (Doc doc); 2135 2136 /*********************************************************************** 2137 2138 Generate a representation of the given node-subtree 2139 2140 ***********************************************************************/ 2141 2142 void print (Node root, scope void delegate(T[][]...) emit); 2143 } 2144 } 2145 2146 2147 2148 /******************************************************************************* 2149 2150 *******************************************************************************/ 2151 2152 debug (Document) 2153 { 2154 import tango.io.Stdout; 2155 import tango.text.xml.DocPrinter; 2156 2157 void main() 2158 { 2159 auto doc = new Document!(char); 2160 2161 // attach an xml header 2162 doc.header; 2163 2164 // attach an element with some attributes, plus 2165 // a child element with an attached data value 2166 doc.tree.element (null, "root") 2167 .attribute (null, "attrib1", "value") 2168 .attribute (null, "attrib2", "other") 2169 .element (null, "child") 2170 .cdata ("some text"); 2171 2172 // attach a sibling to the interior elements 2173 doc.elements.element (null, "sibling"); 2174 2175 bool foo (doc.Node node) 2176 { 2177 node = node.attributes.name(null, "attrib1"); 2178 return node && "value" == node.value; 2179 } 2180 2181 foreach (node; doc.query.descendant("root").filter(&foo).child) 2182 Stdout.formatln(">> {}", node.name); 2183 2184 foreach (node; doc.elements.attributes) 2185 Stdout.formatln("<< {}", node.name); 2186 2187 foreach (node; doc.elements.children) 2188 Stdout.formatln("<< {}", node.name); 2189 2190 foreach (node; doc.query.descendant.cdata) 2191 Stdout.formatln ("{}: {}", node.parent.name, node.value); 2192 2193 // emit the result 2194 auto printer = new DocPrinter!(char); 2195 printer.print (doc, stdout); 2196 doc.reset(); 2197 } 2198 }