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