1 /**
2  * Copyright: Copyright (c) 2010-2011 Jacob Carlborg.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 26, 2010
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module orange.serialization.archives.XmlArchive;
8 
9 import std.conv;
10 
11 import orange.serialization.archives._;
12 import orange.serialization.Serializer;
13 import orange.util._;
14 import orange.xml.XmlDocument;
15 
16 private enum ArchiveMode
17 {
18     archiving,
19     unarchiving
20 }
21 
22 /**
23  * This class is a concrete implementation of the Archive interface. This archive
24  * uses XML as the final format for the serialized data.
25  */
26 final class XmlArchive (U = char) : ArchiveBase!(U)
27 {
28     private alias Archive.Id Id;
29 
30     private struct Tags
31     {
32         static enum Data structTag = "struct";
33         static enum Data dataTag = "data";
34         static enum Data archiveTag = "archive";
35         static enum Data arrayTag = "array";
36         static enum Data objectTag = "object";
37         static enum Data baseTag = "base";
38         static enum Data stringTag = "string";
39         static enum Data referenceTag = "reference";
40         static enum Data pointerTag = "pointer";
41         static enum Data associativeArrayTag = "associativeArray";
42         static enum Data nullTag = "null";
43         static enum Data enumTag = "enum";
44         static enum Data sliceTag = "slice";
45         static enum Data elementTag = "element";
46         static enum Data keyTag = "key";
47         static enum Data valueTag = "value";
48     }
49 
50     private struct Attributes
51     {
52         static enum Data invalidAttribute = "\0";
53         static enum Data typeAttribute = "type";
54         static enum Data versionAttribute = "version";
55         static enum Data lengthAttribute = "length";
56         static enum Data keyAttribute = "key";
57         static enum Data runtimeTypeAttribute = "runtimeType";
58         static enum Data idAttribute = "id";
59         static enum Data keyTypeAttribute = "keyType";
60         static enum Data valueTypeAttribute = "valueType";
61         static enum Data offsetAttribute = "offset";
62         static enum Data baseTypeAttribute = "baseType";
63     }
64 
65     private struct Node
66     {
67         XmlDocument.Node parent;
68         XmlDocument.Node node;
69         Id id;
70         string key;
71     }
72 
73     private
74     {
75         Data archiveType = "org.dsource.orange.xml";
76         Data archiveVersion = "1.0.0";
77 
78         XmlDocument doc;
79         doc.Node lastElement;
80 
81         bool hasBegunArchiving;
82         bool hasBegunUnarchiving;
83 
84         Node[Id] archivedArrays;
85         Node[Id] archivedPointers;
86         void[][Data] unarchivedSlices;
87     }
88 
89     /**
90      * Creates a new instance of this class with the give error callback.
91      *
92      * Params:
93      *     errorCallback = The callback to be called when an error occurs
94      */
95     this (ErrorCallback errorCallback = null)
96     {
97         super(errorCallback);
98         doc = new XmlDocument;
99     }
100 
101     /// Starts the archiving process. Call this method before archiving any values.
102     public void beginArchiving ()
103     {
104         if (!hasBegunArchiving)
105         {
106             doc.header;
107             lastElement = doc.tree.element(Tags.archiveTag)
108                 .attribute(Attributes.typeAttribute, archiveType)
109                 .attribute(Attributes.versionAttribute, archiveVersion);
110             lastElement = lastElement.element(Tags.dataTag);
111 
112             hasBegunArchiving = true;
113         }
114     }
115 
116     /**
117      * Begins the unarchiving process. Call this method before unarchiving any values.
118      *
119      * Params:
120      *     untypedData = the data to unarchive
121      */
122     public void beginUnarchiving (UntypedData untypedData)
123     {
124         auto data = cast(Data) untypedData;
125 
126         if (!hasBegunUnarchiving)
127         {
128             doc.parse(data);
129             hasBegunUnarchiving = true;
130 
131             auto set = doc.query[Tags.archiveTag][Tags.dataTag];
132 
133             if (set.nodes.length == 1)
134                 lastElement = set.nodes[0];
135 
136             else
137             {
138                 auto dataTag = to!(string)(Tags.dataTag);
139 
140                 if (set.nodes.length == 0)
141                     error(errorMessage!(ArchiveMode.unarchiving) ~ `The "` ~ to!(string)(Tags.dataTag) ~ `" tag could not be found.`, [dataTag]);
142 
143                 else
144                     error(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, [dataTag]);
145             }
146         }
147     }
148 
149     /// Returns the data stored in the archive in an untyped form.
150     UntypedData untypedData ()
151     {
152         return doc.toString();
153     }
154 
155     /// Returns the data stored in the archive in an typed form.
156     Data data ()
157     {
158         return doc.toString;
159     }
160 
161     /**
162      * Resets the archive. This resets the archive in a state making it ready to start
163      * a new archiving process.
164      */
165     void reset ()
166     {
167         hasBegunArchiving = false;
168         hasBegunUnarchiving = false;
169         doc.reset;
170     }
171 
172     /**
173      * Archives an array.
174      *
175      * Examples:
176      * ---
177      * int[] arr = [1, 2, 3];
178      *
179      * auto archive = new XmlArchive!();
180      *
181      * auto a = Array(arr.ptr, arr.length, typeof(a[0]).sizeof);
182      *
183      * archive.archive(a, typeof(a[0]).string, "arr", 0, {
184      *     // archive the individual elements
185      * });
186      * ---
187      *
188      * Params:
189      *     array = the array to archive
190      *     type = the runtime type of an element of the array
191      *     key = the key associated with the array
192      *     id = the id associated with the array
193      *     dg = a callback that performs the archiving of the individual elements
194      */
195     void archiveArray (Array array, string type, string key, Id id, void delegate () dg)
196     {
197         restore(lastElement) in {
198             internalArchiveArray(array, type, key, id, Tags.arrayTag);
199             dg();
200         };
201     }
202 
203     private void internalArchiveArray(Array array, string type, string key, Id id, Data tag, Data content = null)
204     {
205         auto parent = lastElement;
206 
207         if (array.length == 0)
208             lastElement = lastElement.element(tag);
209 
210         else
211             lastElement = doc.createNode(tag, content);
212 
213         lastElement.attribute(Attributes.typeAttribute, toData(type))
214         .attribute(Attributes.lengthAttribute, toData(array.length))
215         .attribute(Attributes.keyAttribute, toData(key))
216         .attribute(Attributes.idAttribute, toData(id));
217 
218         addArchivedArray(id, parent, lastElement, key);
219     }
220 
221     /**
222      * Archives an associative array.
223      *
224      * Examples:
225      * ---
226      * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
227      *
228      * auto archive = new XmlArchive!();
229      *
230      * archive.archive(string.stringof, int.stringof, arr.length, "arr", 0, {
231      *     // archive the individual keys and values
232      * });
233      * ---
234      *
235      *
236      * Params:
237      *     keyType = the runtime type of the keys
238      *     valueType = the runtime type of the values
239      *     length = the length of the associative array
240      *     key = the key associated with the associative array
241      *     id = the id associated with the associative array
242      *     dg = a callback that performs the archiving of the individual keys and values
243      *
244      * See_Also: archiveAssociativeArrayValue
245      * See_Also: archiveAssociativeArrayKey
246      */
247     void archiveAssociativeArray (string keyType, string valueType, size_t length, string key, Id id, void delegate () dg)
248     {
249         restore(lastElement) in {
250             lastElement = lastElement.element(Tags.associativeArrayTag)
251             .attribute(Attributes.keyTypeAttribute, toData(keyType))
252             .attribute(Attributes.valueTypeAttribute, toData(valueType))
253             .attribute(Attributes.lengthAttribute, toData(length))
254             .attribute(Attributes.keyAttribute, key)
255             .attribute(Attributes.idAttribute, toData(id));
256 
257             dg();
258         };
259     }
260 
261     /**
262      * Archives an associative array key.
263      *
264      * There are separate methods for archiving associative array keys and values
265      * because both the key and the value can be of arbitrary type and needs to be
266      * archived on its own.
267      *
268      * Examples:
269      * ---
270      * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
271      *
272      * auto archive = new XmlArchive!();
273      *
274      * foreach(k, v ; arr)
275      * {
276      *     archive.archiveAssociativeArrayKey(to!(string)(i), {
277      *         // archive the key
278      *     });
279      * }
280      * ---
281      *
282      * The foreach statement in the above example would most likely be executed in the
283      * callback passed to the archiveAssociativeArray method.
284      *
285      * Params:
286      *     key = the key associated with the key
287      *     dg = a callback that performs the actual archiving of the key
288      *
289      * See_Also: archiveAssociativeArray
290      * See_Also: archiveAssociativeArrayValue
291      */
292     void archiveAssociativeArrayKey (string key, void delegate () dg)
293     {
294         internalArchiveAAKeyValue(key, Tags.keyTag, dg);
295     }
296 
297     /**
298      * Archives an associative array value.
299      *
300      * There are separate methods for archiving associative array keys and values
301      * because both the key and the value can be of arbitrary type and needs to be
302      * archived on its own.
303      *
304      * Examples:
305      * ---
306      * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
307      *
308      * auto archive = new XmlArchive!();
309      * size_t i;
310      *
311      * foreach(k, v ; arr)
312      * {
313      *     archive.archiveAssociativeArrayValue(to!(string)(i), {
314      *         // archive the value
315      *     });
316      *
317      *     i++;
318      * }
319      * ---
320      *
321      * The foreach statement in the above example would most likely be executed in the
322      * callback passed to the archiveAssociativeArray method.
323      *
324      * Params:
325      *     key = the key associated with the value
326      *     dg = a callback that performs the actual archiving of the value
327      *
328      * See_Also: archiveAssociativeArray
329      * See_Also: archiveAssociativeArrayKey
330      */
331     void archiveAssociativeArrayValue (string key, void delegate () dg)
332     {
333         internalArchiveAAKeyValue(key, Tags.valueTag, dg);
334     }
335 
336     private void internalArchiveAAKeyValue (string key, Data tag, void delegate () dg)
337     {
338         restore(lastElement) in {
339             lastElement = lastElement.element(tag)
340             .attribute(Attributes.keyAttribute, toData(key));
341 
342             dg();
343         };
344     }
345 
346     /**
347      * Archives the given value.
348      *
349      * Example:
350      * ---
351      * enum Foo : bool
352      * {
353      *     bar
354      * }
355      *
356      * auto foo = Foo.bar;
357      * auto archive = new XmlArchive!();
358      * archive.archive(foo, "bool", "foo", 0);
359      * ---
360      *
361      * Params:
362      *     value = the value to archive
363      *     type = the base type of the enum
364      *     key = the key associated with the value
365      *     id = the id associated with the value
366      */
367     void archiveEnum (bool value, string type, string key, Id id)
368     {
369         internalArchiveEnum(value, type, key, id);
370     }
371 
372     /// Ditto
373     void archiveEnum (byte value, string type, string key, Id id)
374     {
375         internalArchiveEnum(value, type, key, id);
376     }
377 
378     /// Ditto
379     void archiveEnum (char value, string type, string key, Id id)
380     {
381         internalArchiveEnum(value, type, key, id);
382     }
383 
384     /// Ditto
385     void archiveEnum (dchar value, string type, string key, Id id)
386     {
387         internalArchiveEnum(value, type, key, id);
388     }
389 
390     /// Ditto
391     void archiveEnum (int value, string type, string key, Id id)
392     {
393         internalArchiveEnum(value, type, key, id);
394     }
395 
396     /// Ditto
397     void archiveEnum (long value, string type, string key, Id id)
398     {
399         internalArchiveEnum(value, type, key, id);
400     }
401 
402     /// Ditto
403     void archiveEnum (short value, string type, string key, Id id)
404     {
405         internalArchiveEnum(value, type, key, id);
406     }
407 
408     /// Ditto
409     void archiveEnum (ubyte value, string type, string key, Id id)
410     {
411         internalArchiveEnum(value, type, key, id);
412     }
413 
414     /// Ditto
415     void archiveEnum (uint value, string type, string key, Id id)
416     {
417         internalArchiveEnum(value, type, key, id);
418     }
419 
420     /// Ditto
421     void archiveEnum (ulong value, string type, string key, Id id)
422     {
423         internalArchiveEnum(value, type, key, id);
424     }
425 
426     /// Ditto
427     void archiveEnum (ushort value, string type, string key, Id id)
428     {
429         internalArchiveEnum(value, type, key, id);
430     }
431 
432     /// Ditto
433     void archiveEnum (wchar value, string type, string key, Id id)
434     {
435         internalArchiveEnum(value, type, key, id);
436     }
437 
438     private void internalArchiveEnum (T) (T value, string type, string key, Id id)
439     {
440         lastElement.element(Tags.enumTag, toData(value))
441         .attribute(Attributes.typeAttribute, toData(type))
442         .attribute(Attributes.baseTypeAttribute, toData(T.stringof))
443         .attribute(Attributes.keyAttribute, toData(key))
444         .attribute(Attributes.idAttribute, toData(id));
445     }
446 
447     /**
448      * Archives a base class.
449      *
450      * This method is used to indicate that the all following calls to archive a value
451      * should be part of the base class. This method is usually called within the
452      * callback passed to archiveObject. The archiveObject method can the mark the end
453      * of the class.
454      *
455      * Examples:
456      * ---
457      * class ArchiveBase {}
458      * class Foo : ArchiveBase {}
459      *
460      * auto archive = new XmlArchive!();
461      * archive.archiveBaseClass("ArchiveBase", "base", 0);
462      * ---
463      *
464      * Params:
465      *     type = the type of the base class to archive
466      *     key = the key associated with the base class
467      *     id = the id associated with the base class
468      */
469     void archiveBaseClass (string type, string key, Id id)
470     {
471         lastElement = lastElement.element(Tags.baseTag)
472         .attribute(Attributes.typeAttribute, toData(type))
473         .attribute(Attributes.keyAttribute, toData(key))
474         .attribute(Attributes.idAttribute, toData(id));
475     }
476 
477     /**
478      * Archives a null pointer or reference.
479      *
480      * Examples:
481      * ---
482      * int* ptr;
483      *
484      * auto archive = new XmlArchive!();
485      * archive.archiveNull(typeof(ptr).stringof, "ptr");
486      * ---
487      *
488      * Params:
489      *     type = the runtime type of the pointer or reference to archive
490      *     key = the key associated with the null pointer
491      */
492     void archiveNull (string type, string key)
493     {
494         lastElement.element(Tags.nullTag)
495         .attribute(Attributes.typeAttribute, toData(type))
496         .attribute(Attributes.keyAttribute, toData(key));
497     }
498 
499     /**
500      * Archives an object, either a class or an interface.
501      *
502      * Examples:
503      * ---
504      * class Foo
505      * {
506      *     int a;
507      * }
508      *
509      * auto foo = new Foo;
510      *
511      * auto archive = new XmlArchive!();
512      * archive.archiveObject(Foo.classinfo.name, "Foo", "foo", 0, {
513      *     // archive the fields of Foo
514      * });
515      * ---
516      *
517      * Params:
518      *     runtimeType = the runtime type of the object
519      *     type = the static type of the object
520      *     key = the key associated with the object
521      *     id = the id associated with the object
522      *     dg = a callback that performs the archiving of the individual fields
523      */
524     void archiveObject (string runtimeType, string type, string key, Id id, void delegate () dg)
525     {
526         restore(lastElement) in {
527             lastElement = lastElement.element(Tags.objectTag)
528             .attribute(Attributes.runtimeTypeAttribute, toData(runtimeType))
529             .attribute(Attributes.typeAttribute, toData(type))
530             .attribute(Attributes.keyAttribute, toData(key))
531             .attribute(Attributes.idAttribute, toData(id));
532 
533             dg();
534         };
535     }
536 
537     /**
538      * Archives a pointer.
539      *
540      * If a pointer points to a value that is serialized as well, the pointer should be
541      * archived as a reference. Otherwise the value that the pointer points to should be
542      * serialized as a regular value.
543      *
544      * Examples:
545      * ---
546      * class Foo
547      * {
548      *     int a;
549      *     int* b;
550      * }
551      *
552      * auto foo = new Foo;
553      * foo.a = 3;
554      * foo.b = &foo.a;
555      *
556      * archive = new XmlArchive!();
557      * archive.archivePointer("b", 0, {
558      *     // archive "foo.b" as a reference
559      * });
560      * ---
561      *
562      * ---
563      * int a = 3;
564      *
565      * class Foo
566      * {
567      *     int* b;
568      * }
569      *
570      * auto foo = new Foo;
571      * foo.b = &a;
572      *
573      * archive = new XmlArchive!();
574      * archive.archivePointer("b", 0, {
575      *     // archive "foo.b" as a regular value
576      * });
577      * ---
578      *
579      * Params:
580      *     key = the key associated with the pointer
581      *     id = the id associated with the pointer
582      *     dg = a callback that performs the archiving of value pointed to by the pointer
583      */
584     void archivePointer (string key, Id id, void delegate () dg)
585     {
586         restore(lastElement) in {
587             lastElement = lastElement.element(Tags.pointerTag)
588             .attribute(Attributes.keyAttribute, toData(key))
589             .attribute(Attributes.idAttribute, toData(id));
590 
591             dg();
592         };
593     }
594 
595     /**
596      * Archives a reference.
597      *
598      * A reference is reference to another value. For example, if an object is archived
599      * more than once, the first time it's archived it will actual archive the object.
600      * The second time the object will be archived a reference will be archived instead
601      * of the actual object.
602      *
603      * This method is also used when archiving a pointer that points to a value that has
604      * been or will be archived as well.
605      *
606      * Examples:
607      * ---
608      * class Foo {}
609      *
610      * class Bar
611      * {
612      *     Foo f;
613      *     Foo f2;
614      * }
615      *
616      * auto bar = new Bar;
617      * bar.f = new Foo;
618      * bar.f2 = bar.f;
619      *
620      * auto archive = new XmlArchive!();
621      *
622      * // when achiving "bar"
623      * archive.archiveObject(Foo.classinfo.name, "Foo", "f", 0, {});
624      * archive.archiveReference("f2", 0); // archive a reference to "f"
625      * ---
626      *
627      * Params:
628      *     key = the key associated with the reference
629      *     id = the id of the value this reference refers to
630      */
631     void archiveReference (string key, Id id)
632     {
633         lastElement.element(Tags.referenceTag, toData(id))
634         .attribute(Attributes.keyAttribute, toData(key));
635     }
636 
637     /**
638      * Archives a slice.
639      *
640      * This method should be used when archiving an array that is a slice of an
641      * already archived array or an array that has not yet been archived.
642      *
643      * Examples:
644      * ---
645      * auto arr = [1, 2, 3, 4];
646      * auto slice = arr[1 .. 3];
647      *
648      * auto archive = new XmlArchive!();
649      * // archive "arr" with id 0
650      *
651      * auto s = Slice(slice.length, 1);
652      * archive.archiveSlice(s, 1, 0);
653      * ---
654      *
655      * Params:
656      *     slice = the slice to be archived
657      *     sliceId = the id associated with the slice
658      *     arrayId = the id associated with the array this slice is a slice of
659      */
660     void archiveSlice (Slice slice, Id sliceId, Id arrayId)
661     {
662         if (auto sliceNode = getArchivedArray(sliceId))
663         {
664             if (auto arrayNode = getArchivedArray(arrayId))
665             {
666                 sliceNode.parent.element(Tags.sliceTag, toData(arrayNode.id))
667                 .attribute(Attributes.keyAttribute, toData(sliceNode.key))
668                 .attribute(Attributes.offsetAttribute, toData(slice.offset))
669                 .attribute(Attributes.lengthAttribute, toData(slice.length));
670             }
671         }
672     }
673 
674     /**
675      * Archives a struct.
676      *
677      * Examples:
678      * ---
679      * struct Foo
680      * {
681      *     int a;
682      * }
683      *
684      * auto foo = Foo(3);
685      *
686      * auto archive = new XmlArchive!();
687      * archive.archiveStruct(Foo.stringof, "foo", 0, {
688      *     // archive the fields of Foo
689      * });
690      * ---
691      *
692      * Params:
693      *     type = the type of the struct
694      *     key = the key associated with the struct
695      *     id = the id associated with the struct
696      *     dg = a callback that performs the archiving of the individual fields
697      */
698     void archiveStruct (string type, string key, Id id, void delegate () dg)
699     {
700         restore(lastElement) in {
701             lastElement = lastElement.element(Tags.structTag)
702             .attribute(Attributes.typeAttribute, toData(type))
703             .attribute(Attributes.keyAttribute, toData(key))
704             .attribute(Attributes.idAttribute, toData(id));
705 
706             dg();
707         };
708     }
709 
710     /**
711      * Archives the given value.
712      *
713      * Params:
714      *     value = the value to archive
715      *     key = the key associated with the value
716      *     id = the id associated wit the value
717      */
718     void archive (string value, string key, Id id)
719     {
720         archiveString(value, key, id);
721     }
722 
723     /// Ditto
724     void archive (wstring value, string key, Id id)
725     {
726         archiveString(value, key, id);
727     }
728 
729     /// Ditto
730     void archive (dstring value, string key, Id id)
731     {
732         archiveString(value, key, id);
733     }
734 
735     private void archiveString (T) (T value, string key, Id id)
736     {
737         restore(lastElement) in {
738             alias ElementTypeOfArray!(T) ElementType;
739             auto array = Array(value.ptr, value.length, ElementType.sizeof);
740 
741             internalArchiveArray(array, ElementType.stringof, key, id, Tags.stringTag, toData(value));
742         };
743     }
744 
745     /// Ditto
746     void archive (bool value, string key, Id id)
747     {
748         archivePrimitive(value, key, id);
749     }
750 
751     /// Ditto
752     void archive (byte value, string key, Id id)
753     {
754         archivePrimitive(value, key, id);
755     }
756 
757     //currently not suppported by to!()
758     /*void archive (cdouble value, string key, Id id)
759     {
760         archivePrimitive(value, key, id);
761     }*/
762 
763     //currently not implemented but a reserved keyword
764     /*void archive (cent value, string key, Id id)
765     {
766         archivePrimitive(value, key, id);
767     }*/
768 
769     //currently not suppported by to!()
770     /*void archive (cfloat value, string key, Id id)
771     {
772         archivePrimitive(value, key, id);
773     }*/
774 
775     /// Ditto
776     void archive (char value, string key, Id id)
777     {
778         archivePrimitive(value, key, id);
779     }
780 
781     //currently not suppported by to!()
782     /*void archive (creal value, string key, Id id)
783     {
784         archivePrimitive(value, key, id);
785     }*/
786 
787     /// Ditto
788     void archive (dchar value, string key, Id id)
789     {
790         archivePrimitive(value, key, id);
791     }
792 
793     /// Ditto
794     void archive (double value, string key, Id id)
795     {
796         archivePrimitive(value, key, id);
797     }
798 
799     /// Ditto
800     void archive (float value, string key, Id id)
801     {
802         archivePrimitive(value, key, id);
803     }
804 
805     //currently not suppported by to!()
806     /*void archive (idouble value, string key, Id id)
807     {
808         archivePrimitive(value, key, id);
809     }*/
810 
811     //currently not suppported by to!()
812     /*void archive (ifloat value, string key, Id id)
813     {
814         archivePrimitive(value, key, id);
815     }*/
816 
817     /// Ditto
818     void archive (int value, string key, Id id)
819     {
820         archivePrimitive(value, key, id);
821     }
822 
823     //currently not suppported by to!()
824     /*void archive (ireal value, string key, Id id)
825     {
826         archivePrimitive(value, key, id);
827     }*/
828 
829     /// Ditto
830     void archive (long value, string key, Id id)
831     {
832         archivePrimitive(value, key, id);
833     }
834 
835     /// Ditto
836     void archive (real value, string key, Id id)
837     {
838         archivePrimitive(value, key, id);
839     }
840 
841     /// Ditto
842     void archive (short value, string key, Id id)
843     {
844         archivePrimitive(value, key, id);
845     }
846 
847     /// Ditto
848     void archive (ubyte value, string key, Id id)
849     {
850         archivePrimitive(value, key, id);
851     }
852 
853     //currently not implemented but a reserved keyword
854     /*void archive (ucent value, string key, Id id)
855     {
856         archivePrimitive(value, key, id);
857     }*/
858 
859     /// Ditto
860     void archive (uint value, string key, Id id)
861     {
862         archivePrimitive(value, key, id);
863     }
864 
865     /// Ditto
866     void archive (ulong value, string key, Id id)
867     {
868         archivePrimitive(value, key, id);
869     }
870 
871     /// Ditto
872     void archive (ushort value, string key, Id id)
873     {
874         archivePrimitive(value, key, id);
875     }
876 
877     /// Ditto
878     void archive (wchar value, string key, Id id)
879     {
880         archivePrimitive(value, key, id);
881     }
882 
883     private void archivePrimitive (T) (T value, string key, Id id)
884     {
885         lastElement.element(toData(T.stringof), toData(value))
886         .attribute(Attributes.keyAttribute, toData(key))
887         .attribute(Attributes.idAttribute, toData(id));
888     }
889 
890     /**
891      * Unarchives the value associated with the given key as an array.
892      *
893      * Examples:
894      * ---
895      * auto archive = new XmlArchive!();
896      * archive.beginUnarchiving(data);
897      * auto id = archive.unarchiveArray("arr", (size_t length) {
898      *     auto arr = new int[length]; // pre-allocate the array
899      *     // unarchive the individual elements of "arr"
900      * });
901      * ---
902      *
903      * Params:
904      *     key = the key associated with the array
905      *     dg = a callback that performs the unarchiving of the individual elements.
906      *             $(I length) is the length of the archived array
907      *
908      * Returns: the id associated with the array
909      *
910      * See_Also: unarchiveArray
911      */
912     Id unarchiveArray (string key, void delegate (size_t) dg)
913     {
914         return restore!(Id)(lastElement) in {
915             auto element = getElement(Tags.arrayTag, key);
916 
917             if (!element.isValid)
918                 return Id.max;
919 
920             lastElement = element;
921             auto len = getValueOfAttribute(Attributes.lengthAttribute);
922 
923             if (!len)
924                 return Id.max;
925 
926             auto length = fromData!(size_t)(len);
927             auto id = getValueOfAttribute(Attributes.idAttribute);
928 
929             if (!id)
930                 return Id.max;
931 
932             dg(length);
933 
934             return toId(id);
935         };
936     }
937 
938     /**
939      * Unarchives the value associated with the given id as an array.
940      *
941      * Examples:
942      * ---
943      * auto archive = new XmlArchive!();
944      * archive.beginUnarchiving(data);
945      * archive.unarchiveArray(0, (size_t length) {
946      *     auto arr = new int[length]; // pre-allocate the array
947      *     // unarchive the individual elements of "arr"
948      * });
949      * ---
950      *
951      * Params:
952      *     id = the id associated with the value
953      *     dg = a callback that performs the unarchiving of the individual elements.
954      *             $(I length) is the length of the archived array
955      *
956      * See_Also: unarchiveArray
957      */
958     void unarchiveArray (Id id, void delegate (size_t) dg)
959     {
960         restore(lastElement) in {
961             auto element = getElement(Tags.arrayTag, to!(string)(id), Attributes.idAttribute);
962 
963             if (!element.isValid)
964                 return;
965 
966             lastElement = element;
967             auto len = getValueOfAttribute(Attributes.lengthAttribute);
968 
969             if (!len)
970                 return;
971 
972             auto length = fromData!(size_t)(len);
973             auto stringId = getValueOfAttribute(Attributes.idAttribute);
974 
975             if (!stringId)
976                 return;
977 
978             dg(length);
979         };
980     }
981 
982     /**
983      * Unarchives the value associated with the given id as an associative array.
984      *
985      * Examples:
986      * ---
987      * auto archive = new XmlArchive!();
988      * archive.beginUnarchiving(data);
989      *
990      * auto id = archive.unarchiveAssociativeArray("aa", (size_t length) {
991      *     // unarchive the individual keys and values
992      * });
993      * ---
994      *
995      * Params:
996      *     key = the key associated with the associative array
997      *     dg = a callback that performs the unarchiving of the individual keys and values.
998      *             $(I length) is the length of the archived associative array
999      *
1000      * Returns: the id associated with the associative array
1001      *
1002      * See_Also: unarchiveAssociativeArrayKey
1003      * See_Also: unarchiveAssociativeArrayValue
1004      */
1005     Id unarchiveAssociativeArray (string key, void delegate (size_t length) dg)
1006     {
1007         return restore!(Id)(lastElement) in {
1008             auto element = getElement(Tags.associativeArrayTag, key);
1009 
1010             if (!element.isValid)
1011                 return Id.max;
1012 
1013             lastElement = element;
1014             auto len = getValueOfAttribute(Attributes.lengthAttribute);
1015 
1016             if (!len)
1017                 return Id.max;
1018 
1019             auto length = fromData!(size_t)(len);
1020             auto id = getValueOfAttribute(Attributes.idAttribute);
1021 
1022             if (!id)
1023                 return Id.max;
1024 
1025             dg(length);
1026 
1027             return toId(id);
1028         };
1029     }
1030 
1031     /**
1032      * Unarchives an associative array key.
1033      *
1034      * There are separate methods for unarchiving associative array keys and values
1035      * because both the key and the value can be of arbitrary type and needs to be
1036      * unarchived on its own.
1037      *
1038      * Examples:
1039      * ---
1040      * auto archive = new XmlArchive!();
1041      * archive.beginUnarchiving(data);
1042      *
1043      * for (size_t i = 0; i < length; i++)
1044      * {
1045      *     unarchiveAssociativeArrayKey(to!(string(i), {
1046      *         // unarchive the key
1047      *     }));
1048      * }
1049      * ---
1050      *
1051      * The for statement in the above example would most likely be executed in the
1052      * callback passed to the unarchiveAssociativeArray method.
1053      *
1054      * Params:
1055      *     key = the key associated with the key
1056      *     dg = a callback that performs the actual unarchiving of the key
1057      *
1058      * See_Also: unarchiveAssociativeArrayValue
1059      * See_Also: unarchiveAssociativeArray
1060      */
1061     void unarchiveAssociativeArrayKey (string key, void delegate () dg)
1062     {
1063         internalUnarchiveAAKeyValue(key, Tags.keyTag, dg);
1064     }
1065 
1066     /**
1067      * Unarchives an associative array value.
1068      *
1069      * There are separate methods for unarchiving associative array keys and values
1070      * because both the key and the value can be of arbitrary type and needs to be
1071      * unarchived on its own.
1072      *
1073      * Examples:
1074      * ---
1075      * auto archive = new XmlArchive!();
1076      * archive.beginUnarchiving(data);
1077      *
1078      * for (size_t i = 0; i < length; i++)
1079      * {
1080      *     unarchiveAssociativeArrayValue(to!(string(i), {
1081      *         // unarchive the value
1082      *     }));
1083      * }
1084      * ---
1085      *
1086      * The for statement in the above example would most likely be executed in the
1087      * callback passed to the unarchiveAssociativeArray method.
1088      *
1089      * Params:
1090      *     key = the key associated with the value
1091      *     dg = a callback that performs the actual unarchiving of the value
1092      *
1093      * See_Also: unarchiveAssociativeArrayKey
1094      * See_Also: unarchiveAssociativeArray
1095      */
1096     void unarchiveAssociativeArrayValue (string key, void delegate () dg)
1097     {
1098         internalUnarchiveAAKeyValue(key, Tags.valueTag, dg);
1099     }
1100 
1101     private void internalUnarchiveAAKeyValue (string key, Data tag, void delegate () dg)
1102     {
1103         restore(lastElement) in {
1104             auto element = getElement(tag, key);
1105 
1106             if (!element.isValid)
1107                 return;
1108 
1109             lastElement = element;
1110 
1111             dg();
1112         };
1113     }
1114 
1115     /**
1116      * Unarchives the value associated with the given key as a bool.
1117      *
1118      * This method is used when the unarchiving a enum value with the base type bool.
1119      *
1120      * Params:
1121      *     key = the key associated with the value
1122      *
1123      * Returns: the unarchived value
1124      */
1125     bool unarchiveEnumBool (string key, out Id id)
1126     {
1127         return unarchiveEnum!(bool)(key, id);
1128     }
1129 
1130     /// Ditto
1131     byte unarchiveEnumByte (string key, out Id id)
1132     {
1133         return unarchiveEnum!(byte)(key, id);
1134     }
1135 
1136     /// Ditto
1137     char unarchiveEnumChar (string key, out Id id)
1138     {
1139         return unarchiveEnum!(char)(key, id);
1140     }
1141 
1142     /// Ditto
1143     dchar unarchiveEnumDchar (string key, out Id id)
1144     {
1145         return unarchiveEnum!(dchar)(key, id);
1146     }
1147 
1148     /// Ditto
1149     int unarchiveEnumInt (string key, out Id id)
1150     {
1151         return unarchiveEnum!(int)(key, id);
1152     }
1153 
1154     /// Ditto
1155     long unarchiveEnumLong (string key, out Id id)
1156     {
1157         return unarchiveEnum!(long)(key, id);
1158     }
1159 
1160     /// Ditto
1161     short unarchiveEnumShort (string key, out Id id)
1162     {
1163         return unarchiveEnum!(short)(key, id);
1164     }
1165 
1166     /// Ditto
1167     ubyte unarchiveEnumUbyte (string key, out Id id)
1168     {
1169         return unarchiveEnum!(ubyte)(key, id);
1170     }
1171 
1172     /// Ditto
1173     uint unarchiveEnumUint (string key, out Id id)
1174     {
1175         return unarchiveEnum!(uint)(key, id);
1176     }
1177 
1178     /// Ditto
1179     ulong unarchiveEnumUlong (string key, out Id id)
1180     {
1181         return unarchiveEnum!(ulong)(key, id);
1182     }
1183 
1184     /// Ditto
1185     ushort unarchiveEnumUshort (string key, out Id id)
1186     {
1187         return unarchiveEnum!(ushort)(key, id);
1188     }
1189 
1190     /// Ditto
1191     wchar unarchiveEnumWchar (string key, out Id id)
1192     {
1193         return unarchiveEnum!(wchar)(key, id);
1194     }
1195 
1196     /**
1197      * Unarchives the value associated with the given id as a bool.
1198      *
1199      * This method is used when the unarchiving a enum value with the base type bool.
1200      *
1201      * Params:
1202      *     id = the id associated with the value
1203      *
1204      * Returns: the unarchived value
1205      */
1206     bool unarchiveEnumBool (Id id)
1207     {
1208         return unarchiveEnum!(bool)(id);
1209     }
1210 
1211     /// Ditto
1212     byte unarchiveEnumByte (Id id)
1213     {
1214         return unarchiveEnum!(byte)(id);
1215     }
1216 
1217     /// Ditto
1218     char unarchiveEnumChar (Id id)
1219     {
1220         return unarchiveEnum!(char)(id);
1221     }
1222 
1223     /// Ditto
1224     dchar unarchiveEnumDchar (Id id)
1225     {
1226         return unarchiveEnum!(dchar)(id);
1227     }
1228 
1229     /// Ditto
1230     int unarchiveEnumInt (Id id)
1231     {
1232         return unarchiveEnum!(int)(id);
1233     }
1234 
1235     /// Ditto
1236     long unarchiveEnumLong (Id id)
1237     {
1238         return unarchiveEnum!(long)(id);
1239     }
1240 
1241     /// Ditto
1242     short unarchiveEnumShort (Id id)
1243     {
1244         return unarchiveEnum!(short)(id);
1245     }
1246 
1247     /// Ditto
1248     ubyte unarchiveEnumUbyte (Id id)
1249     {
1250         return unarchiveEnum!(ubyte)(id);
1251     }
1252 
1253     /// Ditto
1254     uint unarchiveEnumUint (Id id)
1255     {
1256         return unarchiveEnum!(uint)(id);
1257     }
1258 
1259     /// Ditto
1260     ulong unarchiveEnumUlong (Id id)
1261     {
1262         return unarchiveEnum!(ulong)(id);
1263     }
1264 
1265     /// Ditto
1266     ushort unarchiveEnumUshort (Id id)
1267     {
1268         return unarchiveEnum!(ushort)(id);
1269     }
1270 
1271     /// Ditto
1272     wchar unarchiveEnumWchar (Id id)
1273     {
1274         return unarchiveEnum!(wchar)(id);
1275     }
1276 
1277     private T unarchiveEnum (T, U) (U keyOrId)
1278     {
1279         Id dummy;
1280         return unarchiveEnum!(T, U)(keyOrId, dummy);
1281     }
1282 
1283     private T unarchiveEnum (T, U) (U keyOrId, out Id id)
1284     {
1285         id = Id.max;
1286         auto tag = Tags.enumTag;
1287 
1288         static if (isString!(U))
1289             auto element = getElement(Tags.enumTag, keyOrId);
1290 
1291         else static if (is(U == Id))
1292             auto element = getElement(tag, toData(keyOrId), Attributes.idAttribute);
1293 
1294         else
1295             static assert (false, format!(`Invalid type "`, U, `". Valid types are "string" and "Id"`));
1296 
1297         if (!element.isValid)
1298             return T.init;
1299 
1300         auto stringId = getValueOfAttribute(Attributes.idAttribute, element);
1301         id = stringId ? toId(stringId) : Id.max;
1302 
1303         return fromData!(T)(element.value);
1304     }
1305 
1306     /**
1307      * Unarchives the base class associated with the given key.
1308      *
1309      * This method is used to indicate that the all following calls to unarchive a
1310      * value should be part of the base class. This method is usually called within the
1311      * callback passed to unarchiveObject. The unarchiveObject method can the mark the
1312      * end of the class.
1313      *
1314      * Examples:
1315      * ---
1316      * auto archive = new XmlArchive!();
1317      * archive.beginUnarchiving(data);
1318      * archive.unarchiveBaseClass("base");
1319      * ---
1320      *
1321      * Params:
1322      *     key = the key associated with the base class.
1323      *
1324      * See_Also: unarchiveObject
1325      */
1326     void unarchiveBaseClass (string key)
1327     {
1328         auto element = getElement(Tags.baseTag, key);
1329 
1330         if (element.isValid)
1331             lastElement = element;
1332     }
1333 
1334     /**
1335      * Unarchives the object associated with the given key.
1336      *
1337      * Examples:
1338      * ---
1339      * class Foo
1340      * {
1341      *     int a;
1342      * }
1343      *
1344      * auto archive = new XmlArchive!();
1345      * archive.beginUnarchiving(data);
1346      *
1347      * Id id;
1348      * Object o;
1349      *
1350      * archive.unarchiveObject("foo", id, o, {
1351      *     // unarchive the fields of Foo
1352      * });
1353      *
1354      * auto foo = cast(Foo) o;
1355      * ---
1356      *
1357      * Params:
1358      *     key = the key associated with the object
1359      *     id = the id associated with the object
1360      *     result = the unarchived object
1361      *     dg = a callback the performs the unarchiving of the individual fields
1362      */
1363     void unarchiveObject (string key, out Id id, out Object result, void delegate () dg)
1364     {
1365         restore(lastElement) in {
1366             auto tmp = getElement(Tags.objectTag, key, Attributes.keyAttribute, false);
1367 
1368             if (!tmp.isValid)
1369             {
1370                 lastElement = getElement(Tags.nullTag, key);
1371                 return;
1372             }
1373 
1374             lastElement = tmp;
1375 
1376             auto runtimeType = getValueOfAttribute(Attributes.runtimeTypeAttribute);
1377 
1378             if (!runtimeType)
1379                 return;
1380 
1381             auto name = fromData!(string)(runtimeType);
1382             auto stringId = getValueOfAttribute(Attributes.idAttribute);
1383 
1384             if (!stringId)
1385                 return;
1386 
1387             id = toId(stringId);
1388             result = newInstance(name);
1389             dg();
1390         };
1391     }
1392 
1393     /**
1394      * Unarchives the pointer associated with the given key.
1395      *
1396      * Examples:
1397      * ---
1398      * auto archive = new XmlArchive!();
1399      * archive.beginUnarchiving(data);
1400      * auto id = unarchivePointer("ptr", {
1401      *     // unarchive the value pointed to by the pointer
1402      * });
1403      * ---
1404      *
1405      * Params:
1406      *     key = the key associated with the pointer
1407      *     dg = a callback that performs the unarchiving of value pointed to by the pointer
1408      *
1409      * Returns: the id associated with the pointer
1410      */
1411     Id unarchivePointer (string key, void delegate () dg)
1412     {
1413         return restore!(Id)(lastElement) in {
1414             auto tmp = getElement(Tags.pointerTag, key, Attributes.keyAttribute, false);
1415 
1416             if (!tmp.isValid)
1417             {
1418                 lastElement = getElement(Tags.nullTag, key);
1419                 return Id.max;
1420             }
1421 
1422             lastElement = tmp;
1423             auto id = getValueOfAttribute(Attributes.idAttribute);
1424 
1425             if (!id)
1426                 return Id.max;
1427 
1428             dg();
1429 
1430             return toId(id);
1431         };
1432     }
1433 
1434     /**
1435      * Unarchives the reference associated with the given key.
1436      *
1437      * A reference is reference to another value. For example, if an object is archived
1438      * more than once, the first time it's archived it will actual archive the object.
1439      * The second time the object will be archived a reference will be archived instead
1440      * of the actual object.
1441      *
1442      * This method is also used when unarchiving a pointer that points to a value that has
1443      * been or will be unarchived as well.
1444      *
1445      * Examples:
1446      * ---
1447      * auto archive = new XmlArchive!();
1448      * archive.beginUnarchiving(data);
1449      * auto id = unarchiveReference("foo");
1450      *
1451      * // unarchive the value with the associated id
1452      * ---
1453      *
1454      * Params:
1455      *     key = the key associated with the reference
1456      *
1457      * Returns: the id the reference refers to
1458      */
1459     Id unarchiveReference (string key)
1460     {
1461         auto element = getElement(Tags.referenceTag, key, Attributes.keyAttribute, false);
1462 
1463         if (element.isValid)
1464             return toId(element.value);
1465 
1466         return Id.max;
1467     }
1468 
1469     /**
1470      * Unarchives the slice associated with the given key.
1471      *
1472      * This method should be used when unarchiving an array that is a slice of an
1473      * already unarchived array or an array that has not yet been unarchived.
1474      *
1475      * Examples:
1476      * ---
1477      * auto archive = new XmlArchive!();
1478      * archive.beginUnarchiving(data);
1479      * auto slice = unarchiveSlice("slice");
1480      *
1481      * // slice the original array with the help of the unarchived slice
1482      * ---
1483      *
1484      * Params:
1485      *     key = the key associated with the slice
1486      *
1487      * Returns: the unarchived slice
1488      */
1489     Slice unarchiveSlice (string key)
1490     {
1491         auto element = getElement(Tags.sliceTag, key, Attributes.keyAttribute, false);
1492 
1493         if (element.isValid)
1494         {
1495             auto length = fromData!(size_t)(getValueOfAttribute(Attributes.lengthAttribute, element));
1496             auto offset = fromData!(size_t)(getValueOfAttribute(Attributes.offsetAttribute, element));
1497             auto id = toId(element.value);
1498 
1499             return Slice(length, offset, id);
1500         }
1501 
1502         return Slice.init;
1503     }
1504 
1505     /**
1506      * Unarchives the struct associated with the given key.
1507      *
1508      * Examples:
1509      * ---
1510      * struct Foo
1511      * {
1512      *     int a;
1513      * }
1514      *
1515      * auto archive = new XmlArchive!();
1516      * archive.beginUnarchiving(data);
1517      * archive.unarchiveStruct("foo", {
1518      *     // unarchive the fields of Foo
1519      * });
1520      * ---
1521      *
1522      * Params:
1523      *     key = the key associated with the struct
1524      *     dg = a callback that performs the unarchiving of the individual fields
1525      */
1526     Id unarchiveStruct (string key, void delegate () dg)
1527     {
1528         Id id = Id.max;
1529 
1530         restore(lastElement) in {
1531             auto element = getElement(Tags.structTag, key);
1532 
1533             if (!element.isValid)
1534                 return;
1535 
1536             auto stringId = getValueOfAttribute(Attributes.idAttribute, element);
1537             id = stringId ? toId(stringId) : Id.max;
1538 
1539             lastElement = element;
1540             dg();
1541         };
1542 
1543         return id;
1544     }
1545 
1546     /**
1547      * Unarchives the struct associated with the given id.
1548      *
1549      * Examples:
1550      * ---
1551      * struct Foo
1552      * {
1553      *     int a;
1554      * }
1555      *
1556      * auto archive = new XmlArchive!();
1557      * archive.beginUnarchiving(data);
1558      * archive.unarchiveStruct(0, {
1559      *     // unarchive the fields of Foo
1560      * });
1561      * ---
1562      *
1563      * Params:
1564      *     id = the id associated with the struct
1565      *     dg = a callback that performs the unarchiving of the individual fields.
1566      *                The callback will receive the key the struct was archived with.
1567      */
1568     void unarchiveStruct (Id id, void delegate () dg)
1569     {
1570         restore(lastElement) in {
1571             auto element = getElement(Tags.structTag, toData(id), Attributes.idAttribute);
1572 
1573             if (!element.isValid)
1574                 return;
1575 
1576             lastElement = element;
1577             dg();
1578         };
1579     }
1580 
1581     /**
1582      * Unarchives the string associated with the given id.
1583      *
1584      * Examples:
1585      * ---
1586      * auto archive = new XmlArchive!();
1587      * archive.beginUnarchiving(data);
1588      * auto str = archive.unarchiveString(0);
1589      * ---
1590      *
1591      * Params:
1592      *     id = the id associated with the string
1593      *
1594      * Returns: the unarchived string
1595      */
1596     string unarchiveString (string key, out Id id)
1597     {
1598         return internalUnarchiveString!(string)(key, id);
1599     }
1600 
1601     /// Ditto
1602     wstring unarchiveWstring (string key, out Id id)
1603     {
1604         return internalUnarchiveString!(wstring)(key, id);
1605     }
1606 
1607     /// Ditto
1608     dstring unarchiveDstring (string key, out Id id)
1609     {
1610         return internalUnarchiveString!(dstring)(key, id);
1611     }
1612 
1613     private T internalUnarchiveString (T) (string key, out Id id)
1614     {
1615         auto element = getElement(Tags.stringTag, key);
1616 
1617         if (!element.isValid)
1618             return T.init;
1619 
1620         auto value = fromData!(T)(element.value);
1621         auto stringId = getValueOfAttribute(Attributes.idAttribute, element);
1622 
1623         if (!stringId)
1624             return T.init;
1625 
1626         id = toId(stringId);
1627         return value;
1628     }
1629 
1630     /**
1631      * Unarchives the string associated with the given key.
1632      *
1633      * Examples:
1634      * ---
1635      * auto archive = new XmlArchive!();
1636      * archive.beginUnarchiving(data);
1637      *
1638      * Id id;
1639      * auto str = archive.unarchiveString("str", id);
1640      * ---
1641      *
1642      * Params:
1643      *     id = the id associated with the string
1644      *
1645      * Returns: the unarchived string
1646      */
1647     string unarchiveString (Id id)
1648     {
1649         return internalUnarchiveString!(string)(id);
1650     }
1651 
1652     /// Ditto
1653     wstring unarchiveWstring (Id id)
1654     {
1655         return internalUnarchiveString!(wstring)(id);
1656     }
1657 
1658     /// Ditto
1659     dstring unarchiveDstring (Id id)
1660     {
1661         return internalUnarchiveString!(dstring)(id);
1662     }
1663 
1664     private T internalUnarchiveString (T) (Id id)
1665     {
1666         auto element = getElement(Tags.stringTag, to!(string)(id), Attributes.idAttribute);
1667 
1668         if (!element.isValid)
1669             return T.init;
1670 
1671         return fromData!(T)(element.value);
1672     }
1673 
1674     /**
1675      * Unarchives the value associated with the given key.
1676      *
1677      * Examples:
1678      * ---
1679      * auto archive = new XmlArchive!();
1680      * archive.beginUnarchiving(data);
1681      * auto foo = unarchiveBool("foo");
1682      * ---
1683      * Params:
1684      *     key = the key associated with the value
1685      *
1686      * Returns: the unarchived value
1687      */
1688     bool unarchiveBool (string key, out Id id)
1689     {
1690         return unarchivePrimitive!(bool)(key, id);
1691     }
1692 
1693     /// Ditto
1694     byte unarchiveByte (string key, out Id id)
1695     {
1696         return unarchivePrimitive!(byte)(key, id);
1697     }
1698 
1699     //currently not suppported by to!()
1700     /*cdouble unarchiveCdouble (string key, out Id id)
1701     {
1702         return unarchivePrimitive!(cdouble)(key, id);
1703     }*/
1704 
1705      //currently not implemented but a reserved keyword
1706     /*cent unarchiveCent (string key, out Id id)
1707     {
1708         return unarchivePrimitive!(cent)(key, id);
1709     }*/
1710 
1711     // currently not suppported by to!()
1712     /*cfloat unarchiveCfloat (string key, out Id id)
1713     {
1714         return unarchivePrimitive!(cfloat)(key, id);
1715     }*/
1716 
1717     /// Ditto
1718     char unarchiveChar (string key, out Id id)
1719     {
1720         return unarchivePrimitive!(char)(key, id);
1721     }
1722 
1723      //currently not implemented but a reserved keyword
1724     /*creal unarchiveCreal (string key, out Id id)
1725     {
1726         return unarchivePrimitive!(creal)(key, id);
1727     }*/
1728 
1729     /// Ditto
1730     dchar unarchiveDchar (string key, out Id id)
1731     {
1732         return unarchivePrimitive!(dchar)(key, id);
1733     }
1734 
1735     /// Ditto
1736     double unarchiveDouble (string key, out Id id)
1737     {
1738         return unarchivePrimitive!(double)(key, id);
1739     }
1740 
1741     /// Ditto
1742     float unarchiveFloat (string key, out Id id)
1743     {
1744         return unarchivePrimitive!(float)(key, id);
1745     }
1746 
1747     //currently not suppported by to!()
1748     /*idouble unarchiveIdouble (string key, out Id id)
1749     {
1750         return unarchivePrimitive!(idouble)(key, id);
1751     }*/
1752 
1753     // currently not suppported by to!()*/
1754     /*ifloat unarchiveIfloat (string key, out Id id)
1755     {
1756         return unarchivePrimitive!(ifloat)(key, id);
1757     }*/
1758 
1759     /// Ditto
1760     int unarchiveInt (string key, out Id id)
1761     {
1762         return unarchivePrimitive!(int)(key, id);
1763     }
1764 
1765     // currently not suppported by to!()
1766     /*ireal unarchiveIreal (string key, out Id id)
1767     {
1768         return unarchivePrimitive!(ireal)(key, id);
1769     }*/
1770 
1771     /// Ditto
1772     long unarchiveLong (string key, out Id id)
1773     {
1774         return unarchivePrimitive!(long)(key, id);
1775     }
1776 
1777     /// Ditto
1778     real unarchiveReal (string key, out Id id)
1779     {
1780         return unarchivePrimitive!(real)(key, id);
1781     }
1782 
1783     /// Ditto
1784     short unarchiveShort (string key, out Id id)
1785     {
1786         return unarchivePrimitive!(short)(key, id);
1787     }
1788 
1789     /// Ditto
1790     ubyte unarchiveUbyte (string key, out Id id)
1791     {
1792         return unarchivePrimitive!(ubyte)(key, id);
1793     }
1794 
1795     // currently not implemented but a reserved keyword
1796     /*ucent unarchiveCcent (string key, out Id id)
1797     {
1798         return unarchivePrimitive!(ucent)(key, id);
1799     }*/
1800 
1801     /// Ditto
1802     uint unarchiveUint (string key, out Id id)
1803     {
1804         return unarchivePrimitive!(uint)(key, id);
1805     }
1806 
1807     /// Ditto
1808     ulong unarchiveUlong (string key, out Id id)
1809     {
1810         return unarchivePrimitive!(ulong)(key, id);
1811     }
1812 
1813     /// Ditto
1814     ushort unarchiveUshort (string key, out Id id)
1815     {
1816         return unarchivePrimitive!(ushort)(key, id);
1817     }
1818 
1819     /// Ditto
1820     wchar unarchiveWchar (string key, out Id id)
1821     {
1822         return unarchivePrimitive!(wchar)(key, id);
1823     }
1824 
1825     /**
1826      * Unarchives the value associated with the given id.
1827      *
1828      * Examples:
1829      * ---
1830      * auto archive = new XmlArchive!();
1831      * archive.beginUnarchiving(data);
1832      * auto foo = unarchiveBool(0);
1833      * ---
1834      * Params:
1835      *     id = the id associated with the value
1836      *
1837      * Returns: the unarchived value
1838      */
1839     bool unarchiveBool (Id id)
1840     {
1841         return unarchivePrimitive!(bool)(id);
1842     }
1843 
1844     /// Ditto
1845     byte unarchiveByte (Id id)
1846     {
1847         return unarchivePrimitive!(byte)(id);
1848     }
1849 
1850     //currently not suppported by to!()
1851     /*cdouble unarchiveCdouble (Id id, out Id id)
1852     {
1853         return unarchivePrimitive!(cdouble)(id,);
1854     }*/
1855 
1856      //currently not implemented but a reserved keyword
1857     /*cent unarchiveCent (Id id)
1858     {
1859         return unarchivePrimitive!(cent)(id);
1860     }*/
1861 
1862     // currently not suppported by to!()
1863     /*cfloat unarchiveCfloat (Id id)
1864     {
1865         return unarchivePrimitive!(cfloat)(id);
1866     }*/
1867 
1868     /// Ditto
1869     char unarchiveChar (Id id)
1870     {
1871         return unarchivePrimitive!(char)(id);
1872     }
1873 
1874      //currently not implemented but a reserved keyword
1875     /*creal unarchiveCreal (Id id)
1876     {
1877         return unarchivePrimitive!(creal)(id);
1878     }*/
1879 
1880     /// Ditto
1881     dchar unarchiveDchar (Id id)
1882     {
1883         return unarchivePrimitive!(dchar)(id);
1884     }
1885 
1886     /// Ditto
1887     double unarchiveDouble (Id id)
1888     {
1889         return unarchivePrimitive!(double)(id);
1890     }
1891 
1892     /// Ditto
1893     float unarchiveFloat (Id id)
1894     {
1895         return unarchivePrimitive!(float)(id);
1896     }
1897 
1898     //currently not suppported by to!()
1899     /*idouble unarchiveIdouble (Id id)
1900     {
1901         return unarchivePrimitive!(idouble)(id);
1902     }*/
1903 
1904     // currently not suppported by to!()*/
1905     /*ifloat unarchiveIfloat (Id id)
1906     {
1907         return unarchivePrimitive!(ifloat)(id);
1908     }*/
1909 
1910     /// Ditto
1911     int unarchiveInt (Id id)
1912     {
1913         return unarchivePrimitive!(int)(id);
1914     }
1915 
1916     // currently not suppported by to!()
1917     /*ireal unarchiveIreal (Id id)
1918     {
1919         return unarchivePrimitive!(ireal)(id);
1920     }*/
1921 
1922     /// Ditto
1923     long unarchiveLong (Id id)
1924     {
1925         return unarchivePrimitive!(long)(id);
1926     }
1927 
1928     /// Ditto
1929     real unarchiveReal (Id id)
1930     {
1931         return unarchivePrimitive!(real)(id);
1932     }
1933 
1934     /// Ditto
1935     short unarchiveShort (Id id)
1936     {
1937         return unarchivePrimitive!(short)(id);
1938     }
1939 
1940     /// Ditto
1941     ubyte unarchiveUbyte (Id id)
1942     {
1943         return unarchivePrimitive!(ubyte)(id);
1944     }
1945 
1946     // currently not implemented but a reserved keyword
1947     /*ucent unarchiveCcent (Id id)
1948     {
1949         return unarchivePrimitive!(ucent)(id);
1950     }*/
1951 
1952     /// Ditto
1953     uint unarchiveUint (Id id)
1954     {
1955         return unarchivePrimitive!(uint)(id);
1956     }
1957 
1958     /// Ditto
1959     ulong unarchiveUlong (Id id)
1960     {
1961         return unarchivePrimitive!(ulong)(id);
1962     }
1963 
1964     /// Ditto
1965     ushort unarchiveUshort (Id id)
1966     {
1967         return unarchivePrimitive!(ushort)(id);
1968     }
1969 
1970     /// Ditto
1971     wchar unarchiveWchar (Id id)
1972     {
1973         return unarchivePrimitive!(wchar)(id);
1974     }
1975 
1976     private T unarchivePrimitive (T, U) (U keyOrId)
1977     {
1978         Id id;
1979         return unarchivePrimitive!(T, U)(keyOrId, id);
1980     }
1981 
1982     private T unarchivePrimitive (T, U) (U keyOrId, out Id id)
1983     {
1984         id = Id.max;
1985         auto tag = toData(T.stringof);
1986 
1987         static if (isString!(U))
1988             auto element = getElement(tag, keyOrId);
1989 
1990         else static if (is(U == Id))
1991             auto element = getElement(tag, to!(string)(keyOrId), Attributes.idAttribute);
1992 
1993         else
1994             static assert (false, format!(`Invalid type "`, U, `". Valid types are "string" and "Id"`));
1995 
1996         if (!element.isValid)
1997             return T.init;
1998 
1999         auto stringId = getValueOfAttribute(Attributes.idAttribute, element);
2000         id = stringId ? toId(stringId) : Id.max;
2001 
2002         return fromData!(T)(element.value);
2003     }
2004 
2005     /**
2006      * Performs post processing of the array associated with the given id.
2007      *
2008      * Post processing can basically be anything that the archive wants to do. This
2009      * method is called by the serializer once for each serialized array at the end of
2010      * the serialization process when all values have been serialized.
2011      *
2012      * With this method the archive has a last chance of changing an archived array to
2013      * an archived slice instead.
2014      *
2015      * Params:
2016      *     id = the id associated with the array
2017      */
2018     void postProcessArray (Id id)
2019     {
2020         if (auto array = getArchivedArray(id))
2021             array.parent.attach(array.node);
2022     }
2023 
2024     private void addArchivedArray (Id id, doc.Node parent, doc.Node element, string key)
2025     {
2026         archivedArrays[id] = Node(parent, element, id, key);
2027     }
2028 
2029     private Node* getArchivedArray (Id id)
2030     {
2031         if (auto array = id in archivedArrays)
2032             return array;
2033 
2034         error(`Could not continue archiving due to no array with the Id "` ~ to!(string)(id) ~ `" was found.`, [to!(string)(id)]);
2035 
2036         return null;
2037     }
2038 
2039     private Node* getArchivedPointer (Id id)
2040     {
2041         if (auto pointer = id in archivedPointers)
2042             return pointer;
2043 
2044         error(`Could not continue archiving due to no pointer with the Id "` ~ to!(string)(id) ~ `" was found.`, [to!(string)(id)]);
2045 
2046         return null;
2047     }
2048 
2049     private doc.Node getElement (Data tag, string key, Data attribute = Attributes.keyAttribute, bool throwOnError = true)
2050     {
2051         auto set = lastElement.query[tag].attribute((doc.Node node) {
2052             if (node.name == attribute && node.value == key)
2053                 return true;
2054 
2055             return false;
2056         });
2057 
2058         if (set.nodes.length == 1)
2059             return set.nodes[0].parent;
2060 
2061         if (throwOnError)
2062         {
2063             if (set.nodes.length == 0)
2064                 error(`Could not find an element "` ~ to!(string)(tag) ~ `" with the attribute "` ~ to!(string)(attribute) ~ `" with the value "` ~ to!(string)(key) ~ `".`, [tag, Attributes.keyAttribute, key]);
2065 
2066             else
2067                 error(`Could not unarchive the value with the key "` ~ to!(string)(key) ~ `" due to malformed data.`, [tag, Attributes.keyAttribute, key]);
2068         }
2069 
2070         return doc.Node.invalid;
2071     }
2072 
2073     private Data getValueOfAttribute (Data attribute, doc.Node element = doc.Node.invalid)
2074     {
2075         if (!element.isValid)
2076             element = lastElement;
2077 
2078         auto set = element.query.attribute(attribute);
2079 
2080         if (set.nodes.length == 1)
2081             return set.nodes[0].value;
2082 
2083         else
2084         {
2085             if (set.nodes.length == 0)
2086                 error(`Could not find the attribute "` ~ to!(string)(attribute) ~ `".`, [attribute]);
2087 
2088             else
2089                 error(`Could not unarchive the value of the attribute "` ~ to!(string)(attribute) ~ `" due to malformed data.`, [attribute]);
2090         }
2091 
2092         return null;
2093     }
2094 
2095     private template errorMessage (ArchiveMode mode = ArchiveMode.archiving)
2096     {
2097         static if (mode == ArchiveMode.archiving)
2098             enum errorMessage = "Could not continue archiving due to unrecognized data format: ";
2099 
2100         else static if (mode == ArchiveMode.unarchiving)
2101             enum errorMessage = "Could not continue unarchiving due to unrecognized data format: ";
2102     }
2103 }