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