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 }