├── .travis.yml ├── .gitmodules ├── wiki └── orange_glass_ball_small.png ├── dsss.conf ├── orange.orbspec ├── .gitignore ├── docs.sh ├── unittest.sh ├── orange ├── xml │ ├── _.d │ └── XmlDocument.d ├── util │ ├── collection │ │ ├── _.d │ │ └── Array.d │ ├── _.d │ ├── CTFE.d │ ├── Use.d │ ├── Reflection.d │ └── Traits.d ├── serialization │ ├── archives │ │ ├── _.d │ │ └── Archive.d │ ├── _.d │ ├── SerializationException.d │ ├── Events.d │ ├── Serializable.d │ └── RegisterWrapper.d ├── core │ └── Attribute.d └── test │ └── UnitTester.d ├── dub.sdl ├── tests ├── unittest.d ├── Event.d ├── Struct.d ├── NonCopyableStruct.d ├── Object.d ├── Enum.d ├── EnumConstantMember.d ├── Subclass.d ├── Util.d ├── Interface.d ├── Custom.d ├── CircularReference.d ├── Array.d ├── CustomWithString.d ├── BaseClass.d ├── ArrayOfObject.d ├── NonSerialized.d ├── NonIntrusiveStruct.d ├── NonIntrusive.d ├── Events.d ├── OverrideSerializer.d ├── NonMutable.d ├── Pointer.d ├── AssociativeArray.d ├── AssociativeArrayReference.d ├── String.d ├── Primitive.d └── Slice.d ├── modules.ddoc ├── README.markdown ├── Makefile └── Makefile.win /.travis.yml: -------------------------------------------------------------------------------- 1 | language: d 2 | d: 3 | - dmd 4 | 5 | os: 6 | - linux 7 | - osx 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "candydoc"] 2 | path = candydoc 3 | url = git://github.com/jacob-carlborg/candydoc.git 4 | -------------------------------------------------------------------------------- /wiki/orange_glass_ball_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-carlborg/orange/HEAD/wiki/orange_glass_ball_small.png -------------------------------------------------------------------------------- /dsss.conf: -------------------------------------------------------------------------------- 1 | [orange] 2 | target = orange 3 | 4 | version (D_Ddoc) { 5 | exclude += orange/test/UnitTester.d 6 | exclude += tests/*.d 7 | } -------------------------------------------------------------------------------- /orange.orbspec: -------------------------------------------------------------------------------- 1 | name "orange" 2 | summary "Orange is a serialization library." 3 | version "1.0.0" 4 | files Dir["**"/"*.{d,conf,png,markdown,sh}"] << "Makefile" 5 | libraries %w[orange] 6 | build "dsss" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.o 3 | dsss_objs 4 | dsss_docs 5 | dsss_imports 6 | dsss.last 7 | *.orig 8 | *.a 9 | *.rf 10 | *.dylib 11 | main.d 12 | main 13 | *.deps 14 | *.framework 15 | kandil 16 | lib 17 | import 18 | unittest 19 | *.exe 20 | *.orb 21 | docs 22 | .dub 23 | docs.json 24 | __dummy.html 25 | -------------------------------------------------------------------------------- /docs.sh: -------------------------------------------------------------------------------- 1 | mkdir -p docs 2 | 3 | dmd -c -Dddocs -D candydoc/candy.ddoc modules.ddoc \ 4 | orange/core/*.d \ 5 | orange/serialization/*.d \ 6 | orange/serialization/archives/*.d \ 7 | orange/test/*.d \ 8 | orange/util/*.d \ 9 | orange/util/collection/*.d \ 10 | orange/xml/*.d 11 | 12 | ln -s -f ../candydoc docs/candydoc -------------------------------------------------------------------------------- /unittest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dmd -g -unittest -ofunittest \ 4 | orange/core/*.d \ 5 | orange/serialization/*.d \ 6 | orange/serialization/archives/*.d \ 7 | orange/test/*.d \ 8 | orange/util/*.d \ 9 | orange/util/collection/*.d \ 10 | orange/xml/*.d \ 11 | tests/*.d 12 | 13 | if [ "$?" = 0 ] ; then 14 | ./unittest 15 | fi 16 | -------------------------------------------------------------------------------- /orange/xml/_.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Jul 23, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.xml._; 8 | 9 | public: 10 | 11 | import orange.xml.PhobosXml; 12 | import orange.xml.XmlDocument; -------------------------------------------------------------------------------- /orange/util/collection/_.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Nov 21, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.util.collection._; 8 | 9 | public: 10 | 11 | import orange.util.collection.Array; -------------------------------------------------------------------------------- /orange/util/_.d: -------------------------------------------------------------------------------- 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.util._; 8 | 9 | public: 10 | 11 | import orange.util.CTFE; 12 | import orange.util.Reflection; 13 | import orange.util.Traits; 14 | import orange.util.Use; -------------------------------------------------------------------------------- /orange/serialization/archives/_.d: -------------------------------------------------------------------------------- 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._; 8 | 9 | public: 10 | 11 | import orange.serialization.archives.Archive; 12 | import orange.serialization.archives.XmlArchive; -------------------------------------------------------------------------------- /orange/util/collection/Array.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2008-2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: 2008 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | * 7 | */ 8 | module orange.util.collection.Array; 9 | 10 | inout(T)[] assumeUnique (T) (ref T[] source, ref inout(T)[] destination) 11 | { 12 | destination = cast(inout(T)[]) source; 13 | source = null; 14 | 15 | return destination; 16 | } -------------------------------------------------------------------------------- /orange/serialization/_.d: -------------------------------------------------------------------------------- 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._; 8 | 9 | public: 10 | 11 | import orange.serialization.Events; 12 | import orange.serialization.RegisterWrapper; 13 | import orange.serialization.Serializable; 14 | import orange.serialization.SerializationException; 15 | import orange.serialization.Serializer; -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "orange" 2 | description "Orange is a serialization library for the D programming language" 3 | authors "Jacob Carlborg" 4 | copyright "Copyright © 2016, Jacob Carlborg" 5 | license "BSL-1.0" 6 | homepage "https://github.com/jacob-carlborg/orange" 7 | 8 | targetType "library" 9 | sourcePaths "orange" 10 | importPaths "." 11 | excludedSourceFiles "orange/test/**/*" 12 | 13 | configuration "unittest" { 14 | sourcePaths "tests" 15 | sourceFiles "orange/test/UnitTester.d" 16 | targetType "executable" 17 | targetName "unittest" 18 | } 19 | -------------------------------------------------------------------------------- /tests/unittest.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Nov 5, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.all; 8 | 9 | import core.stdc.stdlib; 10 | 11 | import orange.test.UnitTester; 12 | 13 | /* 14 | * The tests that test for XML with attributes are not completely 15 | * reliable, due to the XML module in Phobos saves the XML 16 | * attributes in an associative array. 17 | */ 18 | 19 | int main () 20 | { 21 | return run() ? EXIT_SUCCESS : EXIT_FAILURE; 22 | } 23 | -------------------------------------------------------------------------------- /modules.ddoc: -------------------------------------------------------------------------------- 1 | MODULES = 2 | $(MODULE orange.core.Attribute) 3 | $(MODULE orange.serialization.archives.Archive) 4 | $(MODULE orange.serialization.archives.XmlArchive) 5 | $(MODULE orange.serialization.Events) 6 | $(MODULE orange.serialization.Events) 7 | $(MODULE orange.serialization.RegisterWrapper) 8 | $(MODULE orange.serialization.Serializable) 9 | $(MODULE orange.serialization.SerializationException) 10 | $(MODULE orange.serialization.Serializer) 11 | $(MODULE orange.util.collection.Array) 12 | $(MODULE orange.util.CTFE) 13 | $(MODULE orange.util.Reflection) 14 | $(MODULE orange.util.Traits) 15 | $(MODULE orange.util.Use) 16 | $(MODULE orange.xml.PhobosXml) 17 | $(MODULE orange.xml.XmlDocument) -------------------------------------------------------------------------------- /orange/serialization/SerializationException.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Jan 30, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.serialization.SerializationException; 8 | 9 | /** 10 | * This class represents an exception, it's the base class of all exceptions used 11 | * throughout this library. 12 | */ 13 | class SerializationException : Exception 14 | { 15 | /** 16 | * Creates a new exception with the given message, file and line info. 17 | * 18 | * Params: 19 | * message = the message of the exception 20 | * file = the file where the exception occurred 21 | * line = the line in the file where the exception occurred 22 | */ 23 | this (string message, string file = __FILE__, size_t line = __LINE__) 24 | { 25 | super(message); 26 | } 27 | 28 | /** 29 | * Creates a new exception out of the given exception. Used for wrapping already existing 30 | * exceptions as SerializationExceptions. 31 | * 32 | * 33 | * Params: 34 | * exception = the exception exception to wrap 35 | */ 36 | this (Exception exception) 37 | { 38 | super(exception.msg, exception.file, exception.line); 39 | } 40 | } -------------------------------------------------------------------------------- /tests/Event.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Event; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.Events; 11 | import orange.serialization.archives.XmlArchive; 12 | import orange.test.UnitTester; 13 | import tests.Util; 14 | 15 | Serializer serializer; 16 | XmlArchive!(char) archive; 17 | 18 | int[] arr; 19 | 20 | class Foo 21 | { 22 | 23 | void serializing () 24 | { 25 | arr ~= 1; 26 | } 27 | 28 | void serialized () 29 | { 30 | arr ~= 2; 31 | } 32 | 33 | void deserializing () 34 | { 35 | arr ~= 3; 36 | } 37 | 38 | void deserialized () 39 | { 40 | arr ~= 4; 41 | } 42 | 43 | mixin OnSerializing!(serializing); 44 | mixin OnSerialized!(serialized); 45 | mixin OnDeserializing!(deserializing); 46 | mixin OnDeserialized!(deserialized); 47 | } 48 | 49 | unittest 50 | { 51 | archive = new XmlArchive!(char); 52 | serializer = new Serializer(archive); 53 | 54 | describe("serialization events") in { 55 | it("all four events should be triggered when serializing and deserializing") in { 56 | serializer.serialize(new Foo); 57 | serializer.deserialize!(Foo)(archive.untypedData); 58 | 59 | assert(arr == [1, 2, 3, 4]); 60 | }; 61 | }; 62 | } -------------------------------------------------------------------------------- /tests/Struct.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Struct; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | struct B 18 | { 19 | bool opEquals (ref const B) const 20 | { 21 | return true; 22 | } 23 | } 24 | 25 | B b; 26 | 27 | unittest 28 | { 29 | archive = new XmlArchive!(char); 30 | serializer = new Serializer(archive); 31 | 32 | describe("serialize struct") in { 33 | it("should return a serialized struct") in { 34 | auto expected = q"xml 35 | 36 | 37 | 38 | 39 | 40 | 41 | xml"; 42 | serializer.reset; 43 | serializer.serialize(B()); 44 | 45 | assert(expected.equalToXml(archive.data)); 46 | }; 47 | }; 48 | 49 | describe("deserialize struct") in { 50 | it("should return a deserialized struct equal to the original struct") in { 51 | auto bDeserialized = serializer.deserialize!(B)(archive.untypedData); 52 | assert(b == bDeserialized); 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /tests/NonCopyableStruct.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.NonCopyableStruct; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | struct B 18 | { 19 | @disable this(this); 20 | @disable ref B opAssign () (auto ref B other); 21 | 22 | bool opEquals (ref const B) const 23 | { 24 | return true; 25 | } 26 | } 27 | 28 | B b; 29 | 30 | unittest 31 | { 32 | archive = new XmlArchive!(char); 33 | serializer = new Serializer(archive); 34 | 35 | describe("serialize struct") in { 36 | it("should return a serialized struct") in { 37 | auto expected = q"xml 38 | 39 | 40 | 41 | 42 | 43 | 44 | xml"; 45 | serializer.reset; 46 | serializer.serialize(B()); 47 | 48 | assert(expected.equalToXml(archive.data)); 49 | }; 50 | }; 51 | 52 | describe("deserialize struct") in { 53 | it("should return a deserialized struct equal to the original struct") in { 54 | auto bDeserialized = serializer.deserialize!(B)(archive.untypedData); 55 | assert(b == bDeserialized); 56 | }; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /tests/Object.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Object; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class A 18 | { 19 | override equals_t opEquals (Object other) 20 | { 21 | if (auto o = cast(A) other) 22 | return true; 23 | 24 | return false; 25 | } 26 | } 27 | 28 | A a; 29 | 30 | unittest 31 | { 32 | archive = new XmlArchive!(char); 33 | serializer = new Serializer(archive); 34 | 35 | a = new A; 36 | 37 | describe("serialize object") in { 38 | it("should return a serialized object") in { 39 | auto expected = q"xml 40 | 41 | 42 | 43 | 44 | 45 | 46 | xml"; 47 | serializer.reset; 48 | serializer.serialize(a); 49 | 50 | assert(expected.equalToXml(archive.data)); 51 | }; 52 | }; 53 | 54 | describe("deserialize object") in { 55 | it("should return a deserialized object equal to the original object") in { 56 | auto aDeserialized = serializer.deserialize!(A)(archive.untypedData); 57 | assert(a == aDeserialized); 58 | }; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /tests/Enum.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Enum; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | enum Foo 18 | { 19 | a, 20 | b, 21 | c 22 | } 23 | 24 | class G 25 | { 26 | Foo foo; 27 | } 28 | 29 | G g; 30 | 31 | unittest 32 | { 33 | archive = new XmlArchive!(char); 34 | serializer = new Serializer(archive); 35 | 36 | g = new G; 37 | g.foo = Foo.b; 38 | 39 | describe("serialize enum") in { 40 | it("should return a serialized enum") in { 41 | auto expected = q"xml 42 | 43 | 44 | 45 | 46 | 1 47 | 48 | 49 | 50 | xml"; 51 | 52 | serializer.reset(); 53 | serializer.serialize(g); 54 | 55 | assert(expected.equalToXml(archive.data)); 56 | }; 57 | }; 58 | 59 | 60 | describe("deserialize enum") in { 61 | it("should return an enum equal to the original enum") in { 62 | auto gDeserialized = serializer.deserialize!(G)(archive.untypedData); 63 | assert(g.foo == gDeserialized.foo); 64 | }; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /tests/EnumConstantMember.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2013 Jacob Carlborg. All rights reserved. 3 | * Authors: Juan Manuel 4 | * Version: Initial created: Apr 14, 2013 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.EnumConstantMember; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class G 18 | { 19 | int a; 20 | enum int someConstant = 4 * 1024; 21 | } 22 | 23 | G g; 24 | 25 | unittest 26 | { 27 | archive = new XmlArchive!(char); 28 | serializer = new Serializer(archive); 29 | 30 | g = new G; 31 | g.a = 123; 32 | 33 | describe("serialize enum") in { 34 | it("shouldn't fail to compile when there is a constant enum member") in { 35 | auto expected = q"xml 36 | 37 | 38 | 39 | 40 | 123 41 | 42 | 43 | 44 | xml"; 45 | serializer.reset(); 46 | serializer.serialize(g); 47 | 48 | assert(expected.equalToXml(archive.data)); 49 | }; 50 | }; 51 | 52 | 53 | describe("deserialize enum") in { 54 | it("shouldn't fail to deserialize when there is a constant enum member") in { 55 | auto gDeserialized = serializer.deserialize!(G)(archive.untypedData); 56 | assert(g.a == gDeserialized.a); 57 | }; 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /tests/Subclass.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 7, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Subclass; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class Base 18 | { 19 | int a; 20 | } 21 | 22 | class Sub : Base 23 | { 24 | int b; 25 | } 26 | 27 | Sub sub; 28 | 29 | unittest 30 | { 31 | archive = new XmlArchive!(char); 32 | serializer = new Serializer(archive); 33 | 34 | sub = new Sub; 35 | sub.a = 3; 36 | sub.b = 4; 37 | 38 | describe("serialize a subclass") in { 39 | it("should return serialized subclass") in { 40 | auto expected = q"xml 41 | 42 | 43 | 44 | 45 | 4 46 | 47 | 3 48 | 49 | 50 | 51 | 52 | xml"; 53 | serializer.reset; 54 | serializer.serialize(sub); 55 | 56 | assert(expected.equalToXml(archive.data)); 57 | }; 58 | }; 59 | 60 | describe("deserialize class with a base class") in { 61 | it("should return a deserialized string equal to the original string") in { 62 | auto subDeserialized = serializer.deserialize!(Sub)(archive.untypedData); 63 | 64 | assert(sub.a == subDeserialized.a); 65 | assert(sub.b == subDeserialized.b); 66 | }; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /tests/Util.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Util; 8 | 9 | import std.algorithm; 10 | import std.array; 11 | import std.xml; 12 | 13 | /** 14 | * Returns $(D_KEYWORD true) if the array contains the given pattern. 15 | * 16 | * Params: 17 | * arr = the array to check if it contains the element 18 | * pattern = the pattern whose presence in the array is to be tested 19 | * 20 | * Returns: $(D_KEYWORD true) if the array contains the given pattern 21 | */ 22 | bool contains (T) (T[] arr, T[] pattern) 23 | { 24 | return !arr.find(pattern).empty; 25 | } 26 | 27 | bool equalToXml(string expected, string actual) 28 | { 29 | import std.string; 30 | 31 | return new Document(expected.strip).isEqual(new Document(actual.strip)); 32 | } 33 | 34 | private: 35 | 36 | T toType(T)(Object o) 37 | { 38 | auto t = cast(T)(o); 39 | 40 | if (t is null) 41 | throw new Exception("Attempt to compare a " ~ T.stringof ~ " with an instance of another type"); 42 | 43 | return t; 44 | } 45 | 46 | bool isEqual(Document lhs, Object rhs) 47 | { 48 | auto doc = toType!(Document)(rhs); 49 | return 50 | (lhs.prolog != doc.prolog ) ? false : ( 51 | (!isEqual(cast(Element) lhs, doc) ) ? false : ( 52 | (lhs.epilog != doc.epilog ) ? false : ( 53 | true ))); 54 | } 55 | 56 | bool isEqual(Element lhs, Object rhs) 57 | { 58 | import std.algorithm; 59 | import std.range; 60 | 61 | auto element = toType!(Element)(rhs); 62 | 63 | if (lhs.tag != element.tag) 64 | return false; 65 | 66 | return lhs.items.zip(element.items).all!(e => e[0].isEqual(e[1])); 67 | } 68 | 69 | bool isEqual(Item lhs, Object rhs) 70 | { 71 | if (auto o = cast(Document) lhs) return o.isEqual(rhs); 72 | if (auto o = cast(Element) lhs) return o.isEqual(rhs); 73 | else return lhs == rhs; 74 | } 75 | -------------------------------------------------------------------------------- /tests/Interface.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2015 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 28, 2015 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Interface; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | interface Interface 18 | { 19 | int b (); 20 | } 21 | 22 | class Foo : Interface 23 | { 24 | int b_; 25 | int b () { return b_; } 26 | } 27 | 28 | class Bar 29 | { 30 | Interface inter; 31 | } 32 | 33 | Bar bar; 34 | 35 | unittest 36 | { 37 | archive = new XmlArchive!(char); 38 | serializer = new Serializer(archive); 39 | 40 | auto foo = new Foo; 41 | foo.b_ = 3; 42 | 43 | bar = new Bar; 44 | bar.inter = foo; 45 | 46 | describe("serialize object") in { 47 | it("should return a serialized object") in { 48 | auto expected = q"xml 49 | 50 | 51 | 52 | 53 | 54 | 3 55 | 56 | 57 | 58 | 59 | xml"; 60 | Serializer.register!(Foo); 61 | serializer.reset; 62 | serializer.serialize(bar); 63 | 64 | assert(expected.equalToXml(archive.data)); 65 | }; 66 | }; 67 | 68 | describe("deserialize object") in { 69 | it("should return a deserialized object equal to the original object") in { 70 | auto barDeserialized = serializer.deserialize!(Bar)(archive.untypedData); 71 | assert(bar.inter.b == barDeserialized.inter.b); 72 | }; 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /tests/Custom.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 17, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Custom; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class Foo 18 | { 19 | int a; 20 | int b; 21 | 22 | void toData (Serializer serializer, Serializer.Data key) 23 | { 24 | i++; 25 | serializer.serialize(a, "x"); 26 | } 27 | 28 | void fromData (Serializer serializer, Serializer.Data key) 29 | { 30 | i++; 31 | a = serializer.deserialize!(int)("x"); 32 | } 33 | } 34 | 35 | Foo foo; 36 | int i; 37 | 38 | unittest 39 | { 40 | archive = new XmlArchive!(char); 41 | serializer = new Serializer(archive); 42 | 43 | foo = new Foo; 44 | foo.a = 3; 45 | foo.b = 4; 46 | i = 3; 47 | 48 | describe("serialize object using custom serialization methods") in { 49 | it("should return a custom serialized object") in { 50 | auto expected = q"xml 51 | 52 | 53 | 54 | 55 | 3 56 | 57 | 58 | 59 | xml"; 60 | serializer.serialize(foo); 61 | 62 | assert(expected.equalToXml(archive.data)); 63 | }; 64 | }; 65 | 66 | describe("deserialize object using custom serialization methods") in { 67 | it("short return a custom deserialized object equal to the original object") in { 68 | auto f = serializer.deserialize!(Foo)(archive.untypedData); 69 | 70 | assert(foo.a == f.a); 71 | 72 | assert(i == 5); 73 | }; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /tests/CircularReference.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Nov 13, 2012 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.CircularReference; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class A 18 | { 19 | B b; 20 | int x; 21 | } 22 | 23 | class B 24 | { 25 | A a; 26 | int y; 27 | } 28 | 29 | A a; 30 | B b; 31 | 32 | unittest 33 | { 34 | archive = new XmlArchive!(char); 35 | serializer = new Serializer(archive); 36 | 37 | a = new A; 38 | a.x = 3; 39 | 40 | b = new B; 41 | b.y = 4; 42 | 43 | b.a = a; 44 | a.b = b; 45 | 46 | describe("serialize objects with circular reference") in { 47 | it("should return a serialized object") in { 48 | auto expected = q"xml 49 | 50 | 51 | 52 | 53 | 54 | 0 55 | 4 56 | 57 | 3 58 | 59 | 60 | 61 | xml"; 62 | serializer.reset; 63 | serializer.serialize(a); 64 | 65 | assert(expected.equalToXml(archive.data)); 66 | }; 67 | }; 68 | 69 | describe("deserialize objects with circular reference") in { 70 | it("should return a deserialized object equal to the original object") in { 71 | auto aDeserialized = serializer.deserialize!(A)(archive.untypedData); 72 | 73 | assert(a is a.b.a); 74 | assert(a.x == aDeserialized.x); 75 | assert(a.b.y == aDeserialized.b.y); 76 | }; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /tests/Array.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Array; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class D 18 | { 19 | int[] arr; 20 | int[2] arr2; 21 | } 22 | 23 | D d; 24 | 25 | unittest 26 | { 27 | archive = new XmlArchive!(char); 28 | serializer = new Serializer(archive); 29 | 30 | d = new D; 31 | d.arr = [27, 382, 283, 3820, 32, 832].dup; 32 | d.arr2 = [76, 34]; 33 | 34 | describe("serialize array") in { 35 | it("should return a serialized array") in { 36 | auto expected = q"xml 37 | 38 | 39 | 40 | 41 | 42 | 76 43 | 34 44 | 45 | 46 | 27 47 | 382 48 | 283 49 | 3820 50 | 32 51 | 832 52 | 53 | 54 | 55 | 56 | xml"; 57 | 58 | serializer.reset; 59 | serializer.serialize(d); 60 | 61 | assert(expected.equalToXml(archive.data)); 62 | }; 63 | }; 64 | 65 | describe("deserialize array") in { 66 | it("should return a deserialize array equal to the original array") in { 67 | auto dDeserialized = serializer.deserialize!(D)(archive.untypedData); 68 | assert(d.arr == dDeserialized.arr); 69 | assert(d.arr2 == dDeserialized.arr2); 70 | }; 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Orange [![Build Status](https://travis-ci.org/jacob-carlborg/orange.svg?branch=master)](https://travis-ci.org/jacob-carlborg/orange) [![Go to orange](https://img.shields.io/dub/v/orange.svg)](https://code.dlang.org/packages/orange) 2 | 3 | Orange is a serialization library for the D programming language. It supports D1/Tango and D2/Phobos. 4 | It can serialize most of the available types in D, including third party types and can serialize 5 | through base class references. It supports fully automatic serialization of all supported types 6 | and also supports several ways to customize the serialization process. Orange has a separate front end 7 | (the serializer) and back end (the archive) making it possible for the user to create new archive 8 | types that can be used with the existing serializer. 9 | 10 | ## Building 11 | 12 | ### Requirements 13 | 14 | * Dub http://code.dlang.org/download 15 | 16 | ### Building 17 | 18 | 1. Install all requirements 19 | 1. Clone the repository 20 | 1. Run `dub build` 21 | 22 | ## Unit Tests 23 | 24 | Run the unit tests using Dub `dub test` 25 | 26 | ## Simple Usage Example 27 | 28 | ```d 29 | module main; 30 | 31 | import orange.serialization._; 32 | import orange.serialization.archives._; 33 | 34 | class Foo 35 | { 36 | int a; 37 | } 38 | 39 | void main () 40 | { 41 | auto foo = new Foo; // create something to serialize 42 | foo.a = 3; // change the default value of "a" 43 | 44 | auto archive = new XmlArchive!(char); // create an XML archive 45 | auto serializer = new Serializer(archive); // create the serializer 46 | 47 | serializer.serialize(foo); // serialize "foo" 48 | 49 | // deserialize the serialized data as an instance of "Foo" 50 | auto f = serializer.deserialize!(Foo)(archive.untypedData); 51 | 52 | // verify that the deserialized value is equal to the original value 53 | assert(f.a == foo.a); 54 | } 55 | ``` 56 | 57 | ### More Examples 58 | 59 | See the [test](https://github.com/jacob-carlborg/orange/tree/master/tests) 60 | directory for some examples. 61 | 62 | ## D Versions 63 | 64 | * D2/Phobos - master branch 65 | * D2/Tango - See the [mambo repository](https://github.com/jacob-carlborg/mambo). 66 | The API is the same, just replace "orange" with "mambo" for the imports 67 | * D1/Tango - d1 branch 68 | * D1/Tango and D2/Phobos in the same branch - mix branch 69 | -------------------------------------------------------------------------------- /tests/CustomWithString.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2013 Jacob Carlborg. All rights reserved. 3 | * Authors: Juan Manuel 4 | * Version: Initial created: Apr 14, 2013 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.CustomWithString; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class Foo 18 | { 19 | int a; 20 | string b; 21 | 22 | void toData (Serializer serializer, Serializer.Data key) 23 | { 24 | i++; 25 | serializer.serialize(a, "x"); 26 | serializer.serialize(b, "y"); 27 | } 28 | 29 | void fromData (Serializer serializer, Serializer.Data key) 30 | { 31 | i++; 32 | a = serializer.deserialize!(int)("x"); 33 | b = serializer.deserialize!(string)("y"); 34 | } 35 | } 36 | 37 | Foo foo; 38 | int i; 39 | 40 | unittest 41 | { 42 | archive = new XmlArchive!(char); 43 | serializer = new Serializer(archive); 44 | 45 | foo = new Foo; 46 | foo.a = 3; 47 | foo.b = "a string"; 48 | i = 3; 49 | 50 | describe("serialize object using custom serialization methods") in { 51 | it("should return a custom serialized object") in { 52 | auto expected = q"xml 53 | 54 | 55 | 56 | 57 | 3 58 | a string 59 | 60 | 61 | 62 | xml"; 63 | serializer.serialize(foo); 64 | 65 | assert(expected.equalToXml(archive.data)); 66 | }; 67 | }; 68 | 69 | describe("deserialize object using custom serialization methods") in { 70 | it("should deserialize the string field properly") in { 71 | auto f = serializer.deserialize!(Foo)(archive.untypedData); 72 | 73 | assert(foo.a == f.a); 74 | assert(foo.b == f.b); 75 | 76 | assert(i == 5); 77 | }; 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /tests/BaseClass.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 7, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.BaseClass; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class Base 18 | { 19 | int a; 20 | int[] c; 21 | 22 | int getA () 23 | { 24 | return a; 25 | } 26 | 27 | int getB () 28 | { 29 | return a; 30 | } 31 | } 32 | 33 | class Sub : Base 34 | { 35 | int b; 36 | 37 | override int getB () 38 | { 39 | return b; 40 | } 41 | } 42 | 43 | Sub sub; 44 | Base base; 45 | 46 | unittest 47 | { 48 | archive = new XmlArchive!(char); 49 | serializer = new Serializer(archive); 50 | 51 | sub = new Sub; 52 | sub.a = 3; 53 | sub.b = 4; 54 | base = sub; 55 | 56 | describe("serialize subclass through a base class reference") in { 57 | it("should return serialized subclass with the static type \"Base\" and the runtime type \"tests.BaseClass.Sub\"") in { 58 | auto expected = q"xml 59 | 60 | 61 | 62 | 63 | 4 64 | 65 | 3 66 | 67 | 68 | 69 | 70 | 71 | xml"; 72 | 73 | Serializer.register!(Sub); 74 | serializer.serialize(base); 75 | 76 | assert(expected.equalToXml(archive.data)); 77 | }; 78 | }; 79 | 80 | describe("deserialize subclass through a base class reference") in { 81 | it("should return a deserialized subclass with the static type \"Base\" and the runtime type \"tests.BaseClass.Sub\"") in { 82 | auto subDeserialized = serializer.deserialize!(Base)(archive.untypedData); 83 | 84 | assert(sub.a == subDeserialized.getA); 85 | assert(sub.b == subDeserialized.getB); 86 | 87 | Serializer.resetRegisteredTypes; 88 | }; 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /tests/ArrayOfObject.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2013 Jacob Carlborg. All rights reserved. 3 | * Authors: Juan Manuel 4 | * Version: Initial created: Apr 14, 2013 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.ArrayOfObject; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class A 18 | { 19 | int a; 20 | 21 | this (int value) 22 | { 23 | this.a = value; 24 | } 25 | } 26 | 27 | class D 28 | { 29 | Object[] arr; 30 | } 31 | 32 | D d; 33 | 34 | unittest 35 | { 36 | archive = new XmlArchive!(char); 37 | serializer = new Serializer(archive); 38 | 39 | d = new D; 40 | d.arr = [cast(Object) new A(1), cast(Object) new A(2)].dup; 41 | 42 | 43 | describe("serialize array") in { 44 | it("shouldn't fail to compile while serializing an Object[] array") in { 45 | auto expected = q"xml 46 | 47 | 48 | 49 | 50 | 51 | 52 | 1 53 | 54 | 55 | 2 56 | 57 | 58 | 59 | 60 | 61 | xml"; 62 | serializer.reset; 63 | Serializer.register!(A); 64 | serializer.serialize(d); 65 | 66 | assert(expected.equalToXml(archive.data)); 67 | }; 68 | }; 69 | 70 | describe("deserialize array") in { 71 | it("should return a deserialized Object[] array equal to the original array") in { 72 | auto dDeserialized = serializer.deserialize!(D)(archive.untypedData); 73 | 74 | assert(d.arr.length == dDeserialized.arr.length); 75 | assert((cast(A) d.arr[0]).a == (cast(A) dDeserialized.arr[0]).a); 76 | assert((cast(A) d.arr[1]).a == (cast(A) dDeserialized.arr[1]).a); 77 | 78 | Serializer.resetRegisteredTypes(); 79 | }; 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /tests/NonSerialized.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 20, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.NonSerialized; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.Serializable; 11 | import orange.serialization.archives.XmlArchive; 12 | import orange.test.UnitTester; 13 | import tests.Util; 14 | 15 | Serializer serializer; 16 | XmlArchive!(char) archive; 17 | 18 | class Bar 19 | { 20 | mixin NonSerialized; 21 | 22 | int c; 23 | } 24 | 25 | @nonSerialized class Baz 26 | { 27 | int c; 28 | } 29 | 30 | class Foo 31 | { 32 | int a; 33 | int b; 34 | @nonSerialized int c; 35 | Bar bar; 36 | Baz baz; 37 | 38 | mixin NonSerialized!(a); 39 | } 40 | 41 | Foo foo; 42 | 43 | unittest 44 | { 45 | archive = new XmlArchive!(char); 46 | serializer = new Serializer(archive); 47 | 48 | foo = new Foo; 49 | foo.a = 3; 50 | foo.b = 4; 51 | foo.c = 5; 52 | 53 | foo.bar = new Bar; 54 | foo.baz = new Baz; 55 | 56 | describe("serialize object with a non-serialized field") in { 57 | it("should return serialized object with only one serialized field") in { 58 | auto expected = q"xml 59 | 60 | 61 | 62 | 63 | 4 64 | 65 | 66 | 67 | xml"; 68 | serializer.serialize(foo); 69 | 70 | auto data = archive.data; 71 | assert(expected.equalToXml(data)); 72 | 73 | assert(!data.contains(`key="a"`)); 74 | assert(!data.contains(`key="c"`)); 75 | 76 | assert(!data.contains(`runtimeType="tests.NonSerialized.Bar"`)); 77 | assert(!data.contains(`runtimeType="tests.NonSerialized.Baz"`)); 78 | }; 79 | }; 80 | 81 | describe("deserialize object with a non-serialized field") in { 82 | it("short return deserialized object equal to the original object, where only one field is deserialized") in { 83 | auto f = serializer.deserialize!(Foo)(archive.untypedData); 84 | 85 | assert(f.a == foo.a.init); 86 | assert(f.b == foo.b); 87 | assert(f.c == foo.c.init); 88 | assert(f.bar is foo.bar.init); 89 | assert(f.baz is foo.baz.init); 90 | }; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /tests/NonIntrusiveStruct.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 18, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.NonIntrusiveStruct; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | struct Foo 18 | { 19 | private int a_; 20 | private int b_; 21 | 22 | @disable this(this); 23 | @disable ref Foo opAssign () (auto ref Foo other); 24 | 25 | int a () { return a_; } 26 | int a (int a) { return a_ = a; } 27 | int b () { return b_; } 28 | int b (int b) { return b_ = b; } 29 | } 30 | 31 | Foo foo; 32 | int i; 33 | 34 | void toData (ref Foo foo, Serializer serializer, Serializer.Data key) 35 | { 36 | i++; 37 | serializer.serialize(foo.a, "a"); 38 | serializer.serialize(foo.b, "b"); 39 | } 40 | 41 | void fromData (ref Foo foo, Serializer serializer, Serializer.Data key) 42 | { 43 | i++; 44 | foo.a = serializer.deserialize!(int)("a"); 45 | foo.b = serializer.deserialize!(int)("b"); 46 | } 47 | 48 | unittest 49 | { 50 | archive = new XmlArchive!(char); 51 | serializer = new Serializer(archive); 52 | 53 | foo.a = 3; 54 | foo.b = 4; 55 | i = 3; 56 | 57 | describe("serialize object using a non-intrusive method") in { 58 | it("should return a custom serialized object") in { 59 | auto expected = q"xml 60 | 61 | 62 | 63 | 64 | 3 65 | 4 66 | 67 | 68 | 69 | xml"; 70 | Serializer.registerSerializer(&toData); 71 | Serializer.registerDeserializer(&fromData); 72 | 73 | serializer.serialize(foo); 74 | 75 | assert(i == 4); 76 | assert(expected.equalToXml(archive.data)); 77 | }; 78 | }; 79 | 80 | describe("deserialize object using a non-intrusive method") in { 81 | it("short return a custom deserialized object equal to the original object") in { 82 | scope (exit) 83 | Serializer.resetSerializers; 84 | 85 | auto f = serializer.deserialize!Foo(archive.untypedData); 86 | 87 | assert(foo.a == f.a); 88 | assert(foo.b == f.b); 89 | 90 | assert(i == 5); 91 | }; 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /orange/core/Attribute.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2013 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Jan 5, 2013 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.core.Attribute; 8 | 9 | import std.typetuple; 10 | 11 | import orange.util.Traits; 12 | 13 | /** 14 | * This struct represent a meta attribute. Any declaration that has this attribute attached to 15 | * itself is to be considered an attribute. That declaration should only be used as an 16 | * attribute and never on its own. 17 | */ 18 | struct attribute { } 19 | 20 | /** 21 | * Evaluates to true if the given symbol is an attribute. An attribute is any declaration with 22 | * the "orange.core.Attribute.attribute" attribute attached. 23 | */ 24 | template isAttribute (alias symbol) 25 | { 26 | static if (isSymbol!(symbol)) 27 | enum isAttribute = getAttributes!(symbol, true).contains!(attribute); 28 | 29 | else 30 | enum isAttribute = false; 31 | } 32 | 33 | /** 34 | * Returns a tuple of all attributes attached to the given symbol. By default this will only 35 | * include actual attributes (see orange.core.Attribute.isAttribute). 36 | * 37 | * Params: 38 | * symbol = the symbol to return the attributes for 39 | * includeNonAttributes = if true, will return all values. Included those not considered 40 | * attributes 41 | */ 42 | template getAttributes (alias symbol, bool includeNonAttributes = false) 43 | { 44 | static if (!__traits(compiles, __traits(getAttributes, symbol))) 45 | alias Attributes!(symbol, TypeTuple!()) getAttributes; 46 | 47 | else 48 | { 49 | alias TypeTuple!(__traits(getAttributes, symbol)) Attrs; 50 | 51 | static if (includeNonAttributes) 52 | alias Attrs FilteredAttrs; 53 | 54 | else 55 | alias Filter!(isAttribute, Attrs) FilteredAttrs; 56 | 57 | alias Attributes!(symbol, FilteredAttrs) getAttributes; 58 | } 59 | } 60 | 61 | /// This struct represent a tuple of attributes attached to the symbol. 62 | struct Attributes (alias sym, Attrs ...) 63 | { 64 | 65 | static: 66 | 67 | /// The symbol these attributes originated from 68 | alias sym symbol; 69 | 70 | /// Returns true if these attributes contain the given symbol 71 | bool contains (alias symbol) () 72 | { 73 | return any ? staticIndexOf!(symbol, Attrs) != -1 : false; 74 | } 75 | 76 | /// Returns true if the attributes are empty. 77 | bool isEmpty () 78 | { 79 | return length == 0; 80 | } 81 | 82 | /// Returns the length of the attributes. 83 | size_t length () 84 | { 85 | return Attrs.length; 86 | } 87 | 88 | /// Returns true if there are any attributes. 89 | bool any () 90 | { 91 | return !isEmpty; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/NonIntrusive.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 18, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.NonIntrusive; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class Base 18 | { 19 | int x; 20 | } 21 | 22 | class Foo : Base 23 | { 24 | private int a_; 25 | private int b_; 26 | 27 | int a () { return a_; } 28 | int a (int a) { return a_ = a; } 29 | int b () { return b_; } 30 | int b (int b) { return b_ = b; } 31 | } 32 | 33 | Foo foo; 34 | int i; 35 | 36 | void toData (Foo foo, Serializer serializer, Serializer.Data key) 37 | { 38 | i++; 39 | serializer.serialize(foo.a, "a"); 40 | serializer.serialize(foo.b, "b"); 41 | serializer.serializeBase(foo); 42 | } 43 | 44 | void fromData (ref Foo foo, Serializer serializer, Serializer.Data key) 45 | { 46 | i++; 47 | foo.a = serializer.deserialize!(int)("a"); 48 | foo.b = serializer.deserialize!(int)("b"); 49 | serializer.deserializeBase(foo); 50 | } 51 | 52 | unittest 53 | { 54 | archive = new XmlArchive!(char); 55 | serializer = new Serializer(archive); 56 | 57 | foo = new Foo; 58 | foo.a = 3; 59 | foo.b = 4; 60 | foo.x = 5; 61 | i = 3; 62 | 63 | describe("serialize object using a non-intrusive method") in { 64 | it("should return a custom serialized object") in { 65 | auto expected = q"xml 66 | 67 | 68 | 69 | 70 | 3 71 | 4 72 | 73 | 5 74 | 75 | 76 | 77 | 78 | xml"; 79 | Serializer.registerSerializer(&toData); 80 | Serializer.registerDeserializer(&fromData); 81 | 82 | serializer.serialize(foo); 83 | 84 | assert(i == 4); 85 | assert(expected.equalToXml(archive.data)); 86 | }; 87 | }; 88 | 89 | describe("deserialize object using a non-intrusive method") in { 90 | it("short return a custom deserialized object equal to the original object") in { 91 | auto f = serializer.deserialize!(Foo)(archive.untypedData); 92 | 93 | assert(foo.a == f.a); 94 | assert(foo.b == f.b); 95 | assert(foo.x == f.x); 96 | 97 | assert(i == 5); 98 | 99 | Serializer.resetSerializers; 100 | }; 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBNAME=orange 2 | DC?=dmd 3 | PREFIX?=/usr/local 4 | #Warning, unittests fail with VERSION=release 5 | VERSION?=standard 6 | LIBDIR=lib/$(MODEL) 7 | ARCH=$(shell arch || uname -m) 8 | 9 | SRC=core/Attribute.d \ 10 | serialization/Events.d \ 11 | serialization/RegisterWrapper.d \ 12 | serialization/Serializable.d \ 13 | serialization/SerializationException.d \ 14 | serialization/Serializer.d \ 15 | serialization/_.d \ 16 | serialization/archives/Archive.d \ 17 | serialization/archives/XmlArchive.d \ 18 | serialization/archives/_.d \ 19 | util/CTFE.d \ 20 | util/Reflection.d \ 21 | util/Traits.d \ 22 | util/Use.d \ 23 | util/_.d \ 24 | util/collection/Array.d \ 25 | util/collection/_.d \ 26 | xml/PhobosXml.d \ 27 | xml/XmlDocument.d \ 28 | xml/_.d 29 | 30 | UNITTEST=tests/Array.d \ 31 | tests/AssociativeArray.d \ 32 | tests/AssociativeArrayReference.d \ 33 | tests/BaseClass.d \ 34 | tests/Custom.d \ 35 | tests/Enum.d \ 36 | tests/Event.d \ 37 | tests/Events.d \ 38 | tests/NonIntrusive.d \ 39 | tests/NonSerialized.d \ 40 | tests/Object.d \ 41 | tests/OverrideSerializer.d \ 42 | tests/Pointer.d \ 43 | tests/Primitive.d \ 44 | tests/Slice.d \ 45 | tests/String.d \ 46 | tests/Struct.d \ 47 | tests/Subclass.d \ 48 | tests/unittest.d \ 49 | tests/Util.d 50 | 51 | TEST=test/UnitTester.d 52 | 53 | ifdef MODEL 54 | DCFLAGS=-m$(MODEL) 55 | else ifeq ("$(ARCH)", "x86_64") 56 | DCFLAGS=-m64 57 | override MODEL=64 58 | else 59 | DCFLAGS=-m32 60 | override MODEL=32 61 | endif 62 | 63 | ifeq ("$(VERSION)","release") 64 | DCFLAGS += -O 65 | else ifeq ("$(VERSION)","debug") 66 | DCFLAGS += -g 67 | endif 68 | 69 | ifeq ("$(DC)","dmd") 70 | LIBCOMMAND = $(DC) -lib $(DCFLAGS) -of$@ $^ 71 | else ifeq ("$(DC)","gdc") 72 | override DC=gdmd 73 | LIBCOMMAND = ar rcs $@ $^ 74 | else ifeq ("$(DC)","gdmd") 75 | LIBCOMMAND = ar rcs $@ $^ 76 | endif 77 | 78 | # Everything below this line should be fairly generic (with a few hard-coded things). 79 | 80 | OBJ=$(addsuffix .o,$(addprefix $(LIBDIR)/$(LIBNAME)/,$(basename $(SRC)))) 81 | HEADER=$(addsuffix .di,$(addprefix import/$(LIBNAME)/,$(basename $(SRC)))) 82 | TARGET=$(LIBDIR)/lib$(LIBNAME).a 83 | 84 | all: mkdirs $(TARGET) $(HEADER) 85 | 86 | mkdirs: 87 | mkdir -p $(addprefix $(LIBDIR)/$(LIBNAME)/, $(sort $(dir $(SRC)))) 88 | 89 | install: all 90 | @mkdir -p $(PREFIX)/lib $(PREFIX)/include/d 91 | cp $(TARGET) $(PREFIX)/lib/ 92 | cp -r import/$(LIBNAME) $(PREFIX)/include/d 93 | 94 | uninstall: 95 | rm -rf $(PREFIX)/include/d/$(LIBNAME) 96 | rm -f $(PREFIX)/lib/lib$(LIBNAME).a 97 | @rmdir -p --ignore-fail-on-non-empty $(PREFIX)/lib $(PREFIX)/include/d 2>/dev/null || true 98 | 99 | unittest: ~~cleanunittest 100 | $(DC) $(DCFLAGS) -unittest -ofunittest $(addprefix $(LIBNAME)/,$(SRC)) $(UNITTEST) $(LIBNAME)/$(TEST) 101 | ./unittest 102 | 103 | clean: ~~cleanunittest 104 | rm -rf import/ lib/ 105 | 106 | ~~cleanunittest: 107 | rm -f unittest.o unittest 108 | 109 | $(TARGET): $(OBJ) 110 | $(LIBCOMMAND) 111 | 112 | $(LIBDIR)/$(LIBNAME)/%.o: $(LIBNAME)/%.d 113 | $(DC) -c $(DCFLAGS) -of$@ $< 114 | 115 | import/$(LIBNAME)/%.di: $(LIBNAME)/%.d 116 | $(DC) -c -o- -Hf$@ $< 117 | -------------------------------------------------------------------------------- /orange/util/CTFE.d: -------------------------------------------------------------------------------- 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.util.CTFE; 8 | 9 | import orange.util.Traits; 10 | 11 | /// Compile time string converter. Converts the given arguments to a string. 12 | template format (ARGS...) 13 | { 14 | static if (ARGS.length == 0) 15 | enum format = ""; 16 | 17 | else 18 | { 19 | static if (is(typeof(ARGS[0]) : string)) 20 | enum format = ARGS[0] ~ format!(ARGS[1 .. $]); 21 | 22 | else 23 | enum format = toString_!(ARGS[0]) ~ format!(ARGS[1 .. $]); 24 | } 25 | } 26 | 27 | private 28 | { 29 | template toString_ (T) 30 | { 31 | enum toString_ = T.stringof; 32 | } 33 | 34 | template toString_ (int i) 35 | { 36 | enum toString_ = itoa!(i); 37 | } 38 | 39 | template toString_ (long l) 40 | { 41 | enum toString_ = itoa!(l); 42 | } 43 | 44 | template toString_ (bool b) 45 | { 46 | enum toString_ = b ? "true" : "false"; 47 | } 48 | 49 | template toString_ (float f) 50 | { 51 | enum toString_ = ""; 52 | } 53 | 54 | template toString_ (alias a) 55 | { 56 | enum toString_ = a.stringof; 57 | } 58 | } 59 | 60 | /** 61 | * Compile-time function to get the index of the give element. 62 | * 63 | * Performs a linear scan, returning the index of the first occurrence 64 | * of the specified element in the array, or U.max if the array does 65 | * not contain the element. 66 | * 67 | * Params: 68 | * arr = the array to get the index of the element from 69 | * element = the element to find 70 | * 71 | * Returns: the index of the element or size_t.max if the element was not found. 72 | */ 73 | size_t indexOf (T) (T[] arr, T element) 74 | { 75 | static if (is(T == char) || is(T == wchar) || is(T == dchar)) 76 | { 77 | foreach (i, e ; arr) 78 | if (e == element) 79 | return i; 80 | } 81 | 82 | else 83 | { 84 | foreach (i, e ; arr) 85 | if (e == element) 86 | return i; 87 | } 88 | 89 | return size_t.max; 90 | } 91 | 92 | /** 93 | * Returns true if the given array contains the given element, 94 | * otherwise false. 95 | * 96 | * Params: 97 | * arr = the array to search in for the element 98 | * element = the element to search for 99 | * 100 | * Returns: true if the array contains the element, otherwise false 101 | */ 102 | bool contains (T) (T[] arr, T element) 103 | { 104 | return indexOf(arr, element) != size_t.max; 105 | } 106 | 107 | private: 108 | 109 | template decimalDigit (int n) // [3] 110 | { 111 | enum decimalDigit = "0123456789"[n .. n + 1]; 112 | } 113 | 114 | template itoa (long n) 115 | { 116 | static if (n < 0) 117 | enum itoa = "-" ~ itoa!(-n); 118 | 119 | else static if (n < 10) 120 | enum itoa = decimalDigit!(n); 121 | 122 | else 123 | enum itoa = itoa!(n / 10L) ~ decimalDigit!(n % 10L); 124 | } -------------------------------------------------------------------------------- /tests/Events.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 7, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Events; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.Events; 11 | import orange.serialization.archives.XmlArchive; 12 | import orange.test.UnitTester; 13 | import tests.Util; 14 | 15 | Serializer serializer; 16 | XmlArchive!(char) archive; 17 | 18 | int b; 19 | int c; 20 | 21 | int udaB; 22 | int udaC; 23 | 24 | class Events 25 | { 26 | int a; 27 | int d; 28 | 29 | int udaA; 30 | int udaD; 31 | 32 | void serializing () 33 | { 34 | a = 3; 35 | } 36 | 37 | mixin OnSerializing!(serializing); 38 | 39 | @onSerializing void udaSerializing () 40 | { 41 | udaA = 3; 42 | } 43 | 44 | void serialized () 45 | { 46 | b = 4; 47 | } 48 | 49 | mixin OnSerialized!(serialized); 50 | 51 | @onSerialized void udaSerialized () 52 | { 53 | udaB = 4; 54 | } 55 | 56 | void deserializing () 57 | { 58 | c = 5; 59 | } 60 | 61 | mixin OnDeserializing!(deserializing); 62 | 63 | @onDeserializing void udaDeserializing () 64 | { 65 | udaC = 5; 66 | } 67 | 68 | void deserialized () 69 | { 70 | d = 6; 71 | } 72 | 73 | mixin OnDeserialized!(deserialized); 74 | 75 | @onDeserialized void udaDeserialized () 76 | { 77 | udaD = 6; 78 | } 79 | } 80 | 81 | Events events; 82 | 83 | unittest 84 | { 85 | archive = new XmlArchive!(char); 86 | serializer = new Serializer(archive); 87 | 88 | events = new Events; 89 | 90 | describe("serialize a class with event handlers") in { 91 | it("should return serialized class with the correct values set by the event handlers") in { 92 | auto expected =q"xml 93 | 94 | 95 | 96 | 97 | 3 98 | 0 99 | 3 100 | 0 101 | 102 | 103 | 104 | xml"; 105 | serializer.reset; 106 | serializer.serialize(events); 107 | 108 | assert(b == 4); 109 | assert(udaB == 4); 110 | assert(expected.equalToXml(archive.data)); 111 | }; 112 | }; 113 | 114 | describe("deserialize class with a base class") in { 115 | it("should return a deserialized string equal to the original string") in { 116 | auto eventsDeserialized = serializer.deserialize!(Events)(archive.untypedData); 117 | 118 | assert(eventsDeserialized.a == 3); 119 | assert(eventsDeserialized.d == 6); 120 | 121 | assert(eventsDeserialized.udaA == 3); 122 | assert(eventsDeserialized.udaD == 6); 123 | 124 | assert(c == 5); 125 | assert(udaC == 5); 126 | }; 127 | }; 128 | } 129 | -------------------------------------------------------------------------------- /tests/OverrideSerializer.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Dec 20, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.OverrideSerializer; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class Base 18 | { 19 | int x; 20 | } 21 | 22 | class Foo : Base 23 | { 24 | private int a_; 25 | private int b_; 26 | 27 | int a () { return a_; } 28 | int a (int a) { return a_ = a; } 29 | int b () { return b_; } 30 | int b (int b) { return b_ = b; } 31 | } 32 | 33 | Foo foo; 34 | int i; 35 | 36 | void toData (Foo foo, Serializer serializer, Serializer.Data key) 37 | { 38 | i += 4; 39 | } 40 | 41 | void fromData (ref Foo foo, Serializer serializer, Serializer.Data key) 42 | { 43 | i += 4; 44 | } 45 | 46 | void overrideToData (Foo foo, Serializer serializer, Serializer.Data key) 47 | { 48 | i++; 49 | serializer.serialize(foo.a, "a"); 50 | serializer.serialize(foo.b, "b"); 51 | serializer.serializeBase(foo); 52 | } 53 | 54 | void overrideFromData (ref Foo foo, Serializer serializer, Serializer.Data key) 55 | { 56 | i++; 57 | foo.a = serializer.deserialize!(int)("a"); 58 | foo.b = serializer.deserialize!(int)("b"); 59 | serializer.deserializeBase(foo); 60 | } 61 | 62 | unittest 63 | { 64 | archive = new XmlArchive!(char); 65 | serializer = new Serializer(archive); 66 | 67 | foo = new Foo; 68 | foo.a = 3; 69 | foo.b = 4; 70 | foo.x = 5; 71 | i = 3; 72 | 73 | describe("serialize object using an overridden serializer") in { 74 | it("should return a custom serialized object") in { 75 | auto expected = q"xml 76 | 77 | 78 | 79 | 80 | 3 81 | 4 82 | 83 | 5 84 | 85 | 86 | 87 | 88 | xml"; 89 | Serializer.registerSerializer(&toData); 90 | Serializer.registerDeserializer(&fromData); 91 | 92 | serializer.overrideSerializer(&overrideToData); 93 | serializer.overrideDeserializer(&overrideFromData); 94 | 95 | serializer.serialize(foo); 96 | 97 | assert(i == 4); 98 | assert(expected.equalToXml(archive.data)); 99 | }; 100 | }; 101 | 102 | describe("deserialize object using an overridden deserializer") in { 103 | it("short return a custom deserialized object equal to the original object") in { 104 | auto f = serializer.deserialize!(Foo)(archive.untypedData); 105 | 106 | assert(foo.a == f.a); 107 | assert(foo.b == f.b); 108 | assert(foo.x == f.x); 109 | 110 | assert(i == 5); 111 | 112 | Serializer.resetSerializers; 113 | }; 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /orange/serialization/Events.d: -------------------------------------------------------------------------------- 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.Events; 8 | 9 | import orange.core.Attribute; 10 | import orange.util._; 11 | 12 | /** 13 | * This event is triggered after the struct/class, this template has been mixed into, 14 | * has been completely deserialized, including all the fields. 15 | * 16 | * Params: 17 | * method = the method to be invoked when the event is triggered 18 | */ 19 | template OnDeserialized (alias method) 20 | { 21 | static orange.serialization.Events.Event!(method) __onDeserialized; 22 | } 23 | 24 | /** 25 | * Methods with this attribute attached will be called after the struct/class has been 26 | * deserialized. 27 | */ 28 | @attribute struct onDeserialized { } 29 | 30 | /** 31 | * This event is triggered after the struct/class, this template has been mixed into, 32 | * has been deserialized, but before any fields have been deserialized. 33 | * 34 | * Params: 35 | * method = the method to be invoked when the event is triggered 36 | */ 37 | template OnDeserializing (alias method) 38 | { 39 | static orange.serialization.Events.Event!(method) __onDeserializing; 40 | } 41 | 42 | /** 43 | * Methods with this attribute attached will be called before the struct/class has been 44 | * deserialized. 45 | */ 46 | @attribute struct onDeserializing { } 47 | 48 | /** 49 | * This event is triggered after the struct/class, this template has been mixed into, 50 | * has been completely serialized, including all the fields. 51 | * 52 | * Params: 53 | * method = the method to be invoked when the event is triggered 54 | */ 55 | template OnSerialized (alias method) 56 | { 57 | static orange.serialization.Events.Event!(method) __onSerialized; 58 | } 59 | 60 | /** 61 | * Methods with this attribute attached will be called after the struct/class has been 62 | * serialized. 63 | */ 64 | @attribute struct onSerialized { } 65 | 66 | /** 67 | * This event is triggered after the struct/class, this template has been mixed into, 68 | * has been serialized, but before any fields have been serialized. 69 | * 70 | * Params: 71 | * method = the method to be invoked when the event is triggered 72 | */ 73 | template OnSerializing (alias method) 74 | { 75 | static orange.serialization.Events.Event!(method) __onSerializing; 76 | } 77 | 78 | /** 79 | * Methods with this attribute attached will be called before the struct/class has been 80 | * serialized. 81 | */ 82 | @attribute struct onSerializing { } 83 | 84 | /** 85 | * This struct represents an event. 86 | * 87 | * Params: 88 | * m = the method to be invoked when the event is triggered 89 | */ 90 | struct Event (alias m) 91 | { 92 | private enum method = &m; 93 | 94 | /** 95 | * Triggers the event on the given value. 96 | * 97 | * Params: 98 | * value = the object to trigger the event on 99 | */ 100 | void opCall (T) (T value) 101 | { 102 | void delegate () dg; 103 | dg.ptr = cast(void*) value; 104 | dg.funcptr = method; 105 | dg(); 106 | } 107 | } 108 | 109 | package: 110 | 111 | enum onDeserializedField = "__onDeserialized"; 112 | enum onDeserializingField = "__onDeserializing"; 113 | enum onSerializedField = "__onSerialized"; 114 | enum onSerializingField = "__onSerializing"; -------------------------------------------------------------------------------- /tests/NonMutable.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Nov 7, 2012 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.NonMutable; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class B 18 | { 19 | int a; 20 | 21 | pure this (int a) 22 | { 23 | this.a = a; 24 | } 25 | 26 | override equals_t opEquals (Object other) 27 | { 28 | if (auto o = cast(B) other) 29 | return a == o.a; 30 | 31 | return false; 32 | } 33 | } 34 | 35 | class A 36 | { 37 | const int a; 38 | immutable int b; 39 | immutable string c; 40 | immutable B d; 41 | immutable(int)* e; 42 | 43 | this (int a, int b, string c, immutable B d, immutable(int)* e) 44 | { 45 | this.a = a; 46 | this.b = b; 47 | this.c = c; 48 | this.d = d; 49 | this.e = e; 50 | } 51 | 52 | override equals_t opEquals (Object other) 53 | { 54 | if (auto o = cast(A) other) 55 | return a == o.a && 56 | b == o.b && 57 | c == o.c && 58 | d == o.d && 59 | *e == *o.e; 60 | 61 | return false; 62 | } 63 | } 64 | 65 | A a; 66 | immutable int ptr = 3; 67 | 68 | class CTFEFieldsIssue35 69 | { 70 | public immutable int FIRST; 71 | public immutable int SECOND1; 72 | public bool someFlag; 73 | 74 | this () 75 | { 76 | FIRST = 1; 77 | SECOND1 = 1; 78 | } 79 | } 80 | 81 | unittest 82 | { 83 | archive = new XmlArchive!(char); 84 | serializer = new Serializer(archive); 85 | 86 | a = new A(1, 2, "str", new immutable(B)(3), &ptr); 87 | 88 | describe("serialize object with immutable and const fields") in { 89 | it("should return a serialized object") in { 90 | auto expected = q"xml 91 | 92 | 93 | 94 | 95 | 1 96 | 2 97 | 98 | 3 99 | 100 | 101 | 3 102 | 103 | str 104 | 105 | 106 | 107 | xml"; 108 | serializer.reset; 109 | serializer.serialize(a); 110 | 111 | assert(expected.equalToXml(archive.data)); 112 | }; 113 | }; 114 | 115 | describe("deserialize object") in { 116 | it("should return a deserialized object equal to the original object") in { 117 | auto aDeserialized = serializer.deserialize!(A)(archive.untypedData); 118 | assert(a == aDeserialized); 119 | }; 120 | }; 121 | 122 | describe("serializing object with CTFE fields") in { 123 | it("should compile") in { 124 | assert(__traits(compiles, { 125 | serializer.serialize(new CTFEFieldsIssue35); 126 | })); 127 | }; 128 | }; 129 | } 130 | -------------------------------------------------------------------------------- /tests/Pointer.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Pointer; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class F 18 | { 19 | int value; 20 | int* ptr; 21 | int* ptr2; 22 | } 23 | 24 | F f; 25 | F fDeserialized; 26 | int pointee; 27 | 28 | class OutOfOrder 29 | { 30 | int* ptr; 31 | int value; 32 | int* ptr2; 33 | } 34 | 35 | OutOfOrder outOfOrder; 36 | OutOfOrder outOfOrderDeserialized; 37 | int outOfOrderPointee; 38 | 39 | unittest 40 | { 41 | archive = new XmlArchive!(char); 42 | serializer = new Serializer(archive); 43 | 44 | pointee = 3; 45 | f = new F; 46 | f.value = 9; 47 | f.ptr = &f.value; 48 | f.ptr2 = &pointee; 49 | 50 | outOfOrderPointee = 3; 51 | outOfOrder = new OutOfOrder; 52 | outOfOrder.value = 9; 53 | outOfOrder.ptr = &outOfOrder.value; 54 | outOfOrder.ptr2 = &outOfOrderPointee; 55 | 56 | describe("serialize pointer") in { 57 | it("should return a serialized pointer") in { 58 | auto expected = q"xml 59 | 60 | 61 | 62 | 63 | 9 64 | 65 | 1 66 | 67 | 68 | 3 69 | 70 | 71 | 72 | 73 | xml"; 74 | serializer.reset(); 75 | serializer.serialize(f); 76 | 77 | assert(expected.equalToXml(archive.data)); 78 | }; 79 | }; 80 | 81 | describe("deserialize pointer") in { 82 | fDeserialized = serializer.deserialize!(F)(archive.untypedData); 83 | 84 | it("should return a deserialized pointer equal to the original pointer") in { 85 | assert(*f.ptr == *fDeserialized.ptr); 86 | }; 87 | 88 | it("the pointer should point to the deserialized value") in { 89 | assert(fDeserialized.ptr == &fDeserialized.value); 90 | }; 91 | }; 92 | 93 | describe("serialize pointer out of order") in { 94 | it("should return a serialized pointer") in { 95 | auto expected = q"xml 96 | 97 | 98 | 99 | 100 | 101 | 9 102 | 103 | 1 104 | 105 | 3 106 | 107 | 108 | 109 | 110 | xml"; 111 | serializer.reset(); 112 | serializer.serialize(outOfOrder); 113 | 114 | assert(expected.equalToXml(archive.data)); 115 | }; 116 | }; 117 | 118 | describe("deserialize pointer out of order") in { 119 | outOfOrderDeserialized = serializer.deserialize!(OutOfOrder)(archive.untypedData); 120 | 121 | it("should return a deserialized pointer equal to the original pointer") in { 122 | assert(*outOfOrder.ptr == *outOfOrderDeserialized.ptr); 123 | }; 124 | 125 | it("the pointer should point to the deserialized value") in { 126 | assert(outOfOrderDeserialized.ptr == &outOfOrderDeserialized.value); 127 | }; 128 | }; 129 | } 130 | -------------------------------------------------------------------------------- /tests/AssociativeArray.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.AssociativeArray; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class E 18 | { 19 | int[int] aa; 20 | } 21 | 22 | E e; 23 | 24 | unittest 25 | { 26 | archive = new XmlArchive!(char); 27 | serializer = new Serializer(archive); 28 | 29 | e = new E; 30 | e.aa = [3 : 4, 1 : 2, 39 : 472, 6 : 7]; 31 | 32 | describe("serialize associative array") in { 33 | it("should return a serialized associative array") in { 34 | auto expected2066 = q"xml 35 | 36 | 37 | 38 | 39 | 40 | 41 | 1 42 | 43 | 44 | 2 45 | 46 | 47 | 3 48 | 49 | 50 | 4 51 | 52 | 53 | 6 54 | 55 | 56 | 7 57 | 58 | 59 | 39 60 | 61 | 62 | 472 63 | 64 | 65 | 66 | 67 | 68 | xml"; 69 | 70 | auto expected2067 = q"xml 71 | 72 | 73 | 74 | 75 | 76 | 77 | 6 78 | 79 | 80 | 7 81 | 82 | 83 | 3 84 | 85 | 86 | 4 87 | 88 | 89 | 1 90 | 91 | 92 | 2 93 | 94 | 95 | 39 96 | 97 | 98 | 472 99 | 100 | 101 | 102 | 103 | 104 | xml"; 105 | static if (__VERSION__ >= 2067) auto expected = expected2067; 106 | else auto expected = expected2066; 107 | 108 | serializer.reset(); 109 | serializer.serialize(e); 110 | 111 | assert(expected.equalToXml(archive.data)); 112 | }; 113 | }; 114 | 115 | describe("deserialize associative array") in { 116 | it("should return an associative array equal to the original associative array") in { 117 | auto eDeserialized = serializer.deserialize!(E)(archive.untypedData); 118 | 119 | foreach (k, v ; eDeserialized.aa) 120 | assert(e.aa[k] == v); 121 | 122 | assert(e.aa == eDeserialized.aa); 123 | }; 124 | }; 125 | } 126 | -------------------------------------------------------------------------------- /tests/AssociativeArrayReference.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.AssociativeArrayReference; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class K 18 | { 19 | int[int] a; 20 | int[int] b; 21 | } 22 | 23 | K k; 24 | 25 | unittest 26 | { 27 | archive = new XmlArchive!(char); 28 | serializer = new Serializer(archive); 29 | 30 | k = new K; 31 | k.a = [3 : 4, 1 : 2, 39 : 472, 6 : 7]; 32 | k.b = k.a; 33 | 34 | describe("serialize associative array references") in { 35 | it("should return a serialized associative array and a serialized reference") in { 36 | auto expected2066 = q"xml 37 | 38 | 39 | 40 | 41 | 42 | 43 | 1 44 | 45 | 46 | 2 47 | 48 | 49 | 3 50 | 51 | 52 | 4 53 | 54 | 55 | 6 56 | 57 | 58 | 7 59 | 60 | 61 | 39 62 | 63 | 64 | 472 65 | 66 | 67 | 1 68 | 69 | 70 | 71 | xml"; 72 | 73 | auto expected2067 = q"xml 74 | 75 | 76 | 77 | 78 | 79 | 80 | 6 81 | 82 | 83 | 7 84 | 85 | 86 | 3 87 | 88 | 89 | 4 90 | 91 | 92 | 1 93 | 94 | 95 | 2 96 | 97 | 98 | 39 99 | 100 | 101 | 472 102 | 103 | 104 | 1 105 | 106 | 107 | 108 | xml"; 109 | static if (__VERSION__ >= 2067) auto expected = expected2067; 110 | else auto expected = expected2066; 111 | 112 | serializer.reset(); 113 | serializer.serialize(k); 114 | 115 | assert(expected.equalToXml(archive.data)); 116 | }; 117 | }; 118 | 119 | describe("deserialize associative array references") in { 120 | it("should return two deserialized associative arrays pointing to the same data") in { 121 | auto kDeserialized = serializer.deserialize!(K)(archive.untypedData); 122 | 123 | assert(kDeserialized.a is kDeserialized.b); 124 | }; 125 | }; 126 | } 127 | -------------------------------------------------------------------------------- /orange/util/Use.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Jan 29, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.util.Use; 8 | 9 | import std.typetuple; 10 | import std.typecons; 11 | import std.traits; 12 | 13 | alias ReturnType ReturnTypeOf; 14 | 15 | /** 16 | * This struct can be used to implement, what looks similar to, new statements. This 17 | * struct overloads the "in" operator which a delegate can be passed to. The delegate 18 | * passed to the "in" operator will then be called at an appropriate time decided by the 19 | * implementation of the function returning the Use struct. 20 | * 21 | * Examples: 22 | * --- 23 | * Use!(void delegate (), bool) unless (bool condition) 24 | * { 25 | * Use!(void delegate (), bool) use; 26 | * use.args[1] = condition; 27 | * 28 | * use.args[0] = (void delegate () dg, bool condition) { 29 | * if (!condition) 30 | * dg(); 31 | * }; 32 | * 33 | * return use; 34 | * } 35 | * 36 | * int a = 3; 37 | * int b = 4; 38 | * 39 | * unless(a == b) in { 40 | * println("a != b"); 41 | * }; 42 | * --- 43 | */ 44 | struct Use (ARGS...) 45 | { 46 | static assert (ARGS.length > 0); 47 | 48 | private 49 | { 50 | alias ReturnTypeOf!(ARGS[0]) ReturnType; 51 | 52 | static if (ARGS.length >= 2) 53 | alias Tuple!(ReturnType delegate (ARGS), ARGS[1 .. $]) NEW_ARGS; 54 | 55 | else 56 | alias Tuple!(ReturnType delegate (ARGS)) NEW_ARGS; 57 | } 58 | 59 | /** 60 | * The first argument will be the delegate that performs some arbitrary operation. The 61 | * rest of the arguments will be pass as arguments to the delegate in "args[0]". 62 | */ 63 | NEW_ARGS args; 64 | 65 | /** 66 | * Overloads the "in" operator. The given delegate is supplied by the user and will be 67 | * called at a time the implementaion has decided. 68 | * 69 | * Params: 70 | * dg = the user supplied delegate that will be called 71 | * 72 | * Returns: what ever the delegate stored in "args[0]" returns 73 | */ 74 | ReturnType opBinary(string op: "in")(ARGS[0] dg) 75 | { 76 | assert(args[0]); 77 | 78 | // Issue: https://d.puremagic.com/issues/show_bug.cgi?id=11614 79 | static if (args.length == 1) 80 | return args[0](dg); 81 | 82 | else 83 | return args[0](dg, args.expand[1 .. $]); 84 | } 85 | } 86 | 87 | /** 88 | * This is a helper struct used by the "restore" function. It overloads the "in" operator 89 | * to allow to taking a delegate. 90 | */ 91 | struct RestoreStruct (U, T) 92 | { 93 | /// The delegate that performs the operation. 94 | U delegate(U delegate (), ref T) dg; 95 | 96 | /// A pointer to the value to pass to the delegate. 97 | T* value; 98 | 99 | /** 100 | * Overloads the "in" operator. It will simply call the delegate stored in the struct 101 | * passing in the given delegate and the value stored in the struct. 102 | * 103 | * Params: 104 | * deleg = the delegate to pass the delegate stored in the struct 105 | * 106 | * Returns: whatever the delegate stored in the struct returns 107 | * 108 | * See_Also: restore 109 | */ 110 | U opBinary(string op: "in")(U delegate () deleg) 111 | { 112 | return dg(deleg, *value); 113 | } 114 | } 115 | 116 | /** 117 | * Restores the given variable to the value it was when it was passed to the function 118 | * after the delegate has finished. 119 | * 120 | * Examples: 121 | * --- 122 | * int a = 3; 123 | * 124 | * restore(a) in { 125 | * a = 4; 126 | * } 127 | * 128 | * assert(a == 3); 129 | * --- 130 | * 131 | * Params: 132 | * val = variable that will be restored 133 | * 134 | * Returns: a RestoreStruct 135 | * 136 | * See_Also: RestoreStruct 137 | */ 138 | RestoreStruct!(U, T) restore (U = void, T) (ref T val) 139 | { 140 | RestoreStruct!(U, T) restoreStruct; 141 | 142 | restoreStruct.dg = (U delegate () dg, ref T value){ 143 | T t = value; 144 | 145 | static if (is(U == void)) 146 | { 147 | dg(); 148 | value = t; 149 | } 150 | 151 | else 152 | { 153 | auto result = dg(); 154 | value = t; 155 | 156 | return result; 157 | } 158 | }; 159 | 160 | restoreStruct.value = &val; 161 | 162 | return restoreStruct; 163 | } 164 | -------------------------------------------------------------------------------- /tests/String.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.String; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class C 18 | { 19 | string str; 20 | wstring wstr; 21 | dstring dstr; 22 | } 23 | 24 | C c; 25 | C u; 26 | 27 | unittest 28 | { 29 | archive = new XmlArchive!(char); 30 | serializer = new Serializer(archive); 31 | 32 | c = new C; 33 | c.str = "foo"; 34 | c.wstr = "bar"; 35 | c.dstr = "foobar"; 36 | 37 | describe("serialize strings") in { 38 | it("should return serialized strings") in { 39 | auto expected2066 = q"xml 40 | 41 | 42 | 43 | 44 | bar 45 | foo 46 | foobar 47 | 48 | 49 | 50 | xml"; 51 | 52 | auto expected2067 = q"xml 53 | 54 | 55 | 56 | 57 | bar 58 | foobar 59 | foo 60 | 61 | 62 | 63 | xml"; 64 | static if (__VERSION__ >= 2067) auto expected = expected2067; 65 | else auto expected = expected2066; 66 | 67 | serializer.reset; 68 | serializer.serialize(c); 69 | 70 | assert(expected.equalToXml(archive.data)); 71 | }; 72 | }; 73 | 74 | describe("deserialize string") in { 75 | it("should return a deserialized string equal to the original string") in { 76 | auto cDeserialized = serializer.deserialize!(C)(archive.untypedData); 77 | 78 | assert(c.str == cDeserialized.str); 79 | assert(c.wstr == cDeserialized.wstr); 80 | assert(c.dstr == cDeserialized.dstr); 81 | }; 82 | }; 83 | 84 | u = new C; 85 | u.str = "foo åäö"; 86 | u.wstr = "foo ÅÄÖ"; 87 | u.dstr = "foo åäö ÅÄÖ"; 88 | 89 | describe("serialize Unicode strings") in { 90 | it("should return a serialized string containing proper Unicode") in { 91 | auto expected2066 = q"xml 92 | 93 | 94 | 95 | 96 | foo ÅÄÖ 97 | foo åäö 98 | foo åäö ÅÄÖ 99 | 100 | 101 | 102 | xml"; 103 | 104 | auto expected2067 = q"xml 105 | 106 | 107 | 108 | 109 | foo ÅÄÖ 110 | foo åäö ÅÄÖ 111 | foo åäö 112 | 113 | 114 | 115 | xml"; 116 | static if (__VERSION__ >= 2067) auto expected = expected2067; 117 | else auto expected = expected2066; 118 | 119 | serializer.reset; 120 | serializer.serialize(u); 121 | 122 | assert(expected.equalToXml(archive.data)); 123 | }; 124 | }; 125 | 126 | describe("deserialize Unicode string") in { 127 | it("should return a deserialize Unicode string equal to the original strings") in { 128 | auto uDeserialized = serializer.deserialize!(C)(archive.untypedData); 129 | 130 | assert(u.str == uDeserialized.str); 131 | assert(u.wstr == uDeserialized.wstr); 132 | assert(u.dstr == uDeserialized.dstr); 133 | }; 134 | }; 135 | } 136 | -------------------------------------------------------------------------------- /orange/serialization/Serializable.d: -------------------------------------------------------------------------------- 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.Serializable; 8 | 9 | import orange.core.Attribute; 10 | import orange.serialization.archives.Archive; 11 | import orange.serialization.Events; 12 | import orange.serialization.Serializer; 13 | import orange.util.CTFE; 14 | 15 | /** 16 | * This interface represents a type that this is serializable. To implement this interface 17 | * the user needs to implement two methods, one for serialization and one for 18 | * deserialization. These methods are used to perform custom (de)serialization and will 19 | * be called if available. It's up to these methods to call the serializer to perform 20 | * the (de)serialization. If these methods are available the automatic (de)serialization 21 | * process $(I will not) be performed. 22 | * 23 | * These methods can also be used without actually implementing this interface, i.e. they 24 | * also work for structs. 25 | * 26 | * Examples: 27 | * --- 28 | * class Foo : Serializable 29 | * { 30 | * int a; 31 | * 32 | * void toData (Serializer serializer, Serializer.Data key) 33 | * { 34 | * serializer.serialize(a, "b"); 35 | * } 36 | * 37 | * void fromData (Serializer serializer, Serializer.Data key) 38 | * { 39 | * a = serializer.deserialize!(int)("b"); 40 | * } 41 | * } 42 | * --- 43 | * 44 | * See_Also: isSerializable 45 | */ 46 | interface Serializable 47 | { 48 | /** 49 | * Called by the given serializer when performing custom serialization. 50 | * 51 | * Params: 52 | * serializer = the serializer that performs the serialization 53 | * key = the key of the receiver 54 | */ 55 | void toData (Serializer serializer, Serializer.Data key); 56 | 57 | /** 58 | * Called by the given serializer when performing custom deserialization. 59 | * 60 | * Params: 61 | * serializer = the serializer that performs the deserialization 62 | * key = the key of the receiver 63 | */ 64 | void fromData (Serializer serializer, Serializer.Data key); 65 | } 66 | 67 | /** 68 | * This interface represents a type that this is serializable. To implement this interface 69 | * Evaluates to $(D_KEYWORD true) if the given type is serializable. A type is considered 70 | * serializable when it implements to two methods in the Serializable interface. 71 | * Note that the type does not have to implement the actual interface, i.e. it also works 72 | * for structs. 73 | * 74 | * Examples: 75 | * --- 76 | * struct Foo 77 | * { 78 | * int a; 79 | * 80 | * void toData (Serializer serializer, Serializer.Data key) 81 | * { 82 | * serializer.serialize(a, "b"); 83 | * } 84 | * 85 | * void fromData (Serializer serializer, Serializer.Data key) 86 | * { 87 | * a = serializer.deserialize!(int)("b"); 88 | * } 89 | * } 90 | * 91 | * static assert(isSerializable!(Foo)); 92 | * --- 93 | * 94 | * See_Also: Serializable 95 | */ 96 | template isSerializable (T) 97 | { 98 | enum isSerializable = is(T : Serializable) || ( 99 | is(typeof(T.toData(Serializer.init, Serializer.Data.init))) && 100 | is(typeof(T.fromData(Serializer.init, Serializer.Data.init)))); 101 | } 102 | 103 | /** 104 | * This template is used to indicate that one or several fields should not be 105 | * (de)serialized. If no fields or "this" is specified, it indicates that the whole 106 | * class/struct should not be (de)serialized. 107 | * 108 | * This template is used as a mixin. 109 | * 110 | * Examples: 111 | * --- 112 | * class Foo 113 | * { 114 | * int a; 115 | * int b; 116 | * 117 | * mixin NonSerialized!(b); // "b" will not be (de)serialized 118 | * } 119 | * 120 | * struct Bar 121 | * { 122 | * int a; 123 | * int b; 124 | * 125 | * mixin NonSerialized; // "Bar" will not be (de)serialized 126 | * } 127 | * --- 128 | */ 129 | template NonSerialized (Fields ...) 130 | { 131 | static if (Fields.length == 0) 132 | static enum __nonSerialized = ["this"[]]; 133 | 134 | else 135 | static enum __nonSerialized = toArray!(Fields)(); 136 | } 137 | 138 | /// Indicates that the declaration this attribute is attached to should not be (de)serialized. 139 | @attribute struct nonSerialized { } 140 | 141 | struct NonSerializedField (string name) 142 | { 143 | enum field = name; 144 | } 145 | 146 | /* 147 | * Converts a tuple of aliases to an array of strings containing the names of the given 148 | * aliases. 149 | * 150 | * Examples: 151 | * --- 152 | * int a; 153 | * int b; 154 | * 155 | * enum names = toArray!(a, b); 156 | * 157 | * static assert(names == ["a", "b"]); 158 | * --- 159 | * 160 | * Returns: an array containing the names of the given aliases 161 | */ 162 | template toArray (Args ...) 163 | { 164 | static string[] toArray () 165 | { 166 | string[] args; 167 | 168 | foreach (i, _ ; typeof(Args)) 169 | args ~= Args[i].stringof; 170 | 171 | return args; 172 | } 173 | } 174 | 175 | package: 176 | 177 | enum nonSerializedField = "__nonSerialized"; 178 | enum serializedField = "__serialized"; 179 | enum internalFields = [nonSerializedField[], onDeserializedField, onDeserializingField, onSerializedField, onSerializingField]; -------------------------------------------------------------------------------- /Makefile.win: -------------------------------------------------------------------------------- 1 | # Useage Examples 2 | # --------------- 3 | # 'make -f Makefile.win' make everything using dmd, standard build, 32-bit 4 | # 'make -f Makefile.win install PREFIX=InstallDir' same as above, but also install to directory InstallDir 5 | # 'make -f Makefile.win clean' delete last build output so you can build again 6 | # 7 | # Summary 8 | # ------- 9 | # This is a makefile for windows. Install MSYS first (for make & other tools), set your *System* Path variable to MSYS's bin directory (where make.exe), 10 | # so that you can run make from any where and your command shell will find it). Pass in variable assignments such as doing 'make MODEL=64' to mean build 11 | # for 64-bit machines. Here are all the possible variable assignments accessible at the command line: 12 | # 13 | # Variable Possible Values Default Value Description 14 | # ------------------------------------------------------------------------------ 15 | # DC dmd, gdc, gdmd dmd Which compiler to use. 16 | # VERSION standard, release, debug standard Build for debug or for release? 17 | # 'standard' means the default of the chosen compiler. 18 | # MODEL 32, 64 32 Build 32-bit or 64-bit code? 19 | # PREFIX (any empty directory) Install The directory to install the library and D includes to. 20 | # 21 | # Installation 22 | # ------------ 23 | # Either edit your compiler's default import & library paths (for dmd that's done in dmd2/windows/bin/sc.ini), or 24 | # copy directory & contents of InstallDir/include/d/orange to dmd2/src/orange (these are your imports for using orange in a D program), 25 | # and copy InstallDir/lib/orange.lib to dmd2/windows/lib/orange.lib (this is orange's code that you link with). When building your program 26 | # make sure to link with orange.lib. 27 | # 28 | # BUGS 29 | # ---- 30 | # 'make clean': 31 | # If you get: 32 | # rm: cannot lstat 'lib//32/orange/cor': Permission denied 33 | # make *** [clean] Error 1 34 | # Fix: open up msconfig (Start > Run > 'msconfig'), and enable the Application Experience service 35 | # 36 | # Doesn't work in some shells: 37 | # For instance, spawning a shell from GitHub's native windows app causes MSYS commands to not work. 38 | # Fix: start your shell, e.g. PowerShell, cmd.exe directly. 39 | 40 | LIBNAME=orange 41 | PREFIX?=Install 42 | DC?=dmd 43 | #Warning, unittests fail with VERSION=release 44 | VERSION?=standard 45 | LIBDIR=lib/$(MODEL) 46 | 47 | 48 | SRC=core/Attribute.d \ 49 | serialization/Events.d \ 50 | serialization/RegisterWrapper.d \ 51 | serialization/Serializable.d \ 52 | serialization/SerializationException.d \ 53 | serialization/Serializer.d \ 54 | serialization/_.d \ 55 | serialization/archives/Archive.d \ 56 | serialization/archives/XmlArchive.d \ 57 | serialization/archives/_.d \ 58 | util/CTFE.d \ 59 | util/Reflection.d \ 60 | util/Traits.d \ 61 | util/Use.d \ 62 | util/_.d \ 63 | util/collection/Array.d \ 64 | util/collection/_.d \ 65 | xml/PhobosXml.d \ 66 | xml/XmlDocument.d \ 67 | xml/_.d 68 | 69 | UNITTEST=tests/Array.d \ 70 | tests/AssociativeArray.d \ 71 | tests/AssociativeArrayReference.d \ 72 | tests/BaseClass.d \ 73 | tests/Custom.d \ 74 | tests/Enum.d \ 75 | tests/Event.d \ 76 | tests/Events.d \ 77 | tests/NonIntrusive.d \ 78 | tests/NonSerialized.d \ 79 | tests/Object.d \ 80 | tests/OverrideSerializer.d \ 81 | tests/Pointer.d \ 82 | tests/Primitive.d \ 83 | tests/Slice.d \ 84 | tests/String.d \ 85 | tests/Struct.d \ 86 | tests/Subclass.d \ 87 | tests/unittest.d \ 88 | tests/Util.d 89 | 90 | TEST=test/UnitTester.d 91 | 92 | ifdef MODEL 93 | DCFLAGS=-m$(MODEL) 94 | else 95 | DCFLAGS=-m32 96 | override MODEL=32 97 | endif 98 | 99 | ifeq ("$(VERSION)","release") 100 | DCFLAGS += -O 101 | else ifeq ("$(VERSION)","debug") 102 | DCFLAGS += -g 103 | endif 104 | 105 | ifeq ("$(DC)","dmd") 106 | LIBCOMMAND = $(DC) -lib $(DCFLAGS) -of$@ $^ 107 | else ifeq ("$(DC)","gdc") 108 | override DC=gdmd 109 | LIBCOMMAND = ar rcs $@ $^ 110 | else ifeq ("$(DC)","gdmd") 111 | LIBCOMMAND = ar rcs $@ $^ 112 | endif 113 | 114 | # Everything below this line should be fairly generic (with a few hard-coded things). 115 | 116 | OBJ=$(addsuffix .obj,$(addprefix $(LIBDIR)/$(LIBNAME)/,$(basename $(SRC)))) 117 | HEADER=$(addsuffix .di,$(addprefix import/$(LIBNAME)/,$(basename $(SRC)))) 118 | TARGET=$(LIBDIR)/$(LIBNAME).lib 119 | 120 | all: mkdirs $(TARGET) $(HEADER) 121 | 122 | mkdirs: 123 | mkdir -p $(addprefix $(LIBDIR)/$(LIBNAME)/, $(sort $(dir $(SRC)))) 124 | 125 | install: all 126 | @mkdir -p $(PREFIX)/lib $(PREFIX)/include/d 127 | cp $(TARGET) $(PREFIX)/lib/ 128 | cp -r import/$(LIBNAME) $(PREFIX)/include/d 129 | 130 | uninstall: 131 | rm -rf $(PREFIX)/include/d/$(LIBNAME) 132 | rm -f $(PREFIX)/lib/$(LIBNAME).lib 133 | @rmdir -p --ignore-fail-on-non-empty $(PREFIX)/lib $(PREFIX)/include/d 2>/dev/null || true 134 | 135 | unittest: cleanunittest 136 | $(DC) $(DCFLAGS) -unittest -ofunittest $(addprefix $(LIBNAME)/,$(SRC)) $(UNITTEST) $(LIBNAME)/$(TEST) 137 | .\unittest 138 | 139 | clean: cleanunittest 140 | rm -rf import/ lib/ 141 | 142 | cleanunittest: 143 | rm -f unittest.obj unittest 144 | 145 | $(TARGET): $(OBJ) 146 | $(LIBCOMMAND) 147 | 148 | $(LIBDIR)/$(LIBNAME)/%.obj: $(LIBNAME)/%.d 149 | $(DC) -c $(DCFLAGS) -of$@ $< 150 | 151 | import/$(LIBNAME)/%.di: $(LIBNAME)/%.d 152 | $(DC) -c -o- -Hf$@ $< 153 | -------------------------------------------------------------------------------- /tests/Primitive.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Primitive; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class H 18 | { 19 | bool bool_; 20 | byte byte_; 21 | //cdouble cdouble_; // currently not suppported by to!() 22 | //cent cent_; // currently not implemented but a reserved keyword 23 | //cfloat cfloat_; // currently not suppported by to!() 24 | char char_; 25 | //creal creal_; // currently not suppported by to!() 26 | dchar dchar_; 27 | double double_; 28 | float float_; 29 | //idouble idouble_; // currently not suppported by to!() 30 | //ifloat ifloat_; // currently not suppported by to!() 31 | int int_; 32 | //ireal ireal_; // currently not suppported by to!() 33 | long long_; 34 | real real_; 35 | short short_; 36 | ubyte ubyte_; 37 | //ucent ucent_; // currently not implemented but a reserved keyword 38 | uint uint_; 39 | ulong ulong_; 40 | ushort ushort_; 41 | wchar wchar_; 42 | 43 | override equals_t opEquals (Object other) 44 | { 45 | if (auto o = cast(H) other) 46 | { 47 | return bool_ == o.bool_ && 48 | byte_ == o.byte_ && 49 | //cdouble_ == o.cdouble_ && // currently not suppported by to!() 50 | //cent_ == o.cent_ && // currently not implemented but a reserved keyword 51 | //cfloat_ == o.cfloat_ && // currently not suppported by to!() 52 | char_ == o.char_ && 53 | //creal_ == o.creal_ && // currently not suppported by to!() 54 | dchar_ == o.dchar_ && 55 | double_ == o.double_ && 56 | float_ == o.float_ && 57 | //idouble_ == o.idouble_ && // currently not suppported by to!() 58 | //ifloat_ == o.ifloat_ && // currently not suppported by to!() 59 | int_ == o.int_ && 60 | //ireal_ == o.ireal_ && // currently not suppported by to!() 61 | long_ == o.long_ && 62 | real_ == o.real_ && 63 | short_ == o.short_ && 64 | ubyte_ == o.ubyte_ && 65 | //ucent_ == o.ucent_ && // currently not implemented but a reserved keyword 66 | uint_ == o.uint_ && 67 | ulong_ == o.ulong_ && 68 | ushort_ == o.ushort_ && 69 | wchar_ == o.wchar_; 70 | } 71 | 72 | return false; 73 | } 74 | } 75 | 76 | H h; 77 | 78 | unittest 79 | { 80 | archive = new XmlArchive!(char); 81 | serializer = new Serializer(archive); 82 | 83 | h = new H; 84 | h.bool_ = true; 85 | h.byte_ = 1; 86 | h.char_ = 'a'; 87 | //h.cdouble_ = 0.0 + 0.0 * 1.0i; // currently not supported by to!() 88 | //h.cfloat_ = 0.0f + 0.0f * 1.0i; // currently not supported by to!() 89 | //h.creal_ = 0.0 + 0.0 * 1.0i; // currently not supported by to!() 90 | h.dchar_ = 'b'; 91 | h.double_ = 0.0; 92 | h.float_ = 0.0f; 93 | //h.idouble_ = 0.0 * 1.0i; // currently not supported by to!() 94 | //h.ifloat_ = 0.0f * 1.0i; // currently not supported by to!() 95 | h.int_ = 1; 96 | //h.ireal_ = 0.0 * 1.0i; // currently not supported by to!() 97 | h.long_ = 1L; 98 | h.real_ = 0.0; 99 | h.short_ = 1; 100 | h.ubyte_ = 1U; 101 | h.uint_ = 1U; 102 | h.ulong_ = 1LU; 103 | h.ushort_ = 1U; 104 | h.wchar_ = 'c'; 105 | 106 | enum zero = "0x0p+0"; 107 | 108 | describe("serialize primitives") in { 109 | it("should return serialized primitives") in { 110 | auto expected = q"xml 111 | 112 | 113 | 114 | 115 | true 116 | 1 117 | a 118 | b 119 | 0x0p+0 120 | 0x0p+0 121 | 1 122 | 1 123 | 0x0p+0 124 | 1 125 | 1 126 | 1 127 | 1 128 | 1 129 | c 130 | 131 | 132 | 133 | xml"; 134 | serializer.reset; 135 | serializer.serialize(h); 136 | 137 | assert(expected.equalToXml(archive.data)); 138 | }; 139 | }; 140 | 141 | describe("deserialize primitives") in { 142 | it("should return deserialized primitives equal to the original primitives") in { 143 | auto hDeserialized = serializer.deserialize!(H)(archive.untypedData); 144 | assert(h == hDeserialized); 145 | }; 146 | }; 147 | } 148 | -------------------------------------------------------------------------------- /orange/serialization/RegisterWrapper.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Feb 4, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.serialization.RegisterWrapper; 8 | 9 | import orange.serialization.Serializer; 10 | 11 | /** 12 | * This class is the base of all register wrappers. A register wrapper wraps a function 13 | * that is registered with the serializer. The serializer calls this function to perform 14 | * custom (de)serialization when needed. 15 | */ 16 | class RegisterBase { } 17 | 18 | /** 19 | * This class wraps registered functions for serialization. 20 | * 21 | * Params: 22 | * T = the type of the class or struct which is serialized 23 | */ 24 | class SerializeRegisterWrapper (T) : RegisterBase 25 | { 26 | private void delegate (T, Serializer, Serializer.Data) dg; 27 | private bool isDelegate; 28 | 29 | /** 30 | * Creates a new instance of this class with the given delegate that performs the 31 | * custom serialization. 32 | * 33 | * 34 | * Params: 35 | * dg = the delegate to call when performing custom serialization 36 | */ 37 | this (void delegate (T, Serializer, Serializer.Data) dg) 38 | { 39 | isDelegate = true; 40 | this.dg = dg; 41 | } 42 | 43 | /** 44 | * Creates a new instance of this class with the given function that performs the 45 | * custom serialization. 46 | * 47 | * 48 | * Params: 49 | * func = the delegate to call when performing custom serialization 50 | */ 51 | this (void function (T, Serializer, Serializer.Data) func) 52 | { 53 | dg.funcptr = func; 54 | } 55 | 56 | /** 57 | * Calls the function to perform the custom serialization. 58 | * 59 | * Params: 60 | * value = the instance that is to be serialized 61 | * serializer = the serializer that performs the serialization 62 | * key = the key of the given value 63 | */ 64 | void opCall (T value, Serializer serializer, Serializer.Data key) 65 | { 66 | if (dg && isDelegate) 67 | dg(value, serializer, key); 68 | 69 | else if (dg) 70 | dg.funcptr(value, serializer, key); 71 | } 72 | } 73 | 74 | /// Ditto 75 | class SerializeRegisterWrapperRef (T) : RegisterBase 76 | { 77 | private void delegate (ref T, Serializer, Serializer.Data) dg; 78 | private bool isDelegate; 79 | 80 | /** 81 | * Creates a new instance of this class with the given delegate that performs the 82 | * custom serialization. 83 | * 84 | * 85 | * Params: 86 | * dg = the delegate to call when performing custom serialization 87 | */ 88 | this (void delegate (ref T, Serializer, Serializer.Data) dg) 89 | { 90 | isDelegate = true; 91 | this.dg = dg; 92 | } 93 | 94 | /** 95 | * Creates a new instance of this class with the given function that performs the 96 | * custom serialization. 97 | * 98 | * 99 | * Params: 100 | * func = the delegate to call when performing custom serialization 101 | */ 102 | this (void function (ref T, Serializer, Serializer.Data) func) 103 | { 104 | dg.funcptr = func; 105 | } 106 | 107 | /** 108 | * Calls the function to perform the custom serialization. 109 | * 110 | * Params: 111 | * value = the instance that is to be serialized 112 | * serializer = the serializer that performs the serialization 113 | * key = the key of the given value 114 | */ 115 | void opCall (ref T value, Serializer serializer, Serializer.Data key) 116 | { 117 | if (dg && isDelegate) 118 | dg(value, serializer, key); 119 | 120 | else if (dg) 121 | dg.funcptr(value, serializer, key); 122 | } 123 | } 124 | 125 | /** 126 | * This class wraps registered functions for deserialization. 127 | * 128 | * Params: 129 | * T = the type of the class or struct which is deserialized 130 | */ 131 | class DeserializeRegisterWrapper (T) : RegisterBase 132 | { 133 | private void delegate (ref T, Serializer, Serializer.Data) dg; 134 | private bool isDelegate; 135 | 136 | /** 137 | * Creates a new instance of this class with the given delegate that performs the 138 | * custom deserialization. 139 | * 140 | * 141 | * Params: 142 | * dg = the delegate to call when performing custom serialization 143 | */ 144 | this (void delegate (ref T, Serializer, Serializer.Data) dg) 145 | { 146 | isDelegate = true; 147 | this.dg = dg; 148 | } 149 | 150 | /** 151 | * Creates a new instance of this class with the given function that performs the 152 | * custom serialization. 153 | * 154 | * 155 | * Params: 156 | * func = the delegate to call when performing custom serialization 157 | */ 158 | this (void function (ref T, Serializer, Serializer.Data) func) 159 | { 160 | dg.funcptr = func; 161 | } 162 | 163 | /** 164 | * Calls the function to perform the custom deserialization. 165 | * 166 | * Params: 167 | * value = the instance that is to be deserialized 168 | * serializer = the serializer that performs the deserialization 169 | * key = the key of the given value 170 | */ 171 | void opCall (ref T value, Serializer serializer, Serializer.Data key) 172 | { 173 | if (dg && isDelegate) 174 | dg(value, serializer, key); 175 | 176 | if (dg) 177 | dg.funcptr(value, serializer, key); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /orange/util/Reflection.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2009-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Oct 5, 2009 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.util.Reflection; 8 | 9 | import orange.util.CTFE; 10 | 11 | /** 12 | * Evaluates to true if T has a field with the given name 13 | * 14 | * Params: 15 | * T = the type of the class/struct 16 | * field = the name of the field 17 | */ 18 | template hasField (T, string field) 19 | { 20 | enum hasField = hasFieldImpl!(T, field, 0); 21 | } 22 | 23 | private template hasFieldImpl (T, string field, size_t i) 24 | { 25 | static if (T.tupleof.length == i) 26 | enum hasFieldImpl = false; 27 | 28 | else static if (nameOfFieldAt!(T, i) == field) 29 | enum hasFieldImpl = true; 30 | 31 | else 32 | enum hasFieldImpl = hasFieldImpl!(T, field, i + 1); 33 | } 34 | 35 | /** 36 | * Evaluates to an array of strings containing the names of the fields in the given type 37 | */ 38 | template fieldsOf (T) 39 | { 40 | enum fieldsOf = fieldsOfImpl!(T, 0); 41 | } 42 | 43 | /** 44 | * Implementation for fieldsOf 45 | * 46 | * Returns: an array of strings containing the names of the fields in the given type 47 | */ 48 | template fieldsOfImpl (T, size_t i) 49 | { 50 | static if (T.tupleof.length == 0) 51 | enum fieldsOfImpl = [""]; 52 | 53 | else static if (T.tupleof.length - 1 == i) 54 | enum fieldsOfImpl = [nameOfFieldAt!(T, i)]; 55 | 56 | else 57 | enum fieldsOfImpl = nameOfFieldAt!(T, i) ~ fieldsOfImpl!(T, i + 1); 58 | } 59 | 60 | /** 61 | * Evaluates to the type of the field with the given name 62 | * 63 | * Params: 64 | * T = the type of the class/struct 65 | * field = the name of the field 66 | */ 67 | template TypeOfField (T, string field) 68 | { 69 | static assert(hasField!(T, field), "The given field \"" ~ field ~ "\" doesn't exist in the type \"" ~ T.stringof ~ "\""); 70 | 71 | alias TypeOfFieldImpl!(T, field, 0) TypeOfField; 72 | } 73 | 74 | private template TypeOfFieldImpl (T, string field, size_t i) 75 | { 76 | static if (nameOfFieldAt!(T, i) == field) 77 | alias typeof(T.tupleof[i]) TypeOfFieldImpl; 78 | 79 | else 80 | alias TypeOfFieldImpl!(T, field, i + 1) TypeOfFieldImpl; 81 | } 82 | 83 | /** 84 | * Evaluates to a string containing the name of the field at given position in the given type. 85 | * 86 | * Params: 87 | * T = the type of the class/struct 88 | * position = the position of the field in the tupleof array 89 | */ 90 | template nameOfFieldAt (T, size_t position) 91 | { 92 | static assert (position < T.tupleof.length, format!(`The given position "`, position, `" is greater than the number of fields (`, T.tupleof.length, `) in the type "`, T, `"`)); 93 | 94 | enum nameOfFieldAt = __traits(identifier, T.tupleof[position]); 95 | } 96 | 97 | /** 98 | * Sets the given value to the filed with the given name 99 | * 100 | * Params: 101 | * t = an instance of the type that has the field 102 | * value = the value to set 103 | */ 104 | void setValueOfField (T, U, string field) (ref T t, U value) 105 | in 106 | { 107 | static assert(hasField!(T, field), "The given field \"" ~ field ~ "\" doesn't exist in the type \"" ~ T.stringof ~ "\""); 108 | } 109 | body 110 | { 111 | enum len = T.stringof.length; 112 | 113 | foreach (i, dummy ; typeof(T.tupleof)) 114 | { 115 | static if (nameOfFieldAt!(T, i) == field) 116 | { 117 | t.tupleof[i] = value; 118 | break; 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Gets the value of the field with the given name 125 | * 126 | * Params: 127 | * t = an instance of the type that has the field 128 | * 129 | * Returns: the value of the field 130 | */ 131 | U getValueOfField (T, U, string field) (T t) 132 | in 133 | { 134 | static assert(hasField!(T, field), "The given field \"" ~ field ~ "\" doesn't exist in the type \"" ~ T.stringof ~ "\""); 135 | } 136 | body 137 | { 138 | enum len = T.stringof.length; 139 | 140 | foreach (i, dummy ; typeof(T.tupleof)) 141 | { 142 | static if (nameOfFieldAt!(T, i) == field) 143 | return t.tupleof[i]; 144 | } 145 | 146 | assert(0); 147 | } 148 | 149 | private 150 | { 151 | version (LDC) 152 | extern (C) Object _d_allocclass(in ClassInfo); 153 | 154 | else 155 | extern (C) Object _d_newclass(in ClassInfo); 156 | } 157 | 158 | /** 159 | * Returns a new instnace of the class associated with the given class info. 160 | * 161 | * Params: 162 | * classInfo = the class info associated with the class 163 | * 164 | * Returns: a new instnace of the class associated with the given class info. 165 | */ 166 | Object newInstance (in ClassInfo classInfo) 167 | { 168 | version (LDC) 169 | { 170 | Object object = _d_allocclass(classInfo); 171 | const(void)[]defInitializer = classInfo.initializer(); 172 | if ((defInitializer !is null) && (defInitializer.length > 0)) { 173 | if (defInitializer.ptr !is null) 174 | (cast(byte*) object)[0..defInitializer.length] = (cast(byte*)defInitializer.ptr)[0..defInitializer.length]; 175 | else 176 | (cast(byte*) object)[0..defInitializer.length] = 0; 177 | } 178 | return object; 179 | } 180 | 181 | else 182 | return _d_newclass(classInfo); 183 | } 184 | 185 | /** 186 | * Return a new instance of the class with the given name. 187 | * 188 | * Params: 189 | * name = the fully qualified name of the class 190 | * 191 | * Returns: a new instance or null if the class name could not be found 192 | */ 193 | Object newInstance (string name) 194 | { 195 | auto classInfo = ClassInfo.find(name); 196 | 197 | if (!classInfo) 198 | return null; 199 | 200 | return newInstance(classInfo); 201 | } -------------------------------------------------------------------------------- /orange/util/Traits.d: -------------------------------------------------------------------------------- 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.util.Traits; 8 | 9 | import orange.serialization.Serializable; 10 | import orange.serialization.archives.Archive; 11 | import orange.util._; 12 | 13 | 14 | import Phobos = std.traits; 15 | 16 | /// 17 | alias Phobos.BaseTypeTuple BaseTypeTupleOf; 18 | 19 | /// Evaluates to true if $(D_PARAM T) is a primitive type. 20 | template isPrimitive (T) 21 | { 22 | enum bool isPrimitive = is(T == bool) || 23 | is(T == byte) || 24 | is(T == cdouble) || 25 | //is(T == cent) || 26 | is(T == cfloat) || 27 | is(T == char) || 28 | is(T == creal) || 29 | is(T == dchar) || 30 | is(T == double) || 31 | is(T == float) || 32 | is(T == idouble) || 33 | is(T == ifloat) || 34 | is(T == int) || 35 | is(T == ireal) || 36 | is(T == long) || 37 | is(T == real) || 38 | is(T == short) || 39 | is(T == ubyte) || 40 | //is(T == ucent) || 41 | is(T == uint) || 42 | is(T == ulong) || 43 | is(T == ushort) || 44 | is(T == wchar); 45 | } 46 | 47 | /// Evaluates to true if $(D_PARAM T) is a floating point type. 48 | template isFloatingPoint (T) 49 | { 50 | enum bool isFloatingPoint = is(T == float) || is(T == double) || is(T == real) || 51 | is(T == cfloat) || is(T == cdouble) || is(T == creal) || 52 | is(T == ifloat) || is(T == idouble) || is(T == ireal); 53 | } 54 | 55 | /// Evaluates to true if $(D_PARAM T) is class. 56 | template isClass (T) 57 | { 58 | enum bool isClass = is(T == class); 59 | } 60 | 61 | /// Evaluates to true if $(D_PARAM T) is an interface. 62 | template isInterface (T) 63 | { 64 | enum bool isInterface = is(T == interface); 65 | } 66 | 67 | /// Evaluates to true if $(D_PARAM T) is a class or an interface. 68 | template isObject (T) 69 | { 70 | enum bool isObject = isClass!(T) || isInterface!(T); 71 | } 72 | 73 | /// Evaluates to true if $(D_PARAM T) is a struct. 74 | template isStruct (T) 75 | { 76 | enum bool isStruct = is(T == struct); 77 | } 78 | 79 | /// Evaluates to true if $(D_PARAM T) is an array. 80 | template isArray (T) 81 | { 82 | static if (is(T U : U[])) 83 | enum bool isArray = true; 84 | 85 | else 86 | enum bool isArray = false; 87 | } 88 | 89 | /// Evaluates to true if $(D_PARAM T) is a static array. 90 | enum isStaticArray (T) = is(T : U[n], U, size_t n); 91 | 92 | /// Evaluates to true if $(D_PARAM T) is a string. 93 | template isString (T) 94 | { 95 | enum bool isString = is(T : string) || is(T : wstring) || is(T : dstring); 96 | } 97 | 98 | /// Evaluates to true if $(D_PARAM T) is a an associative array. 99 | template isAssociativeArray (T) 100 | { 101 | enum bool isAssociativeArray = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T); 102 | } 103 | 104 | /// Evaluates to true if $(D_PARAM T) is a pointer. 105 | template isPointer (T) 106 | { 107 | static if (is(T U : U*)) 108 | enum bool isPointer = true; 109 | 110 | else 111 | enum bool isPointer = false; 112 | } 113 | 114 | /// Evaluates to true if $(D_PARAM T) is a function pointer. 115 | template isFunctionPointer (T) 116 | { 117 | enum bool isFunctionPointer = is(typeof(*T) == function); 118 | } 119 | 120 | /// Evaluates to true if $(D_PARAM T) is an enum. 121 | template isEnum (T) 122 | { 123 | enum bool isEnum = is(T == enum); 124 | } 125 | 126 | /// Evaluates to true if $(D_PARAM T) is an object or a pointer. 127 | template isReference (T) 128 | { 129 | enum bool isReference = isObject!(T) || isPointer!(T); 130 | } 131 | 132 | /// Evaluates to true if $(D_PARAM T) is void. 133 | template isVoid (T) 134 | { 135 | enum bool isVoid = is(T == void); 136 | } 137 | 138 | /// Evaluates the type of the element of the array. 139 | template ElementTypeOfArray(T : T[]) 140 | { 141 | alias T ElementTypeOfArray; 142 | } 143 | 144 | /// Evaluates to the type the pointer points to. 145 | template BaseTypeOfPointer (T) 146 | { 147 | static if (is(T U : U*)) 148 | alias BaseTypeOfPointer!(U) BaseTypeOfPointer; 149 | 150 | else 151 | alias T BaseTypeOfPointer; 152 | } 153 | 154 | /// Evaluates to the base type of the enum. 155 | template BaseTypeOfEnum (T) 156 | { 157 | static if (is(T U == enum)) 158 | alias BaseTypeOfEnum!(U) BaseTypeOfEnum; 159 | 160 | else 161 | alias T BaseTypeOfEnum; 162 | } 163 | 164 | /// Evaluates to the key type of the associative array. 165 | template KeyTypeOfAssociativeArray (T) 166 | { 167 | static assert(isAssociativeArray!(Unqual!(T)), "The type needs to be an associative array"); 168 | alias typeof(T.init.keys[0]) KeyTypeOfAssociativeArray; 169 | } 170 | 171 | /// Evaluates to the value type of the associative array. 172 | template ValueTypeOfAssociativeArray (T) 173 | { 174 | static assert(isAssociativeArray!(Unqual!(T)), "The type needs to be an associative array"); 175 | alias typeof(T.init.values[0]) ValueTypeOfAssociativeArray; 176 | } 177 | 178 | /// Evaluates to the type of the data type. 179 | template TypeOfDataType (T) 180 | { 181 | alias T.DataType TypeOfDataType; 182 | } 183 | 184 | /// Unqualifies the given type, i.e. removing const, immutable and so on. 185 | alias Phobos.Unqual Unqual; 186 | 187 | /// Evaluates to true if the given symbol is a type. 188 | template isType (alias symbol) 189 | { 190 | enum isType = __traits(compiles, expectType!(symbol)); 191 | } 192 | 193 | private template expectType (T) {} 194 | 195 | /** 196 | * Evaluates to the type of the given expression or type. The built-in $(D_KEYWORD typeof) 197 | * only accepts expressions, not types. If given a type, this will just evaluate to the given 198 | * type as is. 199 | */ 200 | template TypeOf (alias expr) 201 | { 202 | static if (isType!(expr)) 203 | alias expr TypeOf; 204 | 205 | else 206 | alias typeof(expr) TypeOf; 207 | } 208 | 209 | /// Evaluates to true if the given argument is a symbol. 210 | template isSymbol (alias arg) 211 | { 212 | enum isSymbol = __traits(compiles, __traits(getAttributes, arg)); 213 | } 214 | -------------------------------------------------------------------------------- /tests/Slice.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Aug 6, 2011 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module tests.Slice; 8 | 9 | import orange.serialization.Serializer; 10 | import orange.serialization.archives.XmlArchive; 11 | import orange.test.UnitTester; 12 | import tests.Util; 13 | 14 | Serializer serializer; 15 | XmlArchive!(char) archive; 16 | 17 | class J 18 | { 19 | int[] firstSource; 20 | int[] firstSlice; 21 | 22 | int[] secondSlice; 23 | int[] secondSource; 24 | 25 | int[4] firstStaticSource; 26 | int[] firstStaticSlice; 27 | 28 | int[] firstEmpty; 29 | int[] secondEmpty; 30 | 31 | int[][] thirdEmpty; 32 | } 33 | 34 | J j; 35 | J jDeserialized; 36 | 37 | unittest 38 | { 39 | archive = new XmlArchive!(char); 40 | serializer = new Serializer(archive); 41 | 42 | j = new J; 43 | j.firstSource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].dup; 44 | j.firstSlice = j.firstSource[3 .. 7]; 45 | 46 | j.secondSource = [10, 11, 12, 13, 14, 15].dup; 47 | j.secondSlice = j.secondSource[1 .. 4]; 48 | 49 | j.firstStaticSource = [16, 17, 18, 19]; 50 | j.firstStaticSlice = j.firstStaticSource[1 .. 3]; 51 | 52 | describe("serialize slices") in { 53 | it("should return serialized slices") in { 54 | auto expected2066 = q"xml 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 21 63 | 64 | 16 65 | 17 66 | 18 67 | 19 68 | 69 | 70 | 0 71 | 1 72 | 2 73 | 3 74 | 4 75 | 5 76 | 6 77 | 7 78 | 8 79 | 9 80 | 81 | 82 | 10 83 | 11 84 | 12 85 | 13 86 | 14 87 | 15 88 | 89 | 28 90 | 1 91 | 92 | 93 | 94 | xml"; 95 | auto expected2067 = q"xml 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 10 105 | 11 106 | 12 107 | 13 108 | 14 109 | 15 110 | 111 | 1 112 | 21 113 | 114 | 16 115 | 17 116 | 18 117 | 19 118 | 119 | 28 120 | 121 | 0 122 | 1 123 | 2 124 | 3 125 | 4 126 | 5 127 | 6 128 | 7 129 | 8 130 | 9 131 | 132 | 133 | 134 | 135 | xml"; 136 | 137 | static if (__VERSION__ >= 2067) auto expected = expected2067; 138 | else auto expected = expected2066; 139 | 140 | serializer.reset(); 141 | serializer.serialize(j); 142 | 143 | assert(expected.equalToXml(archive.data)); 144 | }; 145 | }; 146 | 147 | describe("deserialize slices") in { 148 | jDeserialized = serializer.deserialize!(J)(archive.untypedData); 149 | 150 | it("should return deserialized strings equal to the original strings") in { 151 | assert(j.firstSource == jDeserialized.firstSource); 152 | assert(j.secondSource == jDeserialized.secondSource); 153 | }; 154 | 155 | it("should return deserialized slices equal to the original slices") in { 156 | assert(j.firstSlice == jDeserialized.firstSlice); 157 | assert(j.secondSlice == jDeserialized.secondSlice); 158 | }; 159 | 160 | it("the slices should be equal to a slice of the original sources") in { 161 | assert(jDeserialized.firstSource[3 .. 7] == jDeserialized.firstSlice); 162 | assert(jDeserialized.secondSource[1 .. 4] == jDeserialized.secondSlice); 163 | assert(jDeserialized.firstStaticSource[1 .. 3] == jDeserialized.firstStaticSlice); 164 | 165 | assert(j.firstSource[3 .. 7] == jDeserialized.firstSlice); 166 | assert(j.secondSource[1 .. 4] == jDeserialized.secondSlice); 167 | assert(j.firstStaticSource[1 .. 3] == jDeserialized.firstStaticSlice); 168 | }; 169 | 170 | it("the slices should be able to modify the sources") in { 171 | jDeserialized.firstSlice[0] = 55; 172 | jDeserialized.secondSlice[0] = 3; 173 | jDeserialized.firstStaticSlice[0] = 1; 174 | 175 | assert(jDeserialized.firstSource == [0, 1, 2, 55, 4, 5, 6, 7, 8, 9]); 176 | assert(jDeserialized.secondSource == [10, 3, 12, 13, 14, 15]); 177 | assert(jDeserialized.firstStaticSource == [16, 1, 18, 19]); 178 | }; 179 | }; 180 | } 181 | -------------------------------------------------------------------------------- /orange/xml/XmlDocument.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Jun 26, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.xml.XmlDocument; 8 | 9 | import std.string; 10 | import std.stdio; 11 | 12 | import orange.xml.PhobosXml; 13 | 14 | /// This class represents an exception thrown by XmlDocument. 15 | class XMLException : Exception 16 | { 17 | this (string message, string file = null, size_t line = 0) 18 | { 19 | super(message, file, line); 20 | } 21 | } 22 | 23 | /** 24 | * This class represents an XML DOM document. It provides a common interface to the XML 25 | * document implementations available in Phobos and Tango. 26 | */ 27 | final class XmlDocument 28 | { 29 | /// The type of the document implementation. 30 | alias Document Doc; 31 | 32 | /// The type of the node implementation. 33 | alias Element InternalNode; 34 | 35 | /// The type of the query node implementation. 36 | alias Element QueryNode; 37 | 38 | /// The type of the visitor type implementation. 39 | alias Element[] VisitorType; 40 | 41 | /// foreach support for visiting a set of nodes. 42 | struct VisitorProxy 43 | { 44 | private VisitorType nodes; 45 | 46 | private static VisitorProxy opCall (VisitorType nodes) 47 | { 48 | VisitorProxy vp; 49 | vp.nodes = nodes; 50 | 51 | return vp; 52 | } 53 | 54 | /** 55 | * Returns true if this proxy contains any nodes. 56 | * 57 | * Examples: 58 | * --- 59 | * VisitorProxy proxy; 60 | * assert(proxy.exist == false); 61 | * --- 62 | * 63 | * Returns: true if this proxy contains any nodes. 64 | */ 65 | bool exist () 66 | { 67 | return nodes.length > 0; 68 | } 69 | 70 | /** 71 | * Allows to iterate over the set of nodes. 72 | * 73 | * Examples: 74 | * --- 75 | * VisitorProxy proxy 76 | * foreach (node ; proxy) {} 77 | * --- 78 | */ 79 | int opApply (int delegate (ref Node) dg) 80 | { 81 | int result; 82 | 83 | foreach (n ; nodes) 84 | { 85 | auto p = Node(n); 86 | result = dg(p); 87 | 88 | if (result) 89 | break; 90 | } 91 | 92 | return result; 93 | } 94 | } 95 | 96 | /// A generic document node 97 | struct Node 98 | { 99 | private InternalNode node; 100 | private bool shouldAddToDoc = true; 101 | private bool isRoot = true; 102 | 103 | private static Node opCall (InternalNode node, bool shouldAddToDoc = false, bool isRoot = false) 104 | { 105 | Node proxy; 106 | proxy.node = node; 107 | proxy.shouldAddToDoc = shouldAddToDoc; 108 | proxy.isRoot = isRoot; 109 | 110 | return proxy; 111 | } 112 | 113 | /** 114 | * Returns an invalid node. 115 | * 116 | * Examples: 117 | * --- 118 | * auto node = Node.invalid; 119 | * assert(node.isValid == false); 120 | * --- 121 | * 122 | * Returns: an invalid node 123 | */ 124 | public static Node invalid () 125 | { 126 | return Node(null); 127 | } 128 | 129 | /// Returns the name of the node. 130 | string name () 131 | { 132 | return node.name; 133 | } 134 | 135 | /// Returns the value of the node. 136 | string value () 137 | { 138 | return node.value; 139 | } 140 | 141 | /// Returns the parent node. 142 | Node parent () 143 | { 144 | return Node(node.parent); 145 | } 146 | 147 | /** 148 | * Returns true if the receiver is valid. 149 | * 150 | * auto node = Node.invalid; 151 | * assert(node.isValid == false); 152 | * 153 | * Returns: true if the receiver is valid. 154 | * 155 | * See_Also: invalid 156 | */ 157 | bool isValid () 158 | { 159 | return node !is null; 160 | } 161 | 162 | /// Returns a foreach iterator for node children. 163 | VisitorProxy children () 164 | { 165 | return VisitorProxy(node.children); 166 | } 167 | 168 | /// Returns a foreach iterator for node attributes. 169 | VisitorProxy attributes () 170 | { 171 | return VisitorProxy(cast(VisitorType) node.attributes); 172 | } 173 | 174 | /// Return an XPath handle to query the receiver. 175 | QueryProxy query () 176 | { 177 | return QueryProxy(node.query); 178 | } 179 | 180 | /** 181 | * Creates a new element and attaches it to the receiver. 182 | * 183 | * Params: 184 | * name = the name of the element 185 | * value = the value of the element 186 | * 187 | * Returns: the newly create element. 188 | */ 189 | Node element (string name, string value = null) 190 | { 191 | auto element = new Element(name, value); 192 | 193 | if (isRoot) 194 | { 195 | node.tag = element.tag; 196 | node ~= new Text(value); 197 | 198 | return Node(node, true, false); 199 | } 200 | 201 | else 202 | { 203 | if (shouldAddToDoc) 204 | { 205 | shouldAddToDoc = false; 206 | node ~= element; 207 | } 208 | 209 | else 210 | node ~= element; 211 | 212 | return Node(element, shouldAddToDoc, false); 213 | } 214 | } 215 | 216 | /** 217 | * Creates a new attribute and attaches it to the receiver. 218 | * 219 | * Params: 220 | * name = the name of the attribute 221 | * value = the value of the attribute 222 | * 223 | * Returns: the newly created attribute 224 | */ 225 | Node attribute (string name, string value) 226 | { 227 | node.attribute(null, name, value); 228 | 229 | return this; 230 | } 231 | 232 | /** 233 | * Attach an already existing node to the receiver. 234 | * 235 | * Params: 236 | * node = the node to attach. 237 | */ 238 | void attach (Node node) 239 | { 240 | if (this.node !is node.node.parent) 241 | { 242 | node.node.parent = this.node; 243 | this.node ~= node.node; 244 | } 245 | } 246 | } 247 | 248 | /// This an XPath query handle used to perform queries on a set of elements. 249 | struct QueryProxy 250 | { 251 | private Node[] nodes_; 252 | 253 | private static QueryProxy opCall (QueryNode node) 254 | { 255 | QueryProxy qp; 256 | 257 | qp.nodes_ = [Node(node)]; 258 | 259 | return qp; 260 | } 261 | 262 | private static QueryProxy opCall (Node[] nodes) 263 | { 264 | QueryProxy qp; 265 | qp.nodes_ = nodes; 266 | 267 | return qp; 268 | } 269 | 270 | /** 271 | * Returns a set containing all attribute nodes of the nodes within this set which pass 272 | * the given filtering test. 273 | * 274 | * Params: 275 | * filter = the filter to be applied on the attributes. Should return true when there 276 | * is a match for an attribute. 277 | * 278 | * Returns: the set of nodes that passed the filter test 279 | */ 280 | QueryProxy attribute (bool delegate (Node) filter) 281 | { 282 | Node[] nodes; 283 | 284 | foreach (node ; nodes_) 285 | { 286 | foreach (attr ; node.attributes.nodes) 287 | { 288 | auto n = Node(attr); 289 | 290 | if (filter && filter(n)) 291 | nodes ~= n; 292 | } 293 | } 294 | 295 | return QueryProxy(nodes); 296 | } 297 | 298 | /** 299 | * Return a set containing all attributes of the nodes within this set, which match 300 | * the given name. 301 | * 302 | * Params: 303 | * name = the name of the attribute to filter on 304 | * 305 | * Returns: a set of elements that passed the filter test 306 | */ 307 | QueryProxy attribute (string name = null) 308 | { 309 | bool filter (Node node) 310 | { 311 | return node.name == name; 312 | } 313 | 314 | bool always (Node node) 315 | { 316 | return true; 317 | } 318 | 319 | if (name.length > 0) 320 | return attribute(&filter); 321 | 322 | return attribute(&always); 323 | } 324 | 325 | /// Returns an array of all the nodes stored in the receiver. 326 | Node[] nodes () 327 | { 328 | return nodes_; 329 | } 330 | 331 | /** 332 | * Returns a set containing all child elements of the nodes within this set, which 333 | * match the given name. 334 | * 335 | * Params: 336 | * name = the name to filter on. 337 | * 338 | * Returns: a set of elements that passed the filter test 339 | */ 340 | QueryProxy opIndex (string name) 341 | { 342 | Node[] proxies; 343 | 344 | foreach (parent ; nodes_) 345 | { 346 | foreach (e ; parent.node.elements) 347 | { 348 | if (e.tag.name == name) 349 | proxies ~= Node(e); 350 | } 351 | } 352 | 353 | return QueryProxy(proxies); 354 | } 355 | 356 | /** 357 | * Iterates over the set of nodes. 358 | * 359 | * Examples: 360 | * --- 361 | * foreach (node ; nodes) {} 362 | * --- 363 | */ 364 | int opApply (int delegate (ref Node) dg) 365 | { 366 | auto visitor = nodes_; 367 | 368 | int result; 369 | 370 | foreach (n ; visitor) 371 | if (dg(n)) 372 | break; 373 | 374 | return result; 375 | } 376 | } 377 | 378 | /// Set this to true if there should be strict errro checking. 379 | bool strictErrorChecking; 380 | 381 | /// The number of spaces used for indentation used when printing the document. 382 | uint indentation = 4; 383 | 384 | private Doc doc; 385 | InternalNode currentNode; 386 | 387 | /** 388 | * Creates a new instance of this class 389 | * 390 | * Examples: 391 | * --- 392 | * auto doc = new XmlDocument!(); 393 | * --- 394 | * 395 | * Params: 396 | * strictErrorChecking = true if strict errro checking should be enabled 397 | */ 398 | this (bool strictErrorChecking = true) 399 | { 400 | doc = new Doc(new Tag("root")); 401 | this.strictErrorChecking = strictErrorChecking; 402 | } 403 | 404 | /** 405 | * Attaches a header to the document. 406 | * 407 | * Examples: 408 | * --- 409 | * auto doc = new XmlDocument!(); 410 | * doc.header("UTF-8"); 411 | * // 412 | * --- 413 | * 414 | * Params: 415 | * encoding = the encoding that should be put in the header 416 | * 417 | * Returns: the receiver 418 | */ 419 | XmlDocument header (string encoding = null) 420 | { 421 | string newEncoding = encoding.length > 0 ? encoding : "UTF-8"; 422 | string header = ``; 423 | doc.prolog = header; 424 | 425 | return this; 426 | } 427 | 428 | /// Rests the reciver. Allows to parse new content. 429 | XmlDocument reset () 430 | { 431 | doc = new Doc(new Tag("root")); 432 | 433 | return this; 434 | } 435 | 436 | /// Return the root document node, from which all other nodes are descended. 437 | Node tree () 438 | { 439 | return Node(doc, true, true); 440 | } 441 | 442 | /** 443 | * Parses the given string of XML. 444 | * 445 | * Params: 446 | * xml = the XML to parse 447 | */ 448 | void parse (string xml) 449 | { 450 | auto tmp = new Doc(xml); 451 | doc = new Doc(new Tag("root")); 452 | doc.elements ~= tmp; 453 | } 454 | 455 | /// Return an xpath handle to query this document. This starts at the document root. 456 | QueryProxy query () 457 | { 458 | return QueryProxy(doc); 459 | } 460 | 461 | /// Pretty prints the document. 462 | override string toString () 463 | { 464 | return doc.prolog ~ "\n" ~ join(doc.pretty(indentation), "\n"); 465 | } 466 | 467 | /** 468 | * Attaches a new node to the docuement. 469 | * 470 | * Params: 471 | * name = the name of the node 472 | * value = the vale of the node 473 | * 474 | * Returns: returns the newly created node 475 | */ 476 | Node createNode (string name, string value = null) 477 | { 478 | return Node(new Element(name, value), false, false); 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /orange/test/UnitTester.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. All rights reserved. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Oct 17, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | * 7 | * This is a simple unit test framework inspired by rspec. This framework is used for 8 | * collecting unit test failures (assert exceptions) and presents them to the user in a 9 | * nice format. 10 | * 11 | * The following are features of how a test report is printed: 12 | * 13 | * $(UL 14 | * $(LI print the filename and line number of the failing test) 15 | * $(LI print the description of a failing or pending test) 16 | * $(LI print a snippet of the file around a failing test) 17 | * $(LI print the stack trace of a failing test) 18 | * $(LI print the number of failing, pending and passed test. 19 | * As well as the total number of tests) 20 | * $(LI minimal output then all tests pass) 21 | * ) 22 | * 23 | * If an assertion fails in a "it" block, that block will end. No other block is affected 24 | * by the failed assertion. 25 | * 26 | * Examples: 27 | * --- 28 | * import orange.test.UnitTester; 29 | * 30 | * int sum (int x, int y) 31 | * { 32 | * return x * y; 33 | * } 34 | * 35 | * unittest () 36 | * { 37 | * describe("sum") in { 38 | * it("should return the sum of the two given arguments") in { 39 | * assert(sum(1, 2) == 3); 40 | * } 41 | * } 42 | * } 43 | * 44 | * void main () 45 | * { 46 | * run; 47 | * } 48 | * --- 49 | * When the code above is run, it would print, since the test is failing, something similar: 50 | * --- 51 | * sum 52 | * - should return the sum of the given arguments 53 | * 54 | * Failures: 55 | * 1$(RPAREN) sum should return the sum of the given arguments 56 | * # main.d:44 57 | * Stack trace: 58 | * tango.core.Exception.AssertException@main(44): Assertion failure 59 | * 60 | * 61 | * describe("sum") in { 62 | * it("should return the sum of the given arguments") in { 63 | * assert(sum(1, 2) == 3); 64 | * }; 65 | * }; 66 | * 67 | * 1 test, 1 failure 68 | * --- 69 | */ 70 | module orange.test.UnitTester; 71 | 72 | import core.exception; 73 | import std.conv; 74 | import std.stdio; 75 | 76 | private alias AssertError AssertException; 77 | 78 | import orange.util._; 79 | 80 | /** 81 | * Describes a test or a set of tests. 82 | * 83 | * Examples: 84 | * --- 85 | * unittest () 86 | * { 87 | * describe("the description of the tests") in { 88 | * 89 | * }; 90 | * } 91 | * --- 92 | * 93 | * Params: 94 | * message = the message to describe the test 95 | * 96 | * Returns: a context in which the tests will be run 97 | */ 98 | Use!(void delegate (), string) describe (string message) 99 | { 100 | return UnitTester.instance.describe(message); 101 | } 102 | 103 | /** 104 | * Describes what a test should do. 105 | * 106 | * Examples: 107 | * --- 108 | * unittest () 109 | * { 110 | * describe("the description of the tests") in { 111 | * it("should do something") in { 112 | * // put your assert here 113 | * }; 114 | * 115 | * it("should do something else") in { 116 | * // put another assert here 117 | * } 118 | * }; 119 | * } 120 | * --- 121 | * 122 | * Params: 123 | * message = what the test should do 124 | * 125 | * Returns: a context in which the test will be run 126 | */ 127 | Use!(void delegate (), string) it (string message) 128 | { 129 | return UnitTester.instance.test(message); 130 | } 131 | 132 | /// A delegate that will be called before each test. 133 | void delegate () before () 134 | { 135 | return UnitTester.instance.before; 136 | } 137 | 138 | /// A delegate that will be called before each test. 139 | void delegate () before (void delegate () before) 140 | { 141 | return UnitTester.instance.before = before; 142 | } 143 | 144 | /// A delegate that will be called after each test. 145 | void delegate () after () 146 | { 147 | return UnitTester.instance.after; 148 | } 149 | 150 | /// A delegate that will be called after each test. 151 | void delegate () after (void delegate () after) 152 | { 153 | return UnitTester.instance.after = after; 154 | } 155 | 156 | /** 157 | * Runs all tests. 158 | * 159 | * Returns: `false` if any tests failed 160 | */ 161 | bool run () 162 | { 163 | return UnitTester.instance.run; 164 | } 165 | 166 | private: 167 | 168 | class UnitTester 169 | { 170 | private: 171 | 172 | struct DescriptionManager 173 | { 174 | Description[] descriptions; 175 | size_t lastIndex = size_t.max; 176 | 177 | void opOpAssign(string op : "~")(Description description) 178 | { 179 | descriptions ~= description; 180 | lastIndex++; 181 | } 182 | 183 | void opOpAssign(string op : "~")(Test test) 184 | { 185 | last.tests ~= test; 186 | } 187 | 188 | Description opIndex (size_t i) 189 | { 190 | return descriptions[i]; 191 | } 192 | 193 | Description last () 194 | { 195 | return descriptions[$ - 1]; 196 | } 197 | 198 | Description first () 199 | { 200 | return descriptions[0]; 201 | } 202 | 203 | int opApply (int delegate(ref Description) dg) 204 | { 205 | int result = 0; 206 | 207 | foreach (desc ; descriptions) 208 | { 209 | result = dg(desc); 210 | 211 | if (result) 212 | return result; 213 | } 214 | 215 | return result; 216 | } 217 | 218 | size_t length () 219 | { 220 | return descriptions.length; 221 | } 222 | } 223 | 224 | class Description 225 | { 226 | private 227 | { 228 | DescriptionManager descriptions; 229 | Test[] tests; 230 | Test[] failures; 231 | Test[] pending; 232 | size_t lastIndex = size_t.max; 233 | string message; 234 | void delegate () description; 235 | } 236 | 237 | this (string message) 238 | { 239 | this.message = message; 240 | } 241 | 242 | void run () 243 | { 244 | if (shouldRun) 245 | description(); 246 | } 247 | 248 | bool shouldRun () 249 | { 250 | return description !is null; 251 | } 252 | } 253 | 254 | struct Test 255 | { 256 | void delegate () test; 257 | string message; 258 | AssertException exception; 259 | 260 | bool failed () 261 | { 262 | return !succeeded; 263 | } 264 | 265 | bool succeeded () 266 | { 267 | if (exception is null) 268 | return true; 269 | 270 | return false; 271 | } 272 | 273 | void run () 274 | { 275 | if (!isPending) 276 | test(); 277 | } 278 | 279 | bool isPending () 280 | { 281 | return test is null; 282 | } 283 | } 284 | 285 | static UnitTester instance_; 286 | 287 | DescriptionManager descriptions; 288 | Description currentDescription; 289 | 290 | void delegate () before_; 291 | void delegate () after_; 292 | 293 | size_t numberOfFailures; 294 | size_t numberOfPending; 295 | size_t numberOfTests; 296 | size_t failureId; 297 | 298 | string defaultIndentation = " "; 299 | string indentation; 300 | 301 | static UnitTester instance () 302 | { 303 | if (instance_) 304 | return instance_; 305 | 306 | return instance_ = new UnitTester; 307 | } 308 | 309 | Use!(void delegate (), string) describe (string message) 310 | { 311 | addDescription(message); 312 | Use!(void delegate (), string) use; 313 | 314 | use.args[0] = &internalDescribe; 315 | use.args[1] = message; 316 | 317 | return use; 318 | } 319 | 320 | Use!(void delegate (), string) test (string message) 321 | { 322 | addTest(message); 323 | Use!(void delegate (), string) use; 324 | 325 | use.args[0] = &internalTest; 326 | use.args[1] = message; 327 | 328 | return use; 329 | } 330 | 331 | /** 332 | * Runs all tests. 333 | * 334 | * Returns: `false` if any tests failed 335 | */ 336 | bool run () 337 | { 338 | foreach (description ; descriptions) 339 | runDescription(description); 340 | 341 | printResult; 342 | 343 | return !hasFailures; 344 | } 345 | 346 | void runDescription (Description description) 347 | { 348 | restore(currentDescription) in { 349 | currentDescription = description; 350 | description.run; 351 | 352 | foreach (desc ; description.descriptions) 353 | runDescription(desc); 354 | 355 | foreach (test ; description.tests) 356 | { 357 | if (test.isPending) 358 | addPendingTest(description, test); 359 | 360 | try 361 | { 362 | execute in { 363 | test.run(); 364 | }; 365 | } 366 | 367 | catch (AssertException e) 368 | handleFailure(description, test, e); 369 | } 370 | }; 371 | } 372 | 373 | void delegate () before () 374 | { 375 | return before_; 376 | } 377 | 378 | void delegate () before (void delegate () before) 379 | { 380 | return before_ = before; 381 | } 382 | 383 | void delegate () after () 384 | { 385 | return after_; 386 | } 387 | 388 | void delegate () after (void delegate () after) 389 | { 390 | return after_ = after; 391 | } 392 | 393 | void addTest (string message) 394 | { 395 | numberOfTests++; 396 | currentDescription.tests ~= Test(null, message); 397 | } 398 | 399 | void addDescription (string message) 400 | { 401 | if (currentDescription) 402 | currentDescription.descriptions ~= new Description(message); 403 | 404 | else 405 | descriptions ~= new Description(message); 406 | } 407 | 408 | void addPendingTest (Description description, ref Test test) 409 | { 410 | numberOfPending++; 411 | description.pending ~= test; 412 | } 413 | 414 | void handleFailure (Description description, ref Test test, AssertException exception) 415 | { 416 | numberOfFailures++; 417 | test.exception = exception; 418 | description.failures ~= test; 419 | } 420 | 421 | void internalDescribe (void delegate () dg, string message) 422 | { 423 | if (currentDescription) 424 | currentDescription.descriptions.last.description = dg; 425 | 426 | else 427 | descriptions.last.description = dg; 428 | } 429 | 430 | void internalTest (void delegate () dg, string message) 431 | { 432 | currentDescription.tests[$ - 1] = Test(dg, message); 433 | } 434 | 435 | void printResult () 436 | { 437 | if (isAllTestsSuccessful) 438 | return printSuccess(); 439 | 440 | foreach (description ; descriptions) 441 | { 442 | printDescription(description); 443 | printResultImpl(description.descriptions); 444 | } 445 | 446 | failureId = 0; 447 | 448 | printPending; 449 | printFailures; 450 | 451 | write("\n", numberOfTests, " ", pluralize("test", numberOfTests),", ", numberOfFailures, " ", pluralize("failure", numberOfFailures)); 452 | printNumberOfPending; 453 | writeln(); 454 | } 455 | 456 | void printResultImpl (DescriptionManager descriptions) 457 | { 458 | restore(indentation) in { 459 | indentation ~= defaultIndentation; 460 | 461 | foreach (description ; descriptions) 462 | { 463 | printDescription(description); 464 | printResultImpl(description.descriptions); 465 | } 466 | }; 467 | } 468 | 469 | void printDescription (Description description) 470 | { 471 | writeln(indentation, description.message); 472 | 473 | restore(indentation) in { 474 | indentation ~= defaultIndentation; 475 | 476 | foreach (i, ref test ; description.tests) 477 | { 478 | write(indentation, "- ", test.message); 479 | 480 | if (test.isPending) 481 | write(" (PENDING: Not Yet Implemented)"); 482 | 483 | if (test.failed) 484 | write(" (FAILED - ", ++failureId, ')'); 485 | 486 | writeln(); 487 | } 488 | }; 489 | } 490 | 491 | void printPending () 492 | { 493 | if (!hasPending) 494 | return; 495 | 496 | writeln("\nPending:"); 497 | 498 | restore(indentation) in { 499 | indentation ~= defaultIndentation; 500 | 501 | foreach (description ; descriptions) 502 | { 503 | printPendingDescription(description); 504 | printPendingImpl(description.descriptions); 505 | } 506 | }; 507 | } 508 | 509 | void printPendingImpl (DescriptionManager descriptions) 510 | { 511 | foreach (description ; descriptions) 512 | { 513 | printPendingDescription(description); 514 | printPendingImpl(description.descriptions); 515 | } 516 | } 517 | 518 | void printPendingDescription (Description description) 519 | { 520 | foreach (test ; description.pending) 521 | writeln(indentation, description.message, " ", test.message, "\n", indentation, indentation, "# Not Yet Implemented"); 522 | } 523 | 524 | void printFailures () 525 | { 526 | if (!hasFailures) 527 | return; 528 | 529 | writeln("\nFailures:"); 530 | 531 | restore(indentation) in { 532 | indentation ~= defaultIndentation; 533 | 534 | foreach (description ; descriptions) 535 | { 536 | printFailuresDescription(description); 537 | printFailuresImpl(description.descriptions); 538 | } 539 | }; 540 | } 541 | 542 | void printFailuresImpl (DescriptionManager descriptions) 543 | { 544 | foreach (description ; descriptions) 545 | { 546 | printFailuresDescription(description); 547 | printFailuresImpl(description.descriptions); 548 | } 549 | } 550 | 551 | void printFailuresDescription (Description description) 552 | { 553 | foreach (test ; description.failures) 554 | { 555 | auto str = indentation ~ to!(string)(++failureId) ~ ") "; 556 | auto whitespace = toWhitespace(str.length); 557 | 558 | writeln(str, description.message, " ", test.message); 559 | writeln(whitespace, "# ", test.exception.file, ".d:", test.exception.line); 560 | writeln(whitespace, "Stack trace:"); 561 | write(whitespace); 562 | } 563 | } 564 | 565 | void printNumberOfPending () 566 | { 567 | if (hasPending) 568 | write(", ", numberOfPending, " pending"); 569 | } 570 | 571 | void printSuccess () 572 | { 573 | writeln("All ", numberOfTests, pluralize(" test", numberOfTests), " passed successfully."); 574 | } 575 | 576 | bool isAllTestsSuccessful () 577 | { 578 | return !hasPending && !hasFailures; 579 | } 580 | 581 | bool hasPending () 582 | { 583 | return numberOfPending > 0; 584 | } 585 | 586 | bool hasFailures () 587 | { 588 | return numberOfFailures > 0; 589 | } 590 | 591 | Use!(void delegate ()) execute () 592 | { 593 | Use!(void delegate ()) use; 594 | 595 | use.args[0] = &executeImpl; 596 | 597 | return use; 598 | } 599 | 600 | void executeImpl (void delegate () dg) 601 | { 602 | auto before = this.before; 603 | auto after = this.after; 604 | 605 | if (before) before(); 606 | if (dg) dg(); 607 | if (after) after(); 608 | } 609 | 610 | string toWhitespace (size_t value) 611 | { 612 | string str; 613 | 614 | for (size_t i = 0; i < value; i++) 615 | str ~= ' '; 616 | 617 | return str; 618 | } 619 | 620 | string pluralize (string str, size_t value) 621 | { 622 | if (value == 1) 623 | return str; 624 | 625 | return str ~ "s"; 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /orange/serialization/archives/Archive.d: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. 3 | * Authors: Jacob Carlborg 4 | * Version: Initial created: Feb 6, 2010 5 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 | */ 7 | module orange.serialization.archives.Archive; 8 | 9 | import std.array; 10 | import std.conv; 11 | import std.utf; 12 | static import std.string; 13 | 14 | import orange.serialization.SerializationException; 15 | import orange.serialization.Serializer; 16 | import orange.util.Traits; 17 | 18 | /** 19 | * This interface represents an archive. This is the interface all archive 20 | * implementations need to implement to be able to be used as an archive with the 21 | * serializer. 22 | * 23 | * The archive is the backend in the serialization process. It's independent of the 24 | * serializer and any archive implementation. Although there are a couple of 25 | * limitations of what archive types can be implemented (see below). 26 | * 27 | * The archive is responsible for archiving primitive types in the format chosen by 28 | * the archive implementation. The archive ensures that all types are properly 29 | * archived in a format that can be later unarchived. 30 | * 31 | * The archive can only handle primitive types, like strings, integers, floating 32 | * point numbers and so on. It can not handle more complex types like objects or 33 | * arrays; the serializer is responsible for breaking the complex types into 34 | * primitive types that the archive can handle. 35 | * 36 | * Implementing an Archive Type: 37 | * 38 | * There are a couple of limitations when implementing a new archive, this is due 39 | * to how the serializer and the archive interface is built. Except for what this 40 | * interface says explicitly an archive needs to be able to handle the following: 41 | * 42 | * $(UL 43 | * $(LI unarchive a value based on a key or id, regardless of where in the archive 44 | * the value is located) 45 | * $(LI most likely be able to modify already archived data) 46 | * $(LI structured formats like JSON, XML and YAML works best) 47 | * ) 48 | * 49 | * If a method takes a delegate as one of its parameters that delegate should be 50 | * considered as a callback to the serializer. The archive need to make sure that 51 | * any archiving that is performed in the callback be a part of the type that is 52 | * currently being archived. This is easiest explained by an example: 53 | * 54 | * --- 55 | * void archiveArray (Array array, string type, string key, Id id, void delegate () dg) 56 | * { 57 | * markBegningOfNewType("array"); 58 | * storeMetadata(type, key, id); 59 | * 60 | * beginNewScope(); 61 | * dg(); 62 | * endScope(); 63 | * 64 | * markEndOfType(); 65 | * } 66 | * --- 67 | * 68 | * In the above example the archive have to make sure that any values archived by 69 | * the callback (the delegate) get archived as an element of the array. The same 70 | * principle applies to objects, structs, associative arrays and other 71 | * non-primitives that accepts a delegate as a parameter. 72 | * 73 | * An archive implementation needs to be able to handle errors, like missing values 74 | * in the serialized data, without throwing exceptions. This is because the 75 | * interface of the serializer and an archive allows the user to set an error 76 | * callback that is called when an error occurs; and the callback can choose to 77 | * ignore the exceptions. 78 | * 79 | * In all the examples below "XmlArchive" is used as an example of an archive 80 | * implementation. "data" is assumed to be the serialized data. 81 | * 82 | * When implementing a new archive type, if any of these methods do not make sense 83 | * for that particular implementation just implement an empty method and return 84 | * T.init, if the method returns a value. 85 | */ 86 | interface Archive 87 | { 88 | /// The type of an ID. 89 | alias size_t Id; 90 | 91 | /// The typed used to represent the archived data in an untyped form. 92 | alias immutable(void)[] UntypedData; 93 | 94 | /** 95 | * This is the type of an error callback which is called when an unexpected event occurs. 96 | * 97 | * _Param: 98 | * exception = the exception indicating what error occurred 99 | */ 100 | alias ErrorCallback = void delegate (SerializationException exception) ; 101 | 102 | /** 103 | * This callback will be called when an unexpected event occurs, i.e. an expected element 104 | * is missing in the unarchiving process. 105 | * 106 | * Examples: 107 | * --- 108 | * auto archive = new XmlArchive!(); 109 | * serializer.errorCallback = (SerializationException exception) { 110 | * println(exception); 111 | * throw exception; 112 | * }; 113 | * --- 114 | */ 115 | ErrorCallback errorCallback (); 116 | 117 | /** 118 | * This callback will be called when an unexpected event occurs, i.e. an expected element 119 | * is missing in the unarchiving process. 120 | * 121 | * Examples: 122 | * --- 123 | * auto archive = new XmlArchive!(); 124 | * serializer.errorCallback = (SerializationException exception) { 125 | * println(exception); 126 | * throw exception; 127 | * }; 128 | * --- 129 | */ 130 | ErrorCallback errorCallback (ErrorCallback errorCallback); 131 | 132 | /// Starts the archiving process. Call this method before archiving any values. 133 | void beginArchiving (); 134 | 135 | /** 136 | * Begins the unarchiving process. Call this method before unarchiving any values. 137 | * 138 | * Params: 139 | * data = the data to unarchive 140 | */ 141 | void beginUnarchiving (UntypedData data); 142 | 143 | /// Returns the data stored in the archive in an untyped form. 144 | UntypedData untypedData (); 145 | 146 | /** 147 | * Resets the archive. This resets the archive in a state making it ready to start 148 | * a new archiving process. 149 | */ 150 | void reset (); 151 | 152 | /** 153 | * Archives an array. 154 | * 155 | * Examples: 156 | * --- 157 | * int[] arr = [1, 2, 3]; 158 | * 159 | * auto archive = new XmlArchive!(); 160 | * 161 | * auto a = Array(arr.ptr, arr.length, typeof(a[0]).sizeof); 162 | * 163 | * archive.archive(a, typeof(a[0]).string, "arr", 0, { 164 | * // archive the individual elements 165 | * }); 166 | * --- 167 | * 168 | * Params: 169 | * array = the array to archive 170 | * type = the runtime type of an element of the array 171 | * key = the key associated with the array 172 | * id = the id associated with the array 173 | * dg = a callback that performs the archiving of the individual elements 174 | */ 175 | void archiveArray (Array array, string type, string key, Id id, void delegate () dg); 176 | 177 | /** 178 | * Archives an associative array. 179 | * 180 | * Examples: 181 | * --- 182 | * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3]; 183 | * 184 | * auto archive = new XmlArchive!(); 185 | * 186 | * archive.archive(string.stringof, int.stringof, arr.length, "arr", 0, { 187 | * // archive the individual keys and values 188 | * }); 189 | * --- 190 | * 191 | * 192 | * Params: 193 | * keyType = the runtime type of the keys 194 | * valueType = the runtime type of the values 195 | * length = the length of the associative array 196 | * key = the key associated with the associative array 197 | * id = the id associated with the associative array 198 | * dg = a callback that performs the archiving of the individual keys and values 199 | * 200 | * See_Also: archiveAssociativeArrayValue 201 | * See_Also: archiveAssociativeArrayKey 202 | */ 203 | void archiveAssociativeArray (string keyType, string valueType, size_t length, string key, Id id, void delegate () dg); 204 | 205 | /** 206 | * Archives an associative array key. 207 | * 208 | * There are separate methods for archiving associative array keys and values 209 | * because both the key and the value can be of arbitrary type and needs to be 210 | * archived on its own. 211 | * 212 | * Examples: 213 | * --- 214 | * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3]; 215 | * 216 | * auto archive = new XmlArchive!(); 217 | * 218 | * foreach(k, v ; arr) 219 | * { 220 | * archive.archiveAssociativeArrayKey(to!(string)(i), { 221 | * // archive the key 222 | * }); 223 | * } 224 | * --- 225 | * 226 | * The foreach statement in the above example would most likely be executed in the 227 | * callback passed to the archiveAssociativeArray method. 228 | * 229 | * Params: 230 | * key = the key associated with the key 231 | * dg = a callback that performs the actual archiving of the key 232 | * 233 | * See_Also: archiveAssociativeArray 234 | * See_Also: archiveAssociativeArrayValue 235 | */ 236 | void archiveAssociativeArrayKey (string key, void delegate () dg); 237 | 238 | /** 239 | * Archives an associative array value. 240 | * 241 | * There are separate methods for archiving associative array keys and values 242 | * because both the key and the value can be of arbitrary type and needs to be 243 | * archived on its own. 244 | * 245 | * Examples: 246 | * --- 247 | * int[string] arr = ["a"[] : 1, "b" : 2, "c" : 3]; 248 | * 249 | * auto archive = new XmlArchive!(); 250 | * size_t i; 251 | * 252 | * foreach(k, v ; arr) 253 | * { 254 | * archive.archiveAssociativeArrayValue(to!(string)(i), { 255 | * // archive the value 256 | * }); 257 | * 258 | * i++; 259 | * } 260 | * --- 261 | * 262 | * The foreach statement in the above example would most likely be executed in the 263 | * callback passed to the archiveAssociativeArray method. 264 | * 265 | * Params: 266 | * key = the key associated with the value 267 | * dg = a callback that performs the actual archiving of the value 268 | * 269 | * See_Also: archiveAssociativeArray 270 | * See_Also: archiveAssociativeArrayKey 271 | */ 272 | void archiveAssociativeArrayValue (string key, void delegate () dg); 273 | 274 | /** 275 | * Archives the given value. 276 | * 277 | * Example: 278 | * --- 279 | * enum Foo : bool 280 | * { 281 | * bar 282 | * } 283 | * 284 | * auto foo = Foo.bar; 285 | * auto archive = new XmlArchive!(); 286 | * archive.archive(foo, "bool", "foo", 0); 287 | * --- 288 | * 289 | * Params: 290 | * value = the value to archive 291 | * baseType = the base type of the enum 292 | * key = the key associated with the value 293 | * id = the id associated with the value 294 | */ 295 | void archiveEnum (bool value, string baseType, string key, Id id); 296 | 297 | /// Ditto 298 | void archiveEnum (bool value, string baseType, string key, Id id); 299 | 300 | /// Ditto 301 | void archiveEnum (byte value, string baseType, string key, Id id); 302 | 303 | /// Ditto 304 | void archiveEnum (char value, string baseType, string key, Id id); 305 | 306 | /// Ditto 307 | void archiveEnum (dchar value, string baseType, string key, Id id); 308 | 309 | /// Ditto 310 | void archiveEnum (int value, string baseType, string key, Id id); 311 | 312 | /// Ditto 313 | void archiveEnum (long value, string baseType, string key, Id id); 314 | 315 | /// Ditto 316 | void archiveEnum (short value, string baseType, string key, Id id); 317 | 318 | /// Ditto 319 | void archiveEnum (ubyte value, string baseType, string key, Id id); 320 | 321 | /// Ditto 322 | void archiveEnum (uint value, string baseType, string key, Id id); 323 | 324 | /// Ditto 325 | void archiveEnum (ulong value, string baseType, string key, Id id); 326 | 327 | /// Ditto 328 | void archiveEnum (ushort value, string baseType, string key, Id id); 329 | 330 | /// Ditto 331 | void archiveEnum (wchar value, string baseType, string key, Id id); 332 | 333 | /** 334 | * Archives a base class. 335 | * 336 | * This method is used to indicate that the all following calls to archive a value 337 | * should be part of the base class. This method is usually called within the 338 | * callback passed to archiveObject. The archiveObject method can the mark the end 339 | * of the class. 340 | * 341 | * Examples: 342 | * --- 343 | * class ArchiveBase {} 344 | * class Foo : ArchiveBase {} 345 | * 346 | * auto archive = new XmlArchive!(); 347 | * archive.archiveBaseClass("ArchiveBase", "base", 0); 348 | * --- 349 | * 350 | * Params: 351 | * type = the type of the base class to archive 352 | * key = the key associated with the base class 353 | * id = the id associated with the base class 354 | */ 355 | void archiveBaseClass (string type, string key, Id id); 356 | 357 | /** 358 | * Archives a null pointer or reference. 359 | * 360 | * Examples: 361 | * --- 362 | * int* ptr; 363 | * 364 | * auto archive = new XmlArchive!(); 365 | * archive.archiveNull(typeof(ptr).stringof, "ptr"); 366 | * --- 367 | * 368 | * Params: 369 | * type = the runtime type of the pointer or reference to archive 370 | * key = the key associated with the null pointer 371 | */ 372 | void archiveNull (string type, string key); 373 | 374 | /** 375 | * Archives an object, either a class or an interface. 376 | * 377 | * Examples: 378 | * --- 379 | * class Foo 380 | * { 381 | * int a; 382 | * } 383 | * 384 | * auto foo = new Foo; 385 | * 386 | * auto archive = new XmlArchive!(); 387 | * archive.archiveObject(Foo.classinfo.name, "Foo", "foo", 0, { 388 | * // archive the fields of Foo 389 | * }); 390 | * --- 391 | * 392 | * Params: 393 | * runtimeType = the runtime type of the object 394 | * type = the static type of the object 395 | * key = the key associated with the object 396 | * id = the id associated with the object 397 | * dg = a callback that performs the archiving of the individual fields 398 | */ 399 | void archiveObject (string runtimeType, string type, string key, Id id, void delegate () dg); 400 | 401 | /** 402 | * Archives a pointer. 403 | * 404 | * If a pointer points to a value that is serialized as well, the pointer should be 405 | * archived as a reference. Otherwise the value that the pointer points to should be 406 | * serialized as a regular value. 407 | * 408 | * Examples: 409 | * --- 410 | * class Foo 411 | * { 412 | * int a; 413 | * int* b; 414 | * } 415 | * 416 | * auto foo = new Foo; 417 | * foo.a = 3; 418 | * foo.b = &foo.a; 419 | * 420 | * archive = new XmlArchive!(); 421 | * archive.archivePointer("b", 0, { 422 | * // archive "foo.b" as a reference 423 | * }); 424 | * --- 425 | * 426 | * --- 427 | * int a = 3; 428 | * 429 | * class Foo 430 | * { 431 | * int* b; 432 | * } 433 | * 434 | * auto foo = new Foo; 435 | * foo.b = &a; 436 | * 437 | * archive = new XmlArchive!(); 438 | * archive.archivePointer("b", 0, { 439 | * // archive "foo.b" as a regular value 440 | * }); 441 | * --- 442 | * 443 | * Params: 444 | * key = the key associated with the pointer 445 | * id = the id associated with the pointer 446 | * dg = a callback that performs the archiving of value pointed to by the pointer 447 | */ 448 | void archivePointer (string key, Id id, void delegate () dg); 449 | 450 | /** 451 | * Archives a reference. 452 | * 453 | * A reference is reference to another value. For example, if an object is archived 454 | * more than once, the first time it's archived it will actual archive the object. 455 | * The second time the object will be archived a reference will be archived instead 456 | * of the actual object. 457 | * 458 | * This method is also used when archiving a pointer that points to a value that has 459 | * been or will be archived as well. 460 | * 461 | * Examples: 462 | * --- 463 | * class Foo {} 464 | * 465 | * class Bar 466 | * { 467 | * Foo f; 468 | * Foo f2; 469 | * } 470 | * 471 | * auto bar = new Bar; 472 | * bar.f = new Foo; 473 | * bar.f2 = bar.f; 474 | * 475 | * auto archive = new XmlArchive!(); 476 | * 477 | * // when achiving "bar" 478 | * archive.archiveObject(Foo.classinfo.name, "Foo", "f", 0, {}); 479 | * archive.archiveReference("f2", 0); // archive a reference to "f" 480 | * --- 481 | * 482 | * Params: 483 | * key = the key associated with the reference 484 | * id = the id of the value this reference refers to 485 | */ 486 | void archiveReference (string key, Id id); 487 | 488 | /** 489 | * Archives a slice. 490 | * 491 | * This method should be used when archiving an array that is a slice of an 492 | * already archived array or an array that has not yet been archived. 493 | * 494 | * Examples: 495 | * --- 496 | * auto arr = [1, 2, 3, 4]; 497 | * auto slice = arr[1 .. 3]; 498 | * 499 | * auto archive = new XmlArchive!(); 500 | * // archive "arr" with id 0 501 | * 502 | * auto s = Slice(slice.length, 1); 503 | * archive.archiveSlice(s, 1, 0); 504 | * --- 505 | * 506 | * Params: 507 | * slice = the slice to be archived 508 | * sliceId = the id associated with the slice 509 | * arrayId = the id associated with the array this slice is a slice of 510 | */ 511 | void archiveSlice (Slice slice, Id sliceId, Id arrayId); 512 | 513 | /** 514 | * Archives a struct. 515 | * 516 | * Examples: 517 | * --- 518 | * struct Foo 519 | * { 520 | * int a; 521 | * } 522 | * 523 | * auto foo = Foo(3); 524 | * 525 | * auto archive = new XmlArchive!(); 526 | * archive.archiveStruct(Foo.stringof, "foo", 0, { 527 | * // archive the fields of Foo 528 | * }); 529 | * --- 530 | * 531 | * Params: 532 | * type = the type of the struct 533 | * key = the key associated with the struct 534 | * id = the id associated with the struct 535 | * dg = a callback that performs the archiving of the individual fields 536 | */ 537 | void archiveStruct (string type, string key, Id id, void delegate () dg); 538 | 539 | /** 540 | * Archives the given value. 541 | * 542 | * Params: 543 | * value = the value to archive 544 | * key = the key associated with the value 545 | * id = the id associated wit the value 546 | */ 547 | void archive (string value, string key, Id id); 548 | 549 | /// Ditto 550 | void archive (wstring value, string key, Id id); 551 | 552 | /// Ditto 553 | void archive (dstring value, string key, Id id); 554 | 555 | /// Ditto 556 | void archive (bool value, string key, Id id); 557 | 558 | /// Ditto 559 | void archive (byte value, string key, Id id); 560 | 561 | 562 | //void archive (cdouble value, string key, Id id); // currently not supported by to!() 563 | 564 | 565 | //void archive (cent value, string key, Id id); 566 | 567 | //void archive (cfloat value, string key, Id id); // currently not supported by to!() 568 | 569 | /// Ditto 570 | void archive (char value, string key, Id id); 571 | 572 | //void archive (creal value, string key, Id id); // currently not supported by to!() 573 | 574 | /// Ditto 575 | void archive (dchar value, string key, Id id); 576 | 577 | /// Ditto 578 | void archive (double value, string key, Id id); 579 | 580 | /// Ditto 581 | void archive (float value, string key, Id id); 582 | 583 | 584 | //void archive (idouble value, string key, Id id); // currently not supported by to!() 585 | 586 | //void archive (ifloat value, string key, Id id); // currently not supported by to!() 587 | 588 | /// Ditto 589 | void archive (int value, string key, Id id); 590 | 591 | 592 | //void archive (ireal value, string key, Id id); // currently not supported by to!() 593 | 594 | /// Ditto 595 | void archive (long value, string key, Id id); 596 | 597 | /// Ditto 598 | void archive (real value, string key, Id id); 599 | 600 | /// Ditto 601 | void archive (short value, string key, Id id); 602 | 603 | /// Ditto 604 | void archive (ubyte value, string key, Id id); 605 | 606 | //void archive (ucent value, string key, Id id); // currently not implemented but a reserved keyword 607 | 608 | /// Ditto 609 | void archive (uint value, string key, Id id); 610 | 611 | /// Ditto 612 | void archive (ulong value, string key, Id id); 613 | 614 | /// Ditto 615 | void archive (ushort value, string key, Id id); 616 | 617 | /// Ditto 618 | void archive (wchar value, string key, Id id); 619 | 620 | /** 621 | * Unarchives the value associated with the given key as an array. 622 | * 623 | * Examples: 624 | * --- 625 | * auto archive = new XmlArchive!(); 626 | * archive.beginUnarchiving(data); 627 | * auto id = archive.unarchiveArray("arr", (size_t length) { 628 | * auto arr = new int[length]; // pre-allocate the array 629 | * // unarchive the individual elements of "arr" 630 | * }); 631 | * --- 632 | * 633 | * Params: 634 | * key = the key associated with the array 635 | * dg = a callback that performs the unarchiving of the individual elements. 636 | * $(I length) is the length of the archived array 637 | * 638 | * Returns: the id associated with the array 639 | * 640 | * See_Also: unarchiveArray 641 | */ 642 | Id unarchiveArray (string key, void delegate (size_t length) dg); 643 | 644 | /** 645 | * Unarchives the value associated with the given id as an array. 646 | * 647 | * Examples: 648 | * --- 649 | * auto archive = new XmlArchive!(); 650 | * archive.beginUnarchiving(data); 651 | * archive.unarchiveArray(0, (size_t length) { 652 | * auto arr = new int[length]; // pre-allocate the array 653 | * // unarchive the individual elements of "arr" 654 | * }); 655 | * --- 656 | * 657 | * Params: 658 | * id = the id associated with the value 659 | * dg = a callback that performs the unarchiving of the individual elements. 660 | * $(I length) is the length of the archived array 661 | * 662 | * See_Also: unarchiveArray 663 | */ 664 | void unarchiveArray (Id id, void delegate (size_t length) dg); 665 | 666 | /** 667 | * Unarchives the value associated with the given id as an associative array. 668 | * 669 | * Examples: 670 | * --- 671 | * auto archive = new XmlArchive!(); 672 | * archive.beginUnarchiving(data); 673 | * 674 | * auto id = archive.unarchiveAssociativeArray("aa", (size_t length) { 675 | * // unarchive the individual keys and values 676 | * }); 677 | * --- 678 | * 679 | * Params: 680 | * key = the key associated with the associative array 681 | * dg = a callback that performs the unarchiving of the individual keys and values. 682 | * $(I length) is the length of the archived associative array 683 | * 684 | * Returns: the id associated with the associative array 685 | * 686 | * See_Also: unarchiveAssociativeArrayKey 687 | * See_Also: unarchiveAssociativeArrayValue 688 | */ 689 | Id unarchiveAssociativeArray (string key, void delegate (size_t length) dg); 690 | 691 | /** 692 | * Unarchives an associative array key. 693 | * 694 | * There are separate methods for unarchiving associative array keys and values 695 | * because both the key and the value can be of arbitrary type and needs to be 696 | * unarchived on its own. 697 | * 698 | * Examples: 699 | * --- 700 | * auto archive = new XmlArchive!(); 701 | * archive.beginUnarchiving(data); 702 | * 703 | * for (size_t i = 0; i < length; i++) 704 | * { 705 | * unarchiveAssociativeArrayKey(to!(string(i), { 706 | * // unarchive the key 707 | * })); 708 | * } 709 | * --- 710 | * 711 | * The for statement in the above example would most likely be executed in the 712 | * callback passed to the unarchiveAssociativeArray method. 713 | * 714 | * Params: 715 | * key = the key associated with the key 716 | * dg = a callback that performs the actual unarchiving of the key 717 | * 718 | * See_Also: unarchiveAssociativeArrayValue 719 | * See_Also: unarchiveAssociativeArray 720 | */ 721 | void unarchiveAssociativeArrayKey (string key, void delegate () dg); 722 | 723 | /** 724 | * Unarchives an associative array value. 725 | * 726 | * There are separate methods for unarchiving associative array keys and values 727 | * because both the key and the value can be of arbitrary type and needs to be 728 | * unarchived on its own. 729 | * 730 | * Examples: 731 | * --- 732 | * auto archive = new XmlArchive!(); 733 | * archive.beginUnarchiving(data); 734 | * 735 | * for (size_t i = 0; i < length; i++) 736 | * { 737 | * unarchiveAssociativeArrayValue(to!(string(i), { 738 | * // unarchive the value 739 | * })); 740 | * } 741 | * --- 742 | * 743 | * The for statement in the above example would most likely be executed in the 744 | * callback passed to the unarchiveAssociativeArray method. 745 | * 746 | * Params: 747 | * key = the key associated with the value 748 | * dg = a callback that performs the actual unarchiving of the value 749 | * 750 | * See_Also: unarchiveAssociativeArrayKey 751 | * See_Also: unarchiveAssociativeArray 752 | */ 753 | void unarchiveAssociativeArrayValue (string key, void delegate () dg); 754 | 755 | /** 756 | * Unarchives the value associated with the given key as a bool. 757 | * 758 | * This method is used when the unarchiving a enum value with the base type bool. 759 | * 760 | * Params: 761 | * key = the key associated with the value 762 | * 763 | * Returns: the unarchived value 764 | */ 765 | bool unarchiveEnumBool (string key, out Id id); 766 | 767 | /// Ditto 768 | byte unarchiveEnumByte (string key, out Id id); 769 | 770 | /// Ditto 771 | char unarchiveEnumChar (string key, out Id id); 772 | 773 | /// Ditto 774 | dchar unarchiveEnumDchar (string key, out Id id); 775 | 776 | /// Ditto 777 | int unarchiveEnumInt (string key, out Id id); 778 | 779 | /// Ditto 780 | long unarchiveEnumLong (string key, out Id id); 781 | 782 | /// Ditto 783 | short unarchiveEnumShort (string key, out Id id); 784 | 785 | /// Ditto 786 | ubyte unarchiveEnumUbyte (string key, out Id id); 787 | 788 | /// Ditto 789 | uint unarchiveEnumUint (string key, out Id id); 790 | 791 | /// Ditto 792 | ulong unarchiveEnumUlong (string key, out Id id); 793 | 794 | /// Ditto 795 | ushort unarchiveEnumUshort (string key, out Id id); 796 | 797 | /// Ditto 798 | wchar unarchiveEnumWchar (string key, out Id id); 799 | 800 | /** 801 | * Unarchives the value associated with the given id as a bool. 802 | * 803 | * This method is used when the unarchiving a enum value with the base type bool. 804 | * 805 | * Params: 806 | * id = the id associated with the value 807 | * 808 | * Returns: the unarchived value 809 | */ 810 | bool unarchiveEnumBool (Id id); 811 | 812 | /// Ditto 813 | byte unarchiveEnumByte (Id id); 814 | 815 | /// Ditto 816 | char unarchiveEnumChar (Id id); 817 | 818 | /// Ditto 819 | dchar unarchiveEnumDchar (Id id); 820 | 821 | /// Ditto 822 | int unarchiveEnumInt (Id id); 823 | 824 | /// Ditto 825 | long unarchiveEnumLong (Id id); 826 | 827 | /// Ditto 828 | short unarchiveEnumShort (Id id); 829 | 830 | /// Ditto 831 | ubyte unarchiveEnumUbyte (Id id); 832 | 833 | /// Ditto 834 | uint unarchiveEnumUint (Id id); 835 | 836 | /// Ditto 837 | ulong unarchiveEnumUlong (Id id); 838 | 839 | /// Ditto 840 | ushort unarchiveEnumUshort (Id id); 841 | 842 | /// Ditto 843 | wchar unarchiveEnumWchar (Id id); 844 | 845 | /** 846 | * Unarchives the base class associated with the given key. 847 | * 848 | * This method is used to indicate that the all following calls to unarchive a 849 | * value should be part of the base class. This method is usually called within the 850 | * callback passed to unarchiveObject. The unarchiveObject method can the mark the 851 | * end of the class. 852 | * 853 | * Examples: 854 | * --- 855 | * auto archive = new XmlArchive!(); 856 | * archive.beginUnarchiving(data); 857 | * archive.unarchiveBaseClass("base"); 858 | * --- 859 | * 860 | * Params: 861 | * key = the key associated with the base class. 862 | * 863 | * See_Also: unarchiveObject 864 | */ 865 | void unarchiveBaseClass (string key); 866 | 867 | /** 868 | * Unarchives the object associated with the given key. 869 | * 870 | * Examples: 871 | * --- 872 | * class Foo 873 | * { 874 | * int a; 875 | * } 876 | * 877 | * auto archive = new XmlArchive!(); 878 | * archive.beginUnarchiving(data); 879 | * 880 | * Id id; 881 | * Object o; 882 | * 883 | * archive.unarchiveObject("foo", id, o, { 884 | * // unarchive the fields of Foo 885 | * }); 886 | * 887 | * auto foo = cast(Foo) o; 888 | * --- 889 | * 890 | * Params: 891 | * key = the key associated with the object 892 | * id = the id associated with the object 893 | * result = the unarchived object 894 | * dg = a callback the performs the unarchiving of the individual fields 895 | */ 896 | void unarchiveObject (string key, out Id id, out Object result, void delegate () dg); 897 | 898 | /** 899 | * Unarchives the pointer associated with the given key. 900 | * 901 | * Examples: 902 | * --- 903 | * auto archive = new XmlArchive!(); 904 | * archive.beginUnarchiving(data); 905 | * auto id = unarchivePointer("ptr", { 906 | * // unarchive the value pointed to by the pointer 907 | * }); 908 | * --- 909 | * 910 | * Params: 911 | * key = the key associated with the pointer 912 | * dg = a callback that performs the unarchiving of value pointed to by the pointer 913 | * 914 | * Returns: the id associated with the pointer 915 | */ 916 | Id unarchivePointer (string key, void delegate () dg); 917 | 918 | /** 919 | * Unarchives the reference associated with the given key. 920 | * 921 | * A reference is reference to another value. For example, if an object is archived 922 | * more than once, the first time it's archived it will actual archive the object. 923 | * The second time the object will be archived a reference will be archived instead 924 | * of the actual object. 925 | * 926 | * This method is also used when unarchiving a pointer that points to a value that has 927 | * been or will be unarchived as well. 928 | * 929 | * Examples: 930 | * --- 931 | * auto archive = new XmlArchive!(); 932 | * archive.beginUnarchiving(data); 933 | * auto id = unarchiveReference("foo"); 934 | * 935 | * // unarchive the value with the associated id 936 | * --- 937 | * 938 | * Params: 939 | * key = the key associated with the reference 940 | * 941 | * Returns: the id the reference refers to 942 | */ 943 | Id unarchiveReference (string key); 944 | 945 | /** 946 | * Unarchives the slice associated with the given key. 947 | * 948 | * This method should be used when unarchiving an array that is a slice of an 949 | * already unarchived array or an array that has not yet been unarchived. 950 | * 951 | * Examples: 952 | * --- 953 | * auto archive = new XmlArchive!(); 954 | * archive.beginUnarchiving(data); 955 | * auto slice = unarchiveSlice("slice"); 956 | * 957 | * // slice the original array with the help of the unarchived slice 958 | * --- 959 | * 960 | * Params: 961 | * key = the key associated with the slice 962 | * 963 | * Returns: the unarchived slice 964 | */ 965 | Slice unarchiveSlice (string key); 966 | 967 | /** 968 | * Unarchives the struct associated with the given key. 969 | * 970 | * Examples: 971 | * --- 972 | * struct Foo 973 | * { 974 | * int a; 975 | * } 976 | * 977 | * auto archive = new XmlArchive!(); 978 | * archive.beginUnarchiving(data); 979 | * archive.unarchiveStruct("foo", { 980 | * // unarchive the fields of Foo 981 | * }); 982 | * --- 983 | * 984 | * Params: 985 | * key = the key associated with the string 986 | * dg = a callback that performs the unarchiving of the individual fields 987 | */ 988 | Id unarchiveStruct (string key, void delegate () dg); 989 | 990 | /** 991 | * Unarchives the struct associated with the given id. 992 | * 993 | * Examples: 994 | * --- 995 | * struct Foo 996 | * { 997 | * int a; 998 | * } 999 | * 1000 | * auto archive = new XmlArchive!(); 1001 | * archive.beginUnarchiving(data); 1002 | * archive.unarchiveStruct(0, { 1003 | * // unarchive the fields of Foo 1004 | * }); 1005 | * --- 1006 | * 1007 | * Params: 1008 | * id = the id associated with the struct 1009 | * dg = a callback that performs the unarchiving of the individual fields. 1010 | * The callback will receive the key the struct was archived with. 1011 | */ 1012 | void unarchiveStruct (Id id, void delegate () dg); 1013 | 1014 | /** 1015 | * Unarchives the string associated with the given id. 1016 | * 1017 | * Examples: 1018 | * --- 1019 | * auto archive = new XmlArchive!(); 1020 | * archive.beginUnarchiving(data); 1021 | * auto str = archive.unarchiveString(0); 1022 | * --- 1023 | * 1024 | * Params: 1025 | * id = the id associated with the string 1026 | * 1027 | * Returns: the unarchived string 1028 | */ 1029 | string unarchiveString (Id id); 1030 | 1031 | /// Ditto 1032 | wstring unarchiveWstring (Id id); 1033 | 1034 | /// Ditto 1035 | dstring unarchiveDstring (Id id); 1036 | 1037 | /** 1038 | * Unarchives the string associated with the given key. 1039 | * 1040 | * Examples: 1041 | * --- 1042 | * auto archive = new XmlArchive!(); 1043 | * archive.beginUnarchiving(data); 1044 | * 1045 | * Id id; 1046 | * auto str = archive.unarchiveString("str", id); 1047 | * --- 1048 | * 1049 | * Params: 1050 | * id = the id associated with the string 1051 | * 1052 | * Returns: the unarchived string 1053 | */ 1054 | string unarchiveString (string key, out Id id); 1055 | 1056 | /// Ditto 1057 | wstring unarchiveWstring (string key, out Id id); 1058 | 1059 | /// Ditto 1060 | dstring unarchiveDstring (string key, out Id id); 1061 | 1062 | /** 1063 | * Unarchives the value associated with the given key. 1064 | * 1065 | * Examples: 1066 | * --- 1067 | * auto archive = new XmlArchive!(); 1068 | * archive.beginUnarchiving(data); 1069 | * auto foo = unarchiveBool("foo"); 1070 | * --- 1071 | * Params: 1072 | * key = the key associated with the value 1073 | * 1074 | * Returns: the unarchived value 1075 | */ 1076 | bool unarchiveBool (string key, out Id id); 1077 | 1078 | /// Ditto 1079 | byte unarchiveByte (string key, out Id id); 1080 | 1081 | //cdouble unarchiveCdouble (string key, out Id id); // currently not supported by to!() 1082 | //cent unarchiveCent (string key, out Id id); // currently not implemented but a reserved keyword 1083 | //cfloat unarchiveCfloat (string key, out Id id); // currently not supported by to!() 1084 | 1085 | /// Ditto 1086 | char unarchiveChar (string key, out Id id); // currently not implemented but a reserved keyword 1087 | //creal unarchiveCreal (string key, out Id id); // currently not supported by to!() 1088 | 1089 | /// Ditto 1090 | dchar unarchiveDchar (string key, out Id id); 1091 | 1092 | /// Ditto 1093 | double unarchiveDouble (string key, out Id id); 1094 | 1095 | /// Ditto 1096 | float unarchiveFloat (string key, out Id id); 1097 | //idouble unarchiveIdouble (string key, out Id id); // currently not supported by to!() 1098 | //ifloat unarchiveIfloat (string key, out Id id); // currently not supported by to!()*/ 1099 | 1100 | /// Ditto 1101 | int unarchiveInt (string key, out Id id); 1102 | 1103 | //ireal unarchiveIreal (string key, out Id id); // currently not supported by to!() 1104 | 1105 | /// Ditto 1106 | long unarchiveLong (string key, out Id id); 1107 | 1108 | /// Ditto 1109 | real unarchiveReal (string key, out Id id); 1110 | 1111 | /// Ditto 1112 | short unarchiveShort (string key, out Id id); 1113 | 1114 | /// Ditto 1115 | ubyte unarchiveUbyte (string key, out Id id); 1116 | 1117 | /// 1118 | //ucent unarchiveCcent (string key, out Id id); // currently not implemented but a reserved keyword 1119 | 1120 | /// Ditto 1121 | uint unarchiveUint (string key, out Id id); 1122 | 1123 | /// Ditto 1124 | ulong unarchiveUlong (string key, out Id id); 1125 | 1126 | /// Ditto 1127 | ushort unarchiveUshort (string key, out Id id); 1128 | 1129 | /// Ditto 1130 | wchar unarchiveWchar (string key, out Id id); 1131 | 1132 | /** 1133 | * Unarchives the value associated with the given id. 1134 | * 1135 | * Examples: 1136 | * --- 1137 | * auto archive = new XmlArchive!(); 1138 | * archive.beginUnarchiving(data); 1139 | * auto foo = unarchiveBool(0); 1140 | * --- 1141 | * Params: 1142 | * id = the id associated with the value 1143 | * 1144 | * Returns: the unarchived value 1145 | */ 1146 | bool unarchiveBool (Id id); 1147 | 1148 | /// Ditto 1149 | byte unarchiveByte (Id id); 1150 | 1151 | //cdouble unarchiveCdouble (Id id); // currently not supported by to!() 1152 | //cent unarchiveCent (Id id); // currently not implemented but a reserved keyword 1153 | //cfloat unarchiveCfloat (Id id); // currently not supported by to!() 1154 | 1155 | /// Ditto 1156 | char unarchiveChar (Id id); // currently not implemented but a reserved keyword 1157 | //creal unarchiveCreal (Id id); // currently not supported by to!() 1158 | 1159 | /// Ditto 1160 | dchar unarchiveDchar (Id id); 1161 | 1162 | /// Ditto 1163 | double unarchiveDouble (Id id); 1164 | 1165 | /// Ditto 1166 | float unarchiveFloat (Id id); 1167 | //idouble unarchiveIdouble (Id id); // currently not supported by to!() 1168 | //ifloat unarchiveIfloat (Id id); // currently not supported by to!()*/ 1169 | 1170 | /// Ditto 1171 | int unarchiveInt (Id id); 1172 | 1173 | //ireal unarchiveIreal (Id id); // currently not supported by to!() 1174 | 1175 | /// Ditto 1176 | long unarchiveLong (Id id); 1177 | 1178 | /// Ditto 1179 | real unarchiveReal (Id id); 1180 | 1181 | /// Ditto 1182 | short unarchiveShort (Id id); 1183 | 1184 | /// Ditto 1185 | ubyte unarchiveUbyte (Id id); 1186 | 1187 | /// 1188 | //ucent unarchiveCcent (Id id); // currently not implemented but a reserved keyword 1189 | 1190 | /// Ditto 1191 | uint unarchiveUint (Id id); 1192 | 1193 | /// Ditto 1194 | ulong unarchiveUlong (Id id); 1195 | 1196 | /// Ditto 1197 | ushort unarchiveUshort (Id id); 1198 | 1199 | /// Ditto 1200 | wchar unarchiveWchar (Id id); 1201 | 1202 | /** 1203 | * Performs post processing of the array associated with the given id. 1204 | * 1205 | * Post processing can basically be anything that the archive wants to do. This 1206 | * method is called by the serializer once for each serialized array at the end of 1207 | * the serialization process when all values have been serialized. 1208 | * 1209 | * With this method the archive has a last chance of changing an archived array to 1210 | * an archived slice instead. 1211 | * 1212 | * Params: 1213 | * id = the id associated with the array 1214 | */ 1215 | void postProcessArray (Id id); 1216 | } 1217 | 1218 | /** 1219 | * This class serves as an optional base class for archive implementations. It 1220 | * contains some utility methods that can be helpful when creating a new archive 1221 | * implementation. 1222 | * 1223 | * Most of the examples below are assumed to be in a sub class to this class and 1224 | * with $(I string) as the data type. 1225 | */ 1226 | abstract class ArchiveBase (U) : Archive 1227 | { 1228 | /// The typed used to represent the archived data in a typed form. 1229 | alias immutable(U)[] Data; 1230 | 1231 | private ErrorCallback errorCallback_; 1232 | 1233 | /** 1234 | * This callback will be called when an unexpected event occurs, i.e. an expected element 1235 | * is missing in the unarchiving process. 1236 | * 1237 | * Examples: 1238 | * --- 1239 | * auto archive = new XmlArchive!(); 1240 | * serializer.errorCallback = (SerializationException exception) { 1241 | * println(exception); 1242 | * throw exception; 1243 | * }; 1244 | * --- 1245 | */ 1246 | ErrorCallback errorCallback () 1247 | { 1248 | return errorCallback_; 1249 | } 1250 | 1251 | /** 1252 | * This callback will be called when an unexpected event occurs, i.e. an expected element 1253 | * is missing in the unarchiving process. 1254 | * 1255 | * Examples: 1256 | * --- 1257 | * auto archive = new XmlArchive!(); 1258 | * serializer.errorCallback = (SerializationException exception) { 1259 | * println(exception); 1260 | * throw exception; 1261 | * }; 1262 | * --- 1263 | */ 1264 | ErrorCallback errorCallback (ErrorCallback errorCallback) 1265 | { 1266 | return errorCallback_ = errorCallback; 1267 | } 1268 | 1269 | /** 1270 | * Creates a new instance of this class with an error callback 1271 | * 1272 | * Params: 1273 | * errorCallback = the error callback used for ths instance 1274 | */ 1275 | protected this (ErrorCallback errorCallback) 1276 | { 1277 | this.errorCallback = errorCallback; 1278 | } 1279 | 1280 | /** 1281 | * Converts the given value into the type used for archiving. 1282 | * 1283 | * Examples: 1284 | * --- 1285 | * auto i = toData(3); 1286 | * assert(i == "3"); 1287 | * --- 1288 | * 1289 | * Params: 1290 | * value = the value to convert 1291 | * 1292 | * Returns: the converted value 1293 | * 1294 | * Throws: SerializationException if the conversion failed 1295 | * See_Also: fromData 1296 | * See_Also: floatingPointToData 1297 | */ 1298 | protected Data toData (T) (T value) 1299 | { 1300 | try 1301 | { 1302 | static if (isFloatingPoint!(T)) 1303 | return floatingPointToData(value); 1304 | 1305 | else 1306 | return to!(Data)(value); 1307 | } 1308 | 1309 | catch (ConvException e) 1310 | { 1311 | error(e); 1312 | return Data.init; 1313 | } 1314 | } 1315 | 1316 | /** 1317 | * Converts the given value from the type used for archiving to $(I T). 1318 | * 1319 | * Examples: 1320 | * --- 1321 | * auto i = fromData!(int)("3"); 1322 | * assert(i == 3); 1323 | * --- 1324 | * 1325 | * Params: 1326 | * T = the type to convert the given value to 1327 | * value = the value to convert 1328 | * 1329 | * Returns: the converted value 1330 | * 1331 | * Throws: SerializationException if the conversion failed 1332 | * See_Also: toData 1333 | */ 1334 | protected T fromData (T) (Data value) 1335 | { 1336 | try 1337 | { 1338 | static if (is(T == wchar)) 1339 | return toWchar(value); 1340 | 1341 | else 1342 | return to!(T)(value); 1343 | } 1344 | 1345 | catch (ConvException e) 1346 | { 1347 | error(e); 1348 | return T.init; 1349 | } 1350 | 1351 | } 1352 | 1353 | /** 1354 | * The archive is responsible for archiving primitive types in the format chosen by 1355 | * Converts the given floating point value to the type used for archiving. 1356 | * 1357 | * This method is used to convert floating point values, it will convert the 1358 | * floating point value to hexadecimal format. 1359 | * 1360 | * Examples: 1361 | * --- 1362 | * auto f = floatingPointToData(3.15f); 1363 | * assert(f == "0xc.9999ap-2"); 1364 | * --- 1365 | * 1366 | * Params: 1367 | * value = the value to convert 1368 | * 1369 | * Returns: the conveted value 1370 | * 1371 | * Throws: SerializationException if the conversion failed 1372 | */ 1373 | protected Data floatingPointToData (T) (T value) 1374 | { 1375 | static assert(isFloatingPoint!(T), format!(`The given value of the type "`, T, 1376 | `" is not a valid type, the only valid types for this method are floating point types.`)); 1377 | 1378 | return to!(Data)(std.string.format("%a", value)); 1379 | } 1380 | 1381 | /** 1382 | * Converts the id value to the type $(I Id). 1383 | * 1384 | * This method is used to conver ids stored in the serialized data to the correct 1385 | * type. 1386 | * 1387 | * Params: 1388 | * value = the value to convert 1389 | * 1390 | * Returns: the converted id 1391 | * 1392 | * Throws: SerializationException if the converted failed 1393 | * See_Also: fromData 1394 | */ 1395 | protected Id toId (Data value) 1396 | { 1397 | return fromData!(Id)(value); 1398 | } 1399 | 1400 | /** 1401 | * Calls the errorCallback with an exception. 1402 | * 1403 | * Call this method when some type of error occurred, like a field cannot be found. 1404 | * 1405 | * Params: 1406 | * message = the message for the exception 1407 | * data = the data for the exception 1408 | * file = the file where the error occurred 1409 | * line = the line where the error occurred 1410 | */ 1411 | protected void error (string message, string[] data = null, string file = __FILE__, size_t line = __LINE__) 1412 | { 1413 | if (errorCallback) 1414 | errorCallback()(new SerializationException(message, file, line)); 1415 | } 1416 | 1417 | /** 1418 | * Calls the errorCallback with an exception. 1419 | * 1420 | * Call this method when some type of error occurred, like a field cannot be found. 1421 | * 1422 | * Params: 1423 | * exception = the exception to pass to the errorCallback 1424 | */ 1425 | protected void error (Exception exception) 1426 | { 1427 | if (errorCallback) 1428 | errorCallback()(new SerializationException(exception)); 1429 | } 1430 | 1431 | private wchar toWchar (Data value) 1432 | { 1433 | auto c = value.front; 1434 | 1435 | if (codeLength!(wchar)(c) > 2) 1436 | throw new ConvException("Could not convert `" ~ 1437 | to!(string)(value) ~ "` of type " ~ 1438 | Data.stringof ~ " to type wchar."); 1439 | 1440 | return cast(wchar) c; 1441 | } 1442 | } 1443 | --------------------------------------------------------------------------------