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