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