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