├── .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 |
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 |
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 |
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 |
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 [](https://travis-ci.org/jacob-carlborg/orange) [](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 |
--------------------------------------------------------------------------------