1 /**
2  * Copyright: Copyright (c) 2010-2011 Jacob Carlborg.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Feb 6, 2010
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module orange.serialization.archives.Archive;
8 
9 import std.array;
10 import std.conv;
11 import std.utf;
12 static import std.string;
13 
14 import orange.serialization.SerializationException;
15 import orange.serialization.Serializer;
16 import orange.util.Traits;
17 
18 /**
19  * This interface represents an archive. This is the interface all archive
20  * implementations need to implement to be able to be used as an archive with the
21  * serializer.
22  *
23  * The archive is the backend in the serialization process. It's independent of the
24  * serializer and any archive implementation. Although there are a couple of
25  * limitations of what archive types can be implemented (see below).
26  *
27  * The archive is responsible for archiving primitive types in the format chosen by
28  * the archive implementation. The archive ensures that all types are properly
29  * archived in a format that can be later unarchived.
30  *
31  * The archive can only handle primitive types, like strings, integers, floating
32  * point numbers and so on. It can not handle more complex types like objects or
33  * arrays; the serializer is responsible for breaking the complex types into
34  * primitive types that the archive can handle.
35  *
36  * Implementing an Archive Type:
37  *
38  * There are a couple of limitations when implementing a new archive, this is due
39  * to how the serializer and the archive interface is built. Except for what this
40  * interface says explicitly an archive needs to be able to handle the following:
41  *
42  * $(UL
43  *     $(LI unarchive a value based on a key or id, regardless of where in the archive
44  *         the value is located)
45  * $(LI most likely be able to modify already archived data)
46  * $(LI structured formats like JSON, XML and YAML works best)
47  * )
48  *
49  * If a method takes a delegate as one of its parameters that delegate should be
50  * considered as a callback to the serializer. The archive need to make sure that
51  * any archiving that is performed in the callback be a part of the type that is
52  * currently being archived. This is easiest explained by an example:
53  *
54  * ---
55  * void archiveArray (Array array, string type, string key, Id id, void delegate () dg)
56  * {
57  *     markBegningOfNewType("array");
58  *     storeMetadata(type, key, id);
59  *
60  *     beginNewScope();
61  *     dg();
62  *     endScope();
63  *
64  *     markEndOfType();
65  * }
66  * ---
67  *
68  * In the above example the archive have to make sure that any values archived by
69  * the callback (the delegate) get archived as an element of the array. The same
70  * principle applies to objects, structs, associative arrays and other
71  * non-primitives that accepts a delegate as a parameter.
72  *
73  * An archive implementation needs to be able to handle errors, like missing values
74  * in the serialized data, without throwing exceptions. This is because the
75  * interface of the serializer and an archive allows the user to set an error
76  * callback that is called when an error occurs; and the callback can choose to
77  * ignore the exceptions.
78  *
79  * In all the examples below "XmlArchive" is used as an example of an archive
80  * implementation. "data" is assumed to be the serialized data.
81  *
82  * When implementing a new archive type, if any of these methods do not make sense
83  * for that particular implementation just implement an empty method and return
84  * T.init, if the method returns a value.
85  */
86 interface Archive
87 {
88     /// The type of an ID.
89     alias size_t Id;
90 
91     /// The typed used to represent the archived data in an untyped form.
92     alias immutable(void)[] UntypedData;
93 
94     /**
95      * This is the type of an error callback which is called when an unexpected event occurs.
96      *
97      * Params:
98      *     exception = the exception indicating what error occurred
99      *     data = arbitrary data pass along, deprecated
100      */
101     alias void delegate (SerializationException exception) ErrorCallback;
102 
103     /**
104      * This callback will be called when an unexpected event occurs, i.e. an expected element
105      * is missing in the unarchiving process.
106      *
107      * Examples:
108      * ---
109      * auto archive = new XmlArchive!();
110      * serializer.errorCallback = (SerializationException exception) {
111      *     println(exception);
112      *     throw exception;
113      * };
114      * ---
115      */
116     ErrorCallback errorCallback ();
117 
118     /**
119      * This callback will be called when an unexpected event occurs, i.e. an expected element
120      * is missing in the unarchiving process.
121      *
122      * Examples:
123      * ---
124      * auto archive = new XmlArchive!();
125      * serializer.errorCallback = (SerializationException exception) {
126      *     println(exception);
127      *     throw exception;
128      * };
129      * ---
130      */
131     ErrorCallback errorCallback (ErrorCallback errorCallback);
132 
133     /// Starts the archiving process. Call this method before archiving any values.
134     void beginArchiving ();
135 
136     /**
137      * Begins the unarchiving process. Call this method before unarchiving any values.
138      *
139      * Params:
140      *     data = the data to unarchive
141      */
142     void beginUnarchiving (UntypedData data);
143 
144     /// Returns the data stored in the archive in an untyped form.
145     UntypedData untypedData ();
146 
147     /**
148      * Resets the archive. This resets the archive in a state making it ready to start
149      * a new archiving process.
150      */
151     void reset ();
152 
153     /**
154      * Archives an array.
155      *
156      * Examples:
157      * ---
158      * int[] arr = [1, 2, 3];
159      *
160      * auto archive = new XmlArchive!();
161      *
162      * auto a = Array(arr.ptr, arr.length, typeof(a[0]).sizeof);
163      *
164      * archive.archive(a, typeof(a[0]).string, "arr", 0, {
165      *     // archive the individual elements
166      * });
167      * ---
168      *
169      * Params:
170      *     array = the array to archive
171      *     type = the runtime type of an element of the array
172      *     key = the key associated with the array
173      *     id = the id associated with the array
174      *     dg = a callback that performs the archiving of the individual elements
175      */
176     void archiveArray (Array array, string type, string key, Id id, void delegate () dg);
177 
178     /**
179      * Archives an associative array.
180      *
181      * Examples:
182      * ---
183      * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
184      *
185      * auto archive = new XmlArchive!();
186      *
187      * archive.archive(string.stringof, int.stringof, arr.length, "arr", 0, {
188      *     // archive the individual keys and values
189      * });
190      * ---
191      *
192      *
193      * Params:
194      *     keyType = the runtime type of the keys
195      *     valueType = the runtime type of the values
196      *     length = the length of the associative array
197      *     key = the key associated with the associative array
198      *     id = the id associated with the associative array
199      *     dg = a callback that performs the archiving of the individual keys and values
200      *
201      * See_Also: archiveAssociativeArrayValue
202      * See_Also: archiveAssociativeArrayKey
203      */
204     void archiveAssociativeArray (string keyType, string valueType, size_t length, string key, Id id, void delegate () dg);
205 
206     /**
207      * Archives an associative array key.
208      *
209      * There are separate methods for archiving associative array keys and values
210      * because both the key and the value can be of arbitrary type and needs to be
211      * archived on its own.
212      *
213      * Examples:
214      * ---
215      * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
216      *
217      * auto archive = new XmlArchive!();
218      *
219      * foreach(k, v ; arr)
220      * {
221      *     archive.archiveAssociativeArrayKey(to!(string)(i), {
222      *         // archive the key
223      *     });
224      * }
225      * ---
226      *
227      * The foreach statement in the above example would most likely be executed in the
228      * callback passed to the archiveAssociativeArray method.
229      *
230      * Params:
231      *     key = the key associated with the key
232      *     dg = a callback that performs the actual archiving of the key
233      *
234      * See_Also: archiveAssociativeArray
235      * See_Also: archiveAssociativeArrayValue
236      */
237     void archiveAssociativeArrayKey (string key, void delegate () dg);
238 
239     /**
240      * Archives an associative array value.
241      *
242      * There are separate methods for archiving associative array keys and values
243      * because both the key and the value can be of arbitrary type and needs to be
244      * archived on its own.
245      *
246      * Examples:
247      * ---
248      * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3];
249      *
250      * auto archive = new XmlArchive!();
251      * size_t i;
252      *
253      * foreach(k, v ; arr)
254      * {
255      *     archive.archiveAssociativeArrayValue(to!(string)(i), {
256      *         // archive the value
257      *     });
258      *
259      *     i++;
260      * }
261      * ---
262      *
263      * The foreach statement in the above example would most likely be executed in the
264      * callback passed to the archiveAssociativeArray method.
265      *
266      * Params:
267      *     key = the key associated with the value
268      *     dg = a callback that performs the actual archiving of the value
269      *
270      * See_Also: archiveAssociativeArray
271      * See_Also: archiveAssociativeArrayKey
272      */
273     void archiveAssociativeArrayValue (string key, void delegate () dg);
274 
275     /**
276      * Archives the given value.
277      *
278      * Example:
279      * ---
280      * enum Foo : bool
281      * {
282      *     bar
283      * }
284      *
285      * auto foo = Foo.bar;
286      * auto archive = new XmlArchive!();
287      * archive.archive(foo, "bool", "foo", 0);
288      * ---
289      *
290      * Params:
291      *     value = the value to archive
292      *     baseType = the base type of the enum
293      *     key = the key associated with the value
294      *     id = the id associated with the value
295      */
296     void archiveEnum (bool value, string baseType, string key, Id id);
297 
298     /// Ditto
299     void archiveEnum (bool value, string baseType, string key, Id id);
300 
301     /// Ditto
302     void archiveEnum (byte value, string baseType, string key, Id id);
303 
304     /// Ditto
305     void archiveEnum (char value, string baseType, string key, Id id);
306 
307     /// Ditto
308     void archiveEnum (dchar value, string baseType, string key, Id id);
309 
310     /// Ditto
311     void archiveEnum (int value, string baseType, string key, Id id);
312 
313     /// Ditto
314     void archiveEnum (long value, string baseType, string key, Id id);
315 
316     /// Ditto
317     void archiveEnum (short value, string baseType, string key, Id id);
318 
319     /// Ditto
320     void archiveEnum (ubyte value, string baseType, string key, Id id);
321 
322     /// Ditto
323     void archiveEnum (uint value, string baseType, string key, Id id);
324 
325     /// Ditto
326     void archiveEnum (ulong value, string baseType, string key, Id id);
327 
328     /// Ditto
329     void archiveEnum (ushort value, string baseType, string key, Id id);
330 
331     /// Ditto
332     void archiveEnum (wchar value, string baseType, string key, Id id);
333 
334     /**
335      * Archives a base class.
336      *
337      * This method is used to indicate that the all following calls to archive a value
338      * should be part of the base class. This method is usually called within the
339      * callback passed to archiveObject. The archiveObject method can the mark the end
340      * of the class.
341      *
342      * Examples:
343      * ---
344      * class ArchiveBase {}
345      * class Foo : ArchiveBase {}
346      *
347      * auto archive = new XmlArchive!();
348      * archive.archiveBaseClass("ArchiveBase", "base", 0);
349      * ---
350      *
351      * Params:
352      *     type = the type of the base class to archive
353      *     key = the key associated with the base class
354      *     id = the id associated with the base class
355      */
356     void archiveBaseClass (string type, string key, Id id);
357 
358     /**
359      * Archives a null pointer or reference.
360      *
361      * Examples:
362      * ---
363      * int* ptr;
364      *
365      * auto archive = new XmlArchive!();
366      * archive.archiveNull(typeof(ptr).stringof, "ptr");
367      * ---
368      *
369      * Params:
370      *     type = the runtime type of the pointer or reference to archive
371      *     key = the key associated with the null pointer
372      */
373     void archiveNull (string type, string key);
374 
375     /**
376      * Archives an object, either a class or an interface.
377      *
378      * Examples:
379      * ---
380      * class Foo
381      * {
382      *     int a;
383      * }
384      *
385      * auto foo = new Foo;
386      *
387      * auto archive = new XmlArchive!();
388      * archive.archiveObject(Foo.classinfo.name, "Foo", "foo", 0, {
389      *     // archive the fields of Foo
390      * });
391      * ---
392      *
393      * Params:
394      *     runtimeType = the runtime type of the object
395      *     type = the static type of the object
396      *     key = the key associated with the object
397      *     id = the id associated with the object
398      *     dg = a callback that performs the archiving of the individual fields
399      */
400     void archiveObject (string runtimeType, string type, string key, Id id, void delegate () dg);
401 
402     /**
403      * Archives a pointer.
404      *
405      * If a pointer points to a value that is serialized as well, the pointer should be
406      * archived as a reference. Otherwise the value that the pointer points to should be
407      * serialized as a regular value.
408      *
409      * Examples:
410      * ---
411      * class Foo
412      * {
413      *     int a;
414      *     int* b;
415      * }
416      *
417      * auto foo = new Foo;
418      * foo.a = 3;
419      * foo.b = &foo.a;
420      *
421      * archive = new XmlArchive!();
422      * archive.archivePointer("b", 0, {
423      *     // archive "foo.b" as a reference
424      * });
425      * ---
426      *
427      * ---
428      * int a = 3;
429      *
430      * class Foo
431      * {
432      *     int* b;
433      * }
434      *
435      * auto foo = new Foo;
436      * foo.b = &a;
437      *
438      * archive = new XmlArchive!();
439      * archive.archivePointer("b", 0, {
440      *     // archive "foo.b" as a regular value
441      * });
442      * ---
443      *
444      * Params:
445      *     key = the key associated with the pointer
446      *     id = the id associated with the pointer
447      *     dg = a callback that performs the archiving of value pointed to by the pointer
448      */
449     void archivePointer (string key, Id id, void delegate () dg);
450 
451     /**
452      * Archives a reference.
453      *
454      * A reference is reference to another value. For example, if an object is archived
455      * more than once, the first time it's archived it will actual archive the object.
456      * The second time the object will be archived a reference will be archived instead
457      * of the actual object.
458      *
459      * This method is also used when archiving a pointer that points to a value that has
460      * been or will be archived as well.
461      *
462      * Examples:
463      * ---
464      * class Foo {}
465      *
466      * class Bar
467      * {
468      *     Foo f;
469      *     Foo f2;
470      * }
471      *
472      * auto bar = new Bar;
473      * bar.f = new Foo;
474      * bar.f2 = bar.f;
475      *
476      * auto archive = new XmlArchive!();
477      *
478      * // when achiving "bar"
479      * archive.archiveObject(Foo.classinfo.name, "Foo", "f", 0, {});
480      * archive.archiveReference("f2", 0); // archive a reference to "f"
481      * ---
482      *
483      * Params:
484      *     key = the key associated with the reference
485      *     id = the id of the value this reference refers to
486      */
487     void archiveReference (string key, Id id);
488 
489     /**
490      * Archives a slice.
491      *
492      * This method should be used when archiving an array that is a slice of an
493      * already archived array or an array that has not yet been archived.
494      *
495      * Examples:
496      * ---
497      * auto arr = [1, 2, 3, 4];
498      * auto slice = arr[1 .. 3];
499      *
500      * auto archive = new XmlArchive!();
501      * // archive "arr" with id 0
502      *
503      * auto s = Slice(slice.length, 1);
504      * archive.archiveSlice(s, 1, 0);
505      * ---
506      *
507      * Params:
508      *     slice = the slice to be archived
509      *     sliceId = the id associated with the slice
510      *     arrayId = the id associated with the array this slice is a slice of
511      */
512     void archiveSlice (Slice slice, Id sliceId, Id arrayId);
513 
514     /**
515      * Archives a struct.
516      *
517      * Examples:
518      * ---
519      * struct Foo
520      * {
521      *     int a;
522      * }
523      *
524      * auto foo = Foo(3);
525      *
526      * auto archive = new XmlArchive!();
527      * archive.archiveStruct(Foo.stringof, "foo", 0, {
528      *     // archive the fields of Foo
529      * });
530      * ---
531      *
532      * Params:
533      *     type = the type of the struct
534      *     key = the key associated with the struct
535      *     id = the id associated with the struct
536      *     dg = a callback that performs the archiving of the individual fields
537      */
538     void archiveStruct (string type, string key, Id id, void delegate () dg);
539 
540     /**
541      * Archives the given value.
542      *
543      * Params:
544      *     value = the value to archive
545      *     key = the key associated with the value
546      *     id = the id associated wit the value
547      */
548     void archive (string value, string key, Id id);
549 
550     /// Ditto
551     void archive (wstring value, string key, Id id);
552 
553     /// Ditto
554     void archive (dstring value, string key, Id id);
555 
556     ///    Ditto
557     void archive (bool value, string key, Id id);
558 
559     /// Ditto
560     void archive (byte value, string key, Id id);
561 
562 
563     //void archive (cdouble value, string key, Id id); // currently not supported by to!()
564 
565 
566     //void archive (cent value, string key, Id id);
567 
568     //void archive (cfloat value, string key, Id id); // currently not supported by to!()
569 
570     /// Ditto
571     void archive (char value, string key, Id id);
572 
573     //void archive (creal value, string key, Id id); // currently not supported by to!()
574 
575     /// Ditto
576     void archive (dchar value, string key, Id id);
577 
578     /// Ditto
579     void archive (double value, string key, Id id);
580 
581     /// Ditto
582     void archive (float value, string key, Id id);
583 
584 
585     //void archive (idouble value, string key, Id id); // currently not supported by to!()
586 
587     //void archive (ifloat value, string key, Id id); // currently not supported by to!()
588 
589     /// Ditto
590     void archive (int value, string key, Id id);
591 
592 
593     //void archive (ireal value, string key, Id id); // currently not supported by to!()
594 
595     /// Ditto
596     void archive (long value, string key, Id id);
597 
598     /// Ditto
599     void archive (real value, string key, Id id);
600 
601     /// Ditto
602     void archive (short value, string key, Id id);
603 
604     /// Ditto
605     void archive (ubyte value, string key, Id id);
606 
607     //void archive (ucent value, string key, Id id); // currently not implemented but a reserved keyword
608 
609     /// Ditto
610     void archive (uint value, string key, Id id);
611 
612     /// Ditto
613     void archive (ulong value, string key, Id id);
614 
615     /// Ditto
616     void archive (ushort value, string key, Id id);
617 
618     /// Ditto
619     void archive (wchar value, string key, Id id);
620 
621     /**
622      * Unarchives the value associated with the given key as an array.
623      *
624      * Examples:
625      * ---
626      * auto archive = new XmlArchive!();
627      * archive.beginUnarchiving(data);
628      * auto id = archive.unarchiveArray("arr", (size_t length) {
629      *     auto arr = new int[length]; // pre-allocate the array
630      *     // unarchive the individual elements of "arr"
631      * });
632      * ---
633      *
634      * Params:
635      *     key = the key associated with the array
636      *     dg = a callback that performs the unarchiving of the individual elements.
637      *             $(I length) is the length of the archived array
638      *
639      * Returns: the id associated with the array
640      *
641      * See_Also: unarchiveArray
642      */
643     Id unarchiveArray (string key, void delegate (size_t length) dg);
644 
645     /**
646      * Unarchives the value associated with the given id as an array.
647      *
648      * Examples:
649      * ---
650      * auto archive = new XmlArchive!();
651      * archive.beginUnarchiving(data);
652      * archive.unarchiveArray(0, (size_t length) {
653      *     auto arr = new int[length]; // pre-allocate the array
654      *     // unarchive the individual elements of "arr"
655      * });
656      * ---
657      *
658      * Params:
659      *     id = the id associated with the value
660      *     dg = a callback that performs the unarchiving of the individual elements.
661      *             $(I length) is the length of the archived array
662      *
663      * See_Also: unarchiveArray
664      */
665     void unarchiveArray (Id id, void delegate (size_t length) dg);
666 
667     /**
668      * Unarchives the value associated with the given id as an associative array.
669      *
670      * Examples:
671      * ---
672      * auto archive = new XmlArchive!();
673      * archive.beginUnarchiving(data);
674      *
675      * auto id = archive.unarchiveAssociativeArray("aa", (size_t length) {
676      *     // unarchive the individual keys and values
677      * });
678      * ---
679      *
680      * Params:
681      *     key = the key associated with the associative array
682      *     dg = a callback that performs the unarchiving of the individual keys and values.
683      *             $(I length) is the length of the archived associative array
684      *
685      * Returns: the id associated with the associative array
686      *
687      * See_Also: unarchiveAssociativeArrayKey
688      * See_Also: unarchiveAssociativeArrayValue
689      */
690     Id unarchiveAssociativeArray (string key, void delegate (size_t length) dg);
691 
692     /**
693      * Unarchives an associative array key.
694      *
695      * There are separate methods for unarchiving associative array keys and values
696      * because both the key and the value can be of arbitrary type and needs to be
697      * unarchived on its own.
698      *
699      * Examples:
700      * ---
701      * auto archive = new XmlArchive!();
702      * archive.beginUnarchiving(data);
703      *
704      * for (size_t i = 0; i < length; i++)
705      * {
706      *     unarchiveAssociativeArrayKey(to!(string(i), {
707      *         // unarchive the key
708      *     });
709      * }
710      * ---
711      *
712      * The for statement in the above example would most likely be executed in the
713      * callback passed to the unarchiveAssociativeArray method.
714      *
715      * Params:
716      *     key = the key associated with the key
717      *     dg = a callback that performs the actual unarchiving of the key
718      *
719      * See_Also: unarchiveAssociativeArrayValue
720      * See_Also: unarchiveAssociativeArray
721      */
722     void unarchiveAssociativeArrayKey (string key, void delegate () dg);
723 
724     /**
725      * Unarchives an associative array value.
726      *
727      * There are separate methods for unarchiving associative array keys and values
728      * because both the key and the value can be of arbitrary type and needs to be
729      * unarchived on its own.
730      *
731      * Examples:
732      * ---
733      * auto archive = new XmlArchive!();
734      * archive.beginUnarchiving(data);
735      *
736      * for (size_t i = 0; i < length; i++)
737      * {
738      *     unarchiveAssociativeArrayValue(to!(string(i), {
739      *         // unarchive the value
740      *     });
741      * }
742      * ---
743      *
744      * The for statement in the above example would most likely be executed in the
745      * callback passed to the unarchiveAssociativeArray method.
746      *
747      * Params:
748      *     key = the key associated with the value
749      *     dg = a callback that performs the actual unarchiving of the value
750      *
751      * See_Also: unarchiveAssociativeArrayKey
752      * See_Also: unarchiveAssociativeArray
753      */
754     void unarchiveAssociativeArrayValue (string key, void delegate () dg);
755 
756     /**
757      * Unarchives the value associated with the given key as a bool.
758      *
759      * This method is used when the unarchiving a enum value with the base type bool.
760      *
761      * Params:
762      *     key = the key associated with the value
763      *
764      * Returns: the unarchived value
765      */
766     bool unarchiveEnumBool (string key, out Id id);
767 
768     /// Ditto
769     byte unarchiveEnumByte (string key, out Id id);
770 
771     /// Ditto
772     char unarchiveEnumChar (string key, out Id id);
773 
774     /// Ditto
775     dchar unarchiveEnumDchar (string key, out Id id);
776 
777     /// Ditto
778     int unarchiveEnumInt (string key, out Id id);
779 
780     /// Ditto
781     long unarchiveEnumLong (string key, out Id id);
782 
783     /// Ditto
784     short unarchiveEnumShort (string key, out Id id);
785 
786     /// Ditto
787     ubyte unarchiveEnumUbyte (string key, out Id id);
788 
789     /// Ditto
790     uint unarchiveEnumUint (string key, out Id id);
791 
792     /// Ditto
793     ulong unarchiveEnumUlong (string key, out Id id);
794 
795     /// Ditto
796     ushort unarchiveEnumUshort (string key, out Id id);
797 
798     /// Ditto
799     wchar unarchiveEnumWchar (string key, out Id id);
800 
801     /**
802      * Unarchives the value associated with the given id as a bool.
803      *
804      * This method is used when the unarchiving a enum value with the base type bool.
805      *
806      * Params:
807      *     id = the id associated with the value
808      *
809      * Returns: the unarchived value
810      */
811     bool unarchiveEnumBool (Id id);
812 
813     /// Ditto
814     byte unarchiveEnumByte (Id id);
815 
816     /// Ditto
817     char unarchiveEnumChar (Id id);
818 
819     /// Ditto
820     dchar unarchiveEnumDchar (Id id);
821 
822     /// Ditto
823     int unarchiveEnumInt (Id id);
824 
825     /// Ditto
826     long unarchiveEnumLong (Id id);
827 
828     /// Ditto
829     short unarchiveEnumShort (Id id);
830 
831     /// Ditto
832     ubyte unarchiveEnumUbyte (Id id);
833 
834     /// Ditto
835     uint unarchiveEnumUint (Id id);
836 
837     /// Ditto
838     ulong unarchiveEnumUlong (Id id);
839 
840     /// Ditto
841     ushort unarchiveEnumUshort (Id id);
842 
843     /// Ditto
844     wchar unarchiveEnumWchar (Id id);
845 
846     /**
847      * Unarchives the base class associated with the given key.
848      *
849      * This method is used to indicate that the all following calls to unarchive a
850      * value should be part of the base class. This method is usually called within the
851      * callback passed to unarchiveObject. The unarchiveObject method can the mark the
852      * end of the class.
853      *
854      * Examples:
855      * ---
856      * auto archive = new XmlArchive!();
857      * archive.beginUnarchiving(data);
858      * archive.unarchiveBaseClass("base");
859      * ---
860      *
861      * Params:
862      *     key = the key associated with the base class.
863      *
864      * See_Also: unarchiveObject
865      */
866     void unarchiveBaseClass (string key);
867 
868     /**
869      * Unarchives the object associated with the given key.
870      *
871      * Examples:
872      * ---
873      * class Foo
874      * {
875      *     int a;
876      * }
877      *
878      * auto archive = new XmlArchive!();
879      * archive.beginUnarchiving(data);
880      *
881      * Id id;
882      * Object o;
883      *
884      * archive.unarchiveObject("foo", id, o, {
885      *     // unarchive the fields of Foo
886      * });
887      *
888      * auto foo = cast(Foo) o;
889      * ---
890      *
891      * Params:
892      *     key = the key associated with the object
893      *     id = the id associated with the object
894      *     result = the unarchived object
895      *     dg = a callback the performs the unarchiving of the individual fields
896      */
897     void unarchiveObject (string key, out Id id, out Object result, void delegate () dg);
898 
899     /**
900      * Unarchives the pointer associated with the given key.
901      *
902      * Examples:
903      * ---
904      * auto archive = new XmlArchive!();
905      * archive.beginUnarchiving(data);
906      * auto id = unarchivePointer("ptr", {
907      *     // unarchive the value pointed to by the pointer
908      * });
909      * ---
910      *
911      * Params:
912      *     key = the key associated with the pointer
913      *     dg = a callback that performs the unarchiving of value pointed to by the pointer
914      *
915      * Returns: the id associated with the pointer
916      */
917     Id unarchivePointer (string key, void delegate () dg);
918 
919     /**
920      * Unarchives the reference associated with the given key.
921      *
922      * A reference is reference to another value. For example, if an object is archived
923      * more than once, the first time it's archived it will actual archive the object.
924      * The second time the object will be archived a reference will be archived instead
925      * of the actual object.
926      *
927      * This method is also used when unarchiving a pointer that points to a value that has
928      * been or will be unarchived as well.
929      *
930      * Examples:
931      * ---
932      * auto archive = new XmlArchive!();
933      * archive.beginUnarchiving(data);
934      * auto id = unarchiveReference("foo");
935      *
936      * // unarchive the value with the associated id
937      * ---
938      *
939      * Params:
940      *     key = the key associated with the reference
941      *
942      * Returns: the id the reference refers to
943      */
944     Id unarchiveReference (string key);
945 
946     /**
947      * Unarchives the slice associated with the given key.
948      *
949      * This method should be used when unarchiving an array that is a slice of an
950      * already unarchived array or an array that has not yet been unarchived.
951      *
952      * Examples:
953      * ---
954      * auto archive = new XmlArchive!();
955      * archive.beginUnarchiving(data);
956      * auto slice = unarchiveSlice("slice");
957      *
958      * // slice the original array with the help of the unarchived slice
959      * ---
960      *
961      * Params:
962      *     key = the key associated with the slice
963      *
964      * Returns: the unarchived slice
965      */
966     Slice unarchiveSlice (string key);
967 
968     /**
969      * Unarchives the struct associated with the given key.
970      *
971      * Examples:
972      * ---
973      * struct Foo
974      * {
975      *     int a;
976      * }
977      *
978      * auto archive = new XmlArchive!();
979      * archive.beginUnarchiving(data);
980      * archive.unarchiveStruct("foo", {
981      *     // unarchive the fields of Foo
982      * });
983      * ---
984      *
985      * Params:
986      *     key = the key associated with the string
987      *     dg = a callback that performs the unarchiving of the individual fields
988      */
989     Id unarchiveStruct (string key, void delegate () dg);
990 
991     /**
992      * Unarchives the struct associated with the given id.
993      *
994      * Examples:
995      * ---
996      * struct Foo
997      * {
998      *     int a;
999      * }
1000      *
1001      * auto archive = new XmlArchive!();
1002      * archive.beginUnarchiving(data);
1003      * archive.unarchiveStruct(0, {
1004      *     // unarchive the fields of Foo
1005      * });
1006      * ---
1007      *
1008      * Params:
1009      *     id = the id associated with the struct
1010      *     dg = a callback that performs the unarchiving of the individual fields.
1011      *                The callback will receive the key the struct was archived with.
1012      */
1013     void unarchiveStruct (Id id, void delegate () dg);
1014 
1015     /**
1016      * Unarchives the string associated with the given id.
1017      *
1018      * Examples:
1019      * ---
1020      * auto archive = new XmlArchive!();
1021      * archive.beginUnarchiving(data);
1022      * auto str = archive.unarchiveString(0);
1023      * ---
1024      *
1025      * Params:
1026      *     id = the id associated with the string
1027      *
1028      * Returns: the unarchived string
1029      */
1030     string unarchiveString (Id id);
1031 
1032     /// Ditto
1033     wstring unarchiveWstring (Id id);
1034 
1035     /// Ditto
1036     dstring unarchiveDstring (Id id);
1037 
1038     /**
1039      * Unarchives the string associated with the given key.
1040      *
1041      * Examples:
1042      * ---
1043      * auto archive = new XmlArchive!();
1044      * archive.beginUnarchiving(data);
1045      *
1046      * Id id;
1047      * auto str = archive.unarchiveString("str", id);
1048      * ---
1049      *
1050      * Params:
1051      *     id = the id associated with the string
1052      *
1053      * Returns: the unarchived string
1054      */
1055     string unarchiveString (string key, out Id id);
1056 
1057     /// Ditto
1058     wstring unarchiveWstring (string key, out Id id);
1059 
1060     /// Ditto
1061     dstring unarchiveDstring (string key, out Id id);
1062 
1063     /**
1064      * Unarchives the value associated with the given key.
1065      *
1066      * Examples:
1067      * ---
1068      * auto archive = new XmlArchive!();
1069      * archive.beginUnarchiving(data);
1070      * auto foo = unarchiveBool("foo");
1071      * ---
1072      * Params:
1073      *     key = the key associated with the value
1074      *
1075      * Returns: the unarchived value
1076      */
1077     bool unarchiveBool (string key, out Id id);
1078 
1079     /// Ditto
1080     byte unarchiveByte (string key, out Id id);
1081 
1082     //cdouble unarchiveCdouble (string key, out Id id); // currently not supported by to!()
1083     //cent unarchiveCent (string key, out Id id); // currently not implemented but a reserved keyword
1084     //cfloat unarchiveCfloat (string key, out Id id); // currently not supported by to!()
1085 
1086     /// Ditto
1087     char unarchiveChar (string key, out Id id); // currently not implemented but a reserved keyword
1088     //creal unarchiveCreal (string key, out Id id); // currently not supported by to!()
1089 
1090     /// Ditto
1091     dchar unarchiveDchar (string key, out Id id);
1092 
1093     /// Ditto
1094     double unarchiveDouble (string key, out Id id);
1095 
1096     /// Ditto
1097     float unarchiveFloat (string key, out Id id);
1098     //idouble unarchiveIdouble (string key, out Id id); // currently not supported by to!()
1099     //ifloat unarchiveIfloat (string key, out Id id); // currently not supported by to!()*/
1100 
1101     /// Ditto
1102     int unarchiveInt (string key, out Id id);
1103 
1104     //ireal unarchiveIreal (string key, out Id id); // currently not supported by to!()
1105 
1106     /// Ditto
1107     long unarchiveLong (string key, out Id id);
1108 
1109     /// Ditto
1110     real unarchiveReal (string key, out Id id);
1111 
1112     /// Ditto
1113     short unarchiveShort (string key, out Id id);
1114 
1115     /// Ditto
1116     ubyte unarchiveUbyte (string key, out Id id);
1117 
1118     ///
1119     //ucent unarchiveCcent (string key, out Id id); // currently not implemented but a reserved keyword
1120 
1121     /// Ditto
1122     uint unarchiveUint (string key, out Id id);
1123 
1124     /// Ditto
1125     ulong unarchiveUlong (string key, out Id id);
1126 
1127     /// Ditto
1128     ushort unarchiveUshort (string key, out Id id);
1129 
1130     /// Ditto
1131     wchar unarchiveWchar (string key, out Id id);
1132 
1133     /**
1134      * Unarchives the value associated with the given id.
1135      *
1136      * Examples:
1137      * ---
1138      * auto archive = new XmlArchive!();
1139      * archive.beginUnarchiving(data);
1140      * auto foo = unarchiveBool(0);
1141      * ---
1142      * Params:
1143      *     id = the id associated with the value
1144      *
1145      * Returns: the unarchived value
1146      */
1147     bool unarchiveBool (Id id);
1148 
1149     /// Ditto
1150     byte unarchiveByte (Id id);
1151 
1152     //cdouble unarchiveCdouble (Id id); // currently not supported by to!()
1153     //cent unarchiveCent (Id id); // currently not implemented but a reserved keyword
1154     //cfloat unarchiveCfloat (Id id); // currently not supported by to!()
1155 
1156     /// Ditto
1157     char unarchiveChar (Id id); // currently not implemented but a reserved keyword
1158     //creal unarchiveCreal (Id id); // currently not supported by to!()
1159 
1160     /// Ditto
1161     dchar unarchiveDchar (Id id);
1162 
1163     /// Ditto
1164     double unarchiveDouble (Id id);
1165 
1166     /// Ditto
1167     float unarchiveFloat (Id id);
1168     //idouble unarchiveIdouble (Id id); // currently not supported by to!()
1169     //ifloat unarchiveIfloat (Id id); // currently not supported by to!()*/
1170 
1171     /// Ditto
1172     int unarchiveInt (Id id);
1173 
1174     //ireal unarchiveIreal (Id id); // currently not supported by to!()
1175 
1176     /// Ditto
1177     long unarchiveLong (Id id);
1178 
1179     /// Ditto
1180     real unarchiveReal (Id id);
1181 
1182     /// Ditto
1183     short unarchiveShort (Id id);
1184 
1185     /// Ditto
1186     ubyte unarchiveUbyte (Id id);
1187 
1188     ///
1189     //ucent unarchiveCcent (Id id); // currently not implemented but a reserved keyword
1190 
1191     /// Ditto
1192     uint unarchiveUint (Id id);
1193 
1194     /// Ditto
1195     ulong unarchiveUlong (Id id);
1196 
1197     /// Ditto
1198     ushort unarchiveUshort (Id id);
1199 
1200     /// Ditto
1201     wchar unarchiveWchar (Id id);
1202 
1203     /**
1204      * Performs post processing of the array associated with the given id.
1205      *
1206      * Post processing can basically be anything that the archive wants to do. This
1207      * method is called by the serializer once for each serialized array at the end of
1208      * the serialization process when all values have been serialized.
1209      *
1210      * With this method the archive has a last chance of changing an archived array to
1211      * an archived slice instead.
1212      *
1213      * Params:
1214      *     id = the id associated with the array
1215      */
1216     void postProcessArray (Id id);
1217 }
1218 
1219 /**
1220  * This class serves as an optional base class for archive implementations. It
1221  * contains some utility methods that can be helpful when creating a new archive
1222  * implementation.
1223  *
1224  * Most of the examples below are assumed to be in a sub class to this class and
1225  * with $(I string) as the data type.
1226  */
1227 abstract class ArchiveBase (U) : Archive
1228 {
1229     /// The typed used to represent the archived data in a typed form.
1230     alias immutable(U)[] Data;
1231 
1232     private ErrorCallback errorCallback_;
1233 
1234     /**
1235      * This callback will be called when an unexpected event occurs, i.e. an expected element
1236      * is missing in the unarchiving process.
1237      *
1238      * Examples:
1239      * ---
1240      * auto archive = new XmlArchive!();
1241      * serializer.errorCallback = (SerializationException exception) {
1242      *     println(exception);
1243      *     throw exception;
1244      * };
1245      * ---
1246      */
1247     ErrorCallback errorCallback ()
1248     {
1249         return errorCallback_;
1250     }
1251 
1252     /**
1253      * This callback will be called when an unexpected event occurs, i.e. an expected element
1254      * is missing in the unarchiving process.
1255      *
1256      * Examples:
1257      * ---
1258      * auto archive = new XmlArchive!();
1259      * serializer.errorCallback = (SerializationException exception) {
1260      *     println(exception);
1261      *     throw exception;
1262      * };
1263      * ---
1264      */
1265     ErrorCallback errorCallback (ErrorCallback errorCallback)
1266     {
1267         return errorCallback_ = errorCallback;
1268     }
1269 
1270     /**
1271      * Creates a new instance of this class with an error callback
1272      *
1273      * Params:
1274      *     errorCallback = the error callback used for ths instance
1275      */
1276     protected this (ErrorCallback errorCallback)
1277     {
1278         this.errorCallback = errorCallback;
1279     }
1280 
1281     /**
1282      * Converts the given value into the type used for archiving.
1283      *
1284      * Examples:
1285      * ---
1286      * auto i = toData(3);
1287      * assert(i == "3");
1288      * ---
1289      *
1290      * Params:
1291      *     value = the value to convert
1292      *
1293      * Returns: the converted value
1294      *
1295      * Throws: SerializationException if the conversion failed
1296      * See_Also: fromData
1297      * See_Also: floatingPointToData
1298      */
1299     protected Data toData (T) (T value)
1300     {
1301         try
1302         {
1303             static if (isFloatingPoint!(T))
1304                 return floatingPointToData(value);
1305 
1306             else
1307                 return to!(Data)(value);
1308         }
1309 
1310         catch (ConvException e)
1311         {
1312             error(e);
1313             return Data.init;
1314         }
1315     }
1316 
1317     /**
1318      * Converts the given value from the type used for archiving to $(I T).
1319      *
1320      * Examples:
1321      * ---
1322      * auto i = fromData!(int)("3");
1323      * assert(i == 3);
1324      * ---
1325      *
1326      * Params:
1327      *        T = the type to convert the given value to
1328      *     value = the value to convert
1329      *
1330      * Returns: the converted value
1331      *
1332      * Throws: SerializationException if the conversion failed
1333      * See_Also: toData
1334      */
1335     protected T fromData (T) (Data value)
1336     {
1337         try
1338         {
1339             static if (is(T == wchar))
1340                 return toWchar(value);
1341 
1342             else
1343                 return to!(T)(value);
1344         }
1345 
1346         catch (ConvException e)
1347         {
1348             error(e);
1349             return T.init;
1350         }
1351 
1352     }
1353 
1354     /**
1355      * The archive is responsible for archiving primitive types in the format chosen by
1356      * Converts the given floating point value to the type used for archiving.
1357      *
1358      * This method is used to convert floating point values, it will convert the
1359      * floating point value to hexadecimal format.
1360      *
1361      * Examples:
1362      * ---
1363      * auto f = floatingPointToData(3.15f);
1364      * assert(f == "0xc.9999ap-2");
1365      * ---
1366      *
1367      * Params:
1368      *     value = the value to convert
1369      *
1370      * Returns: the conveted value
1371      *
1372      * Throws: SerializationException if the conversion failed
1373      */
1374     protected Data floatingPointToData (T) (T value)
1375     {
1376         static assert(isFloatingPoint!(T), format!(`The given value of the type "`, T,
1377             `" is not a valid type, the only valid types for this method are floating point types.`));
1378 
1379         return to!(Data)(std..string.format("%a", value));
1380     }
1381 
1382     /**
1383      * Converts the id value to the type $(I Id).
1384      *
1385      * This method is used to conver ids stored in the serialized data to the correct
1386      * type.
1387      *
1388      * Params:
1389      *     value = the value to convert
1390      *
1391      * Returns: the converted id
1392      *
1393      * Throws: SerializationException if the converted failed
1394      * See_Also: fromData
1395      */
1396     protected Id toId (Data value)
1397     {
1398         return fromData!(Id)(value);
1399     }
1400 
1401     /**
1402      * Calls the errorCallback with an exception.
1403      *
1404      * Call this method when some type of error occurred, like a field cannot be found.
1405      *
1406      * Params:
1407      *     message = the message for the exception
1408      *     file = the file where the error occurred
1409      *     line = the line where the error occurred
1410      */
1411     protected void error (string message, string[] data = null, string file = __FILE__, size_t line = __LINE__)
1412     {
1413         if (errorCallback)
1414             errorCallback()(new SerializationException(message, file, line));
1415     }
1416 
1417     /**
1418      * Calls the errorCallback with an exception.
1419      *
1420      * Call this method when some type of error occurred, like a field cannot be found.
1421      *
1422      * Params:
1423      *     exception = the exception to pass to the errorCallback
1424      */
1425     protected void error (Exception exception)
1426     {
1427         if (errorCallback)
1428             errorCallback()(new SerializationException(exception));
1429     }
1430 
1431     private wchar toWchar (Data value)
1432     {
1433         auto c = value.front;
1434 
1435         if (codeLength!(wchar)(c) > 2)
1436             throw new ConvException("Could not convert `" ~
1437                 to!(string)(value) ~ "` of type " ~
1438                 Data.stringof ~ " to type wchar.");
1439 
1440         return cast(wchar) c;
1441     }
1442 }