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 }