# HG changeset patch # User Jacob Carlborg # Date 1312401501 -7200 # Node ID 90f6a44b9475312b7418959055d6b25f6e5a486c # Parent 947c32ec0ea7eb90fea7672c0f7ce52ea4c329ff# Parent c523d436052f27390f086ca1aa7711e0244afe2e Merged "experimental" into "default". diff -r 947c32ec0ea7 -r 90f6a44b9475 .hgignore --- a/.hgignore Mon Oct 04 20:45:32 2010 +0200 +++ b/.hgignore Wed Aug 03 21:58:21 2011 +0200 @@ -12,8 +12,9 @@ *.dylib main.d main -main.d.deps +*.deps *.framework kandil lib -import \ No newline at end of file +import +unittest \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 Makefile --- a/Makefile Mon Oct 04 20:45:32 2010 +0200 +++ b/Makefile Wed Aug 03 21:58:21 2011 +0200 @@ -1,6 +1,8 @@ LIBNAME = orange SRC = \ - _.d \ + core/io.d \ + core/string.d \ + core/_.d \ serialization/Events.d \ serialization/RegisterWrapper.d \ serialization/Serializable.d \ @@ -16,17 +18,20 @@ util/Traits.d \ util/Use.d \ util/_.d \ - util/io.d \ - util/string.d \ util/collection/Array.d \ + util/collection/_.d \ xml/PhobosXML.d \ xml/XMLDocument.d \ - _.d + xml/_.d \ DC = dmd DCFLAGS = -I/usr/include/d -I/usr/local/include/d +UNITTEST = test/UnitTester.d \ + tests/Serializer.d \ + tests/all.d \ + # Everything below this line should be fairly generic (with a few hard-coded things). OBJ = $(addsuffix .o,$(addprefix $(LIBNAME)/,$(basename $(SRC)))) @@ -60,4 +65,4 @@ %.o : %.d @echo Compiling $< . . . - @$(DC) -c $(DCFLAGS) $< -of$@ -Hfimport/$(basename $@).di + @$(DC) -c $< -of$@ -Hfimport/$(basename $@).di diff -r 947c32ec0ea7 -r 90f6a44b9475 dsss.conf --- a/dsss.conf Mon Oct 04 20:45:32 2010 +0200 +++ b/dsss.conf Wed Aug 03 21:58:21 2011 +0200 @@ -1,1 +1,8 @@ -[orange] \ No newline at end of file +version (unittest) { + [tests/all.d] + buildflags += -unittest + target = unittest + version += OrangeUnitTest +} else { + [orange] +} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/_.d --- a/orange/_.d Mon Oct 04 20:45:32 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -/** - * Copyright: Copyright (c) 2010 Jacob Carlborg. - * Authors: Jacob Carlborg - * Version: Initial created: Jan 26, 2010 - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) - */ -module orange._; - -public: - -import orange.serialization._; -import orange.serialization.archives._; -import orange.util._; \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/core/_.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/core/_.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,12 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: Oct 17, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.core._; + +public: + +import orange.core.io; +import orange.core.string; \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/core/io.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/core/io.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,66 @@ +/** + * Copyright: Copyright (c) 2007-2008 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: 2007 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + * + */ +module orange.core.io; + +version (Tango) +{ + import tango.io.Stdout; + import tango.io.Console; + + import orange.core.string; +} + +else + import std.stdio; + +/** + * Print to the standard output + * + * Params: + * args = what to print + */ +void print (A...)(A args) +{ + version (Tango) + { + const string fmt = "{}{}{}{}{}{}{}{}" + "{}{}{}{}{}{}{}{}" + "{}{}{}{}{}{}{}{}"; + + static assert (A.length <= fmt.length / 2, "mambo.io.print :: too many arguments"); + + Stdout.format(fmt[0 .. args.length * 2], args).flush; + } + + + else + write(args); +} + +/** + * Print to the standard output, adds a new line + * + * Params: + * args = what to print + */ +void println (A...)(A args) +{ + version (Tango) + { + const string fmt = "{}{}{}{}{}{}{}{}" + "{}{}{}{}{}{}{}{}" + "{}{}{}{}{}{}{}{}"; + + static assert (A.length <= fmt.length / 2, "mambo.io.println :: too many arguments"); + + Stdout.formatln(fmt[0 .. args.length * 2], args); + } + + else + writeln(args); +} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/core/string.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/core/string.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,952 @@ +/** + * Copyright: Copyright (c) 2008-2009 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: 2008 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + * + */ +module orange.core.string; + +public import orange.util.collection.Array; + +version (Tango) +{ + static import tango.stdc.stringz; + import tango.text.Unicode : toFold, isDigit; + import tango.text.convert.Utf; + import tango.text.Util; + + alias tango.stdc.stringz.toStringz toStringz; + alias tango.stdc.stringz.toString16z toString16z; + alias tango.stdc.stringz.toString32z toString32z; + + alias tango.stdc.stringz.fromStringz fromStringz; + alias tango.stdc.stringz.fromString16z fromString16z; + alias tango.stdc.stringz.fromString32z fromString32z; + + alias tango.text.convert.Utf.toString16 toString16; + alias tango.text.convert.Utf.toString32 toString32; +} + +else +{ + import std.string; + import std.utf; + static import std.ascii; + static import std.conv; + static import std.array; + + version = Phobos; + + private alias std.string.toLower toFold; + + alias std.utf.toUTF8 toString; + alias std.utf.toUTF16 toString16; + alias std.utf.toUTF32 toString32; + + alias std.string.toStringz toStringz; + alias std.utf.toUTF16z toString16z; + + alias std.ascii.isHexDigit isHexDigit; +} + +import orange.util.Traits; + +version (Tango) +{ + /** + * string alias + */ + alias char[] string; + + /** + * wstring alias + */ + alias wchar[] wstring; + + /** + * dstring alias + */ + alias dchar[] dstring; +} + +/** + * Compares the $(D_PSYMBOL string) to another $(D_PSYMBOL string), ignoring case + * considerations. Two strings are considered equal ignoring case if they are of the + * same length and corresponding characters in the two strings are equal ignoring case. + * + * Params: + * str = The $(D_PSYMBOL string) first string to compare to + * anotherString = The $(D_PSYMBOL string) to compare the first $(D_PSYMBOL string) with + * + * Returns: $(D_KEYWORD true) if the arguments is not $(D_KEYWORD null) and it + * represents an equivalent $(D_PSYMBOL string) ignoring case; $(D_KEYWORD false) otherwise + * + * Throws: AssertException if the length of any of the strings is 0 + * + * See_Also: opEquals(Object) + */ +bool equalsIgnoreCase (string str, string anotherString) +in +{ + assert(str.length > 0, "mambo.string.equalsIgnoreCase: The length of the first string was 0"); + assert(anotherString.length > 0, "mambo.string.equalsIgnoreCase: The length of the second string was 0"); +} +body +{ + if (str == anotherString) + return true; + + return toFold(str) == toFold(anotherString); +} + +/** + * Compares the $(D_PSYMBOL wstring) to another $(D_PSYMBOL wstring), ignoring case + * considerations. Two wstrings are considered equal ignoring case if they are of the + * same length and corresponding characters in the two wstrings are equal ignoring case. + * + * Params: + * str = The $(D_PSYMBOL wstring) first string to compre to + * anotherString = The $(D_PSYMBOL wstring) to compare the first $(D_PSYMBOL wstring) against + * + * Returns: $(D_KEYWORD true) if the argument is not $(D_KEYWORD null) and it + * represents an equivalent $(D_PSYMBOL wstring) ignoring case; (D_KEYWORD + * false) otherwise + * + * Throws: AssertException if the length of any of the wstrings is 0 + * + * See_Also: opEquals(Object) + */ +bool equalsIgnoreCase (wstring str, wstring anotherString) +in +{ + assert(str.length > 0, "mambo.string.equalsIgnoreCase: The length of the first string was 0"); + assert(anotherString.length > 0, "mambo.string.equalsIgnoreCase: The length of the second string was 0"); +} +body +{ + if (str == anotherString) + return true; + + version (Tango) + return toFold(str) == toFold(anotherString); + + else + return toFold(toUTF8(str)) == toFold(toUTF8(anotherString)); +} + +/** + * Compares the $(D_PSYMBOL dstring) to another $(D_PSYMBOL dstring), ignoring case + * considerations. Two wstrings are considered equal ignoring case if they are of the + * same length and corresponding characters in the two wstrings are equal ignoring case. + * + * Params: + * str = The $(D_PSYMBOL dstring) first string to compare to + * anotherString = The $(D_PSYMBOL wstring) to compare the first $(D_PSYMBOL dstring) against + * + * Returns: $(D_KEYWORD true) if the argument is not $(D_KEYWORD null) and it + * represents an equivalent $(D_PSYMBOL dstring) ignoring case; $(D_KEYWORD false) otherwise + * + * Throws: AssertException if the length of any of the dstrings are 0 + * + * See_Also: opEquals(Object) + */ +bool equalsIgnoreCase (dstring str, dstring anotherString) +in +{ + assert(str.length > 0, "mambo.string.equalsIgnoreCase: The length of the first string was 0"); + assert(anotherString.length > 0, "mambo.string.equalsIgnoreCase: The length of the second string was 0"); +} +body +{ + if (str == anotherString) + return true; + + version (Tango) + return toFold(str) == toFold(anotherString); + + else + return toFold(toUTF8(str)) == toFold(toUTF8(anotherString)); +} + +/** + * Returns the char value at the specified index. An index ranges from 0 to length - 1. + * The first $(D_KEYWORD char) value of the sequence is at index 0, the next at index 1, + * and so on, as for array indexing. + * + * Params: + * str = the string to get the $(D_KEYWORD char) from + * index = the index of the $(D_KEYWORD char) value. + * + * Returns: the $(D_KEYWORD char) value at the specified index of the string. + * The first $(D_KEYWORD char) value is at index 0. + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_CODE index) argument is + * not less than the length of the string. + */ +char charAt (string str, size_t index) +in +{ + assert(str.length > 0, "mambo.string.charAt: The length of the string was 0"); + assert(index <= str.length, "mambo.string.charAt: The index was to greater than the length of the string"); +} +body +{ + return str[index]; +} + +/** + * Returns the $(D_KEYWORD char) value at the specified index. An index ranges from 0 to + * length - 1. The first $(D_KEYWORD char) value of the sequence is at index 0, the next + * at index 1, and so on, as for array indexing. + * + * Params: + * str = the wstring to get the $(D_KEYWORD char) from + * index = the index of the $(D_KEYWORD char) value. + * + * Returns: the $(D_KEYWORD char) value at the specified index of the wstring. + * The first $(D_KEYWORD char) value is at index 0. + * + * Throws: AssertException if the length of the wstring is 0 + * Throws: AssertException if the $(D_CODE index) argument is + * not less than the length of the wstring. + */ +wchar charAt (wstring str, size_t index) +in +{ + assert(str.length > 0, "mambo.string.charAt: The length of the string was 0"); + assert(index <= str.length, "mambo.string.charAt: The index was to greater than the length of the string"); +} +body +{ + return str[index]; +} + +/** + * Returns the $(D_KEYWORD char) value at the specified index. An index ranges from 0 to + * length - 1. The first $(D_KEYWORD char) value of the sequence is at index 0, the next + * at index 1, and so on, as for array indexing. + * + * Params: + * str = the dstring to get the $(D_KEYWORD char) from + * index = the index of the $(D_KEYWORD char) value. + * + * Returns: the $(D_KEYWORD char) value at the specified index of the dstring. + * The first $(D_KEYWORD char) value is at index 0. + * + * Throws: AssertException if the length of the dstring is 0 + * Throws: AssertException if the $(D_CODE index) argument is + * not less than the length of the dstring. + */ +dchar charAt (dstring str, size_t index) +in +{ + assert(str.length > 0, "mambo.string.charAt: The length of the string was 0"); + assert(index <= str.length, "mambo.string.charAt: The index was to greater than the length of the string"); +} +body +{ + return str[index]; +} + +/** + * Returns a new string that is a substring of the specified string. The substring begins + * at the specified $(D_PARAM beginIndex) and extends to the character at index + * $(D_PARAM endIndex) - 1. Thus the length of the substring is $(D_PARAM endIndex - beginIndex). + * + * Examples: + * --- + * "hamburger".substring(4, 8) returns "urge" + * "smiles".substring(1, 5) returns "mile" + * --- + * + * Params: + * str = the string to get the substring from + * beginIndex = the beginning index, inclusive. + * endIndex = the ending index, exclusive. + * + * Returns: the specified substring. + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_PARAM beginIndex) is + * larger than $(D_PARAM endIndex). + * Throws: AssertException if $(D_PARAM endIndex) is larger than the + * length of the $(D_PSYMBOL string). + */ +string substring (string str, size_t beginIndex, size_t endIndex) +in +{ + assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); + assert(beginIndex < endIndex, "mambo.string.substring: The first index was greater the second"); + assert(endIndex <= str.length, "mambo.string.substring: The second index was greater then the length of the string"); +} +body +{ + version (Tango) return str[beginIndex .. endIndex].dup; + else return str[beginIndex .. endIndex].idup; +} + +/** + * Returns a new string that is a substring of the specified string. The substring begins + * at the specified $(D_PARAM beginIndex) and extends to the character at index + * $(D_PARAM endIndex) - 1. Thus the length of the substring is $(D_PARAM endIndex - beginIndex). + * + * Examples: + * --- + * "hamburger".substring(4, 8) returns "urge" + * "smiles".substring(1, 5) returns "mile" + * --- + * + * Params: + * str = the string to get the substring from + * beginIndex = the beginning index, inclusive. + * endIndex = the ending index, exclusive. + * + * Returns: the specified substring. + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_PARAM beginIndex) is + * larger than $(D_PARAM endIndex). + * Throws: AssertException if $(D_PARAM endIndex) is larger than the + * length of the $(D_PSYMBOL string). + */ +wstring substring (wstring str, size_t beginIndex, size_t endIndex) +in +{ + assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); + assert(beginIndex < endIndex, "mambo.string.substring: The first index was greater the second"); + assert(endIndex <= str.length, "mambo.string.substring: The second index was greater then the length of the string"); +} +body +{ + version (Tango) return str[beginIndex .. endIndex].dup; + else return str[beginIndex .. endIndex].idup; +} + +/** + * Returns a new string that is a substring of the specified string. The substring begins + * at the specified $(D_PARAM beginIndex) and extends to the character at index + * $(D_PARAM endIndex) - 1. Thus the length of the substring is $(D_PARAM endIndex - beginIndex). + * + * Examples: + * --- + * "hamburger".substring(4, 8) returns "urge" + * "smiles".substring(1, 5) returns "mile" + * --- + * + * Params: + * str = the string to get the substring from + * beginIndex = the beginning index, inclusive. + * endIndex = the ending index, exclusive. + * + * Returns: the specified substring. + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_PARAM beginIndex) is + * larger than $(D_PARAM endIndex). + * Throws: AssertException if $(D_PARAM endIndex) is larger than the + * length of the $(D_PSYMBOL string). + */ +dstring substring (dstring str, size_t beginIndex, size_t endIndex) +in +{ + assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); + assert(beginIndex < endIndex, "mambo.string.substring: The first index was greater the second"); + assert(endIndex <= str.length, "mambo.string.substring: The second index was greater then the length of the string"); +} +body +{ + version (Tango) return str[beginIndex .. endIndex].dup; + else return str[beginIndex .. endIndex].idup; +} + +/** + * Returns a new string that is a substring of the specified string. The substring begins + * with the character at the specified index and extends to the end of the string. + * + * Examples: + * --- + * "unhappy".substring(2) returns "happy" + * "Harbison".substring(3) returns "bison" + * "emptiness".substring(9) returns "" (an empty string) + * --- + * + * Params: + * str = the string to get the substring from + * beginIndex = the beginning index, inclusive + * + * Returns: the specified substring + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_PARAM beginIndex) is + * larger than the length of the string. + */ +string substring (string str, size_t index) +in +{ + assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); + assert(index < str.length, "mambo.string.substring: The index was greater than the length of the string"); +} +body +{ + return str.substring(index, str.length); +} + +/** + * Returns a new string that is a substring of the specified string. The substring begins + * with the character at the specified index and extends to the end of the string. + * + * Examples: + * --- + * "unhappy".substring(2) returns "happy" + * "Harbison".substring(3) returns "bison" + * "emptiness".substring(9) returns "" (an empty string) + * --- + * + * Params: + * str = the string to get the substring from + * beginIndex = the beginning index, inclusive + * + * Returns: the specified substring + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_PARAM beginIndex) is + * larger than the length of the string. + */ +wstring substring (wstring str, size_t index) +in +{ + assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); + assert(index < str.length, "mambo.string.substring: The index was greater than the length of the string"); +} +body +{ + return str.substring(index, str.length); +} + +/** + * Returns a new string that is a substring of the specified string. The substring begins + * with the character at the specified index and extends to the end of the string. + * + * Examples: + * --- + * "unhappy".substring(2) returns "happy" + * "Harbison".substring(3) returns "bison" + * "emptiness".substring(9) returns "" (an empty string) + * --- + * + * Params: + * str = the string to get the substring from + * beginIndex = the beginning index, inclusive + * + * Returns: the specified substring + * + * Throws: AssertException if the length of the string is 0 + * Throws: AssertException if the $(D_PARAM beginIndex) is + * larger than the length of the string. + */ +dstring substring (dstring str, size_t index) +in +{ + assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); + assert(index < str.length, "mambo.string.substring: The index was greater than the length of the string"); +} +body +{ + return str.substring(index, str.length); +} + +/** + * Returns a new string that is a substring of the given string. + * + * This substring is the character sequence that starts at character + * position pos and has a length of n characters. + * + * Params: + * str = the string to get the substring from + * pos = position of a character in the current string to be used + * as starting character for the substring. + * n = Length of the substring. If this value would make the + * substring to span past the end of the current string content, + * only those characters until the end of the string are used. + * size_t.max is the greatest possible value for an element of + * type size_t, therefore, when this value is used, all the + * characters between pos and the end of the string are used as + * the initialization substring. + * + * Returns: a string containing a substring of the given string + * + * Throws: AssertException if pos is greater than the length of the string + */ +string substr (string str, size_t pos = 0, size_t n = size_t.max) +in +{ + assert(pos < str.length, "mambo.string.substr: The given position was greater than the length of the string."); +} +body +{ + size_t end; + + if (n == size_t.max) + end = str.length; + + else + { + end = pos + n; + + if (end > str.length) + end = str.length; + } + + version (Tango) return str[pos .. end].dup; + else return str[pos .. end].idup; +} + +/** + * Returns a new string that is a substring of the given string. + * + * This substring is the character sequence that starts at character + * position pos and has a length of n characters. + * + * Params: + * str = the string to get the substring from + * pos = position of a character in the current string to be used + * as starting character for the substring. + * n = Length of the substring. If this value would make the + * substring to span past the end of the current string content, + * only those characters until the end of the string are used. + * size_t.max is the greatest possible value for an element of + * type size_t, therefore, when this value is used, all the + * characters between pos and the end of the string are used as + * the initialization substring. + * + * Returns: a string containing a substring of the given string + * + * Throws: AssertException if pos is greater than the length of the string + */ +wstring substr (wstring str, size_t pos = 0, size_t n = size_t.max) +in +{ + assert(pos < str.length, "mambo.string.substr: The given position was greater than the length of the string."); +} +body +{ + size_t end; + + if (n == size_t.max) + end = str.length; + + else + { + end = pos + n; + + if (end > str.length) + end = str.length; + } + + version (Tango) return str[pos .. end].dup; + else return str[pos .. end].idup; +} + +/** + * Returns a new string that is a substring of the given string. + * + * This substring is the character sequence that starts at character + * position pos and has a length of n characters. + * + * Params: + * str = the string to get the substring from + * pos = position of a character in the current string to be used + * as starting character for the substring. + * n = Length of the substring. If this value would make the + * substring to span past the end of the current string content, + * only those characters until the end of the string are used. + * size_t.max is the greatest possible value for an element of + * type size_t, therefore, when this value is used, all the + * characters between pos and the end of the string are used as + * the initialization substring. + * + * Returns: a string containing a substring of the given string + * + * Throws: AssertException if pos is greater than the length of the string + */ +dstring substr (dstring str, size_t pos = 0, size_t n = size_t.max) +in +{ + assert(pos < str.length, "mambo.string.substr: The given position was greater than the length of the string."); +} +body +{ + size_t end; + + if (n == size_t.max) + end = str.length; + + else + { + end = pos + n; + + if (end > str.length) + end = str.length; + } + + version (Tango) return str[pos .. end].dup; + else return str[pos .. end].idup; +} + +/** + * Finds the first occurence of sub in str + * + * Params: + * str = the string to find in + * sub = the substring to find + * start = where to start finding + * + * Returns: the index of the substring or size_t.max when nothing was found + */ +size_t find (string str, string sub) +{ + version (Tango) + { + size_t index = str.locatePattern(sub); + + if (index == str.length) + return size_t.max; + + return index; + } + + else + return std.string.indexOf(str, sub); + +} + +/** + * Finds the first occurence of sub in str + * + * Params: + * str = the string to find in + * sub = the substring to find + * start = where to start finding + * + * Returns: the index of the substring or size_t.max when nothing was found + */ +size_t find (wstring str, wstring sub) +{ + version (Tango) + { + size_t index = str.locatePattern(sub); + + if (index == str.length) + return size_t.max; + + return index; + } + + else + return std.string.indexOf(str, sub); +} + +/** + * Finds the first occurence of sub in str + * + * Params: + * str = the string to find in + * sub = the substring to find + * start = where to start finding + * + * Returns: the index of the substring or size_t.max when nothing was found + */ +size_t find (dstring str, dstring sub) +{ + version (Tango) + { + size_t index = str.locatePattern(sub); + + if (index == str.length) + return size_t.max; + + return index; + } + + else + return std.string.indexOf(str, sub); +} + +/** + * Compares to strings, ignoring case differences. Returns 0 if the content + * matches, less than zero if a is "less" than b, or greater than zero + * where a is "bigger". + * + * Params: + * a = the first array + * b = the second array + * end = the index where the comparision will end + * + * Returns: Returns 0 if the content matches, less than zero if a is + * "less" than b, or greater than zero where a is "bigger". + * + * See_Also: mambo.collection.array.compare + */ +int compareIgnoreCase (U = size_t) (string a, string b, U end = U.max) +{ + return a.toFold().compare(b.toFold(), end); +} + +/** + * Compares to strings, ignoring case differences. Returns 0 if the content + * matches, less than zero if a is "less" than b, or greater than zero + * where a is "bigger". + * + * Params: + * a = the first array + * b = the second array + * end = the index where the comparision will end + * + * Returns: Returns 0 if the content matches, less than zero if a is + * "less" than b, or greater than zero where a is "bigger". + * + * See_Also: mambo.collection.array.compare + */ +int compareIgnoreCase (U = size_t) (wstring a, wstring b, U end = U.max) +{ + return a.toFold().compare(b.toFold(), end); +} + +/** + * Compares to strings, ignoring case differences. Returns 0 if the content + * matches, less than zero if a is "less" than b, or greater than zero + * where a is "bigger". + * + * Params: + * a = the first array + * b = the second array + * end = the index where the comparision will end + * + * Returns: Returns 0 if the content matches, less than zero if a is + * "less" than b, or greater than zero where a is "bigger". + * + * See_Also: mambo.collection.array.compare + */ +int compareIgnoreCase (U = size_t) (dstring a, dstring b, U end = U.max) +{ + return a.toFold().compare(b.toFold(), end); +} + +/** + * Compares to strings, ignoring case differences. Returns 0 if the content + * matches, less than zero if a is "less" than b, or greater than zero + * where a is "bigger". + * + * Params: + * a = the first array + * b = the second array + * end = the index where the comparision will end + * + * Returns: Returns 0 if the content matches, less than zero if a is + * "less" than b, or greater than zero where a is "bigger". + * + * See_Also: mambo.string.compareIgnoreCase + */ +alias compareIgnoreCase icompare; + +/** + * Checks if the given character is a hexdecimal digit character. + * Hexadecimal digits are any of: 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F + * + * Params: + * ch = the character to be checked + * + * Returns: true if the given character is a hexdecimal digit character otherwise false + */ +version (Tango) +{ + bool isHexDigit (dchar ch) + { + + switch (ch) + { + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + + default: break; + } + + if (isDigit(ch)) + return true; + + return false; + } +} + +/*version (Tango) +{ + string toString (string str) + { + return str; + } + + string toString (wstring str) + { + return tango.text.convert.Utf.toString(str); + } + + string toString (dstring str) + { + return tango.text.convert.Utf.toString(str); + } +}*/ + +version (Phobos) +{ + /** + * Converts the given string to C-style 0 terminated string. + * + * Params: + * str = the string to convert + * + * Returns: the a C-style 0 terminated string. + */ + dchar* toString32z (dstring str) + { + return (str ~ '\0').dup.ptr; + } + + /** + * Converts a C-style 0 terminated string to a string + * + * Params: + * str = the C-style 0 terminated string + * + * Returns: the converted string + */ + string fromStringz (char* str) + { + return std.conv.to!(string)(str); + } + + /** + * Converts a C-style 0 terminated string to a wstring + * + * Params: + * str = the C-style 0 terminated string + * + * Returns: the converted wstring + */ + wstring fromString16z (wchar* str) + { + return str[0 .. strlen(str)].idup; + } + + /** + * Converts a C-style 0 terminated string to a dstring + * Params: + * str = the C-style 0 terminated string + * + * Returns: the converted dstring + */ + dstring fromString32z (dchar* str) + { + return str[0 .. strlen(str)].idup; + } + + /** + * Gets the length of the given C-style 0 terminated string + * + * Params: + * str = the C-style 0 terminated string to get the length of + * + * Returns: the length of the string + */ + size_t strlen (wchar* str) + { + size_t i = 0; + + if (str) + while(*str++) + ++i; + + return i; + } + + /** + * Gets the length of the given C-style 0 terminated string + * + * Params: + * str = the C-style 0 terminated string to get the length of + * + * Returns: the length of the string + */ + size_t strlen (dchar* str) + { + size_t i = 0; + + if (str) + while(*str++) + ++i; + + return i; + } +} + +T[] replace (T) (T[] source, dchar match, dchar replacement) +{ + static assert(isChar!(T), `The type "` ~ T.stringof ~ `" is not a valid type for this function only strings are accepted`); + + dchar endOfCodeRange; + + static if (is(T == wchar)) + { + const encodedLength = 2; + endOfCodeRange = wchar.init; + } + + else static if (is(T == char)) + { + const encodedLength = 4; + endOfCodeRange = '\x7F'; + } + + if (replacement <= endOfCodeRange && match <= endOfCodeRange) + { + foreach (ref c ; source) + if (c == match) + c = cast(T) replacement; + + return source; + } + + else + { + static if (!is(T == dchar)) + { + T[encodedLength] encodedMatch; + T[encodedLength] encodedReplacement; + + version (Tango) + return source.substitute(encode(encodedMatch, match), encode(encodedReplacement, replacement)); + + else + { + auto matchLength = encode(encodedMatch, match); + auto replacementLength = encode(encodedReplacement, replacement); + + return std.array.replace(source, encodedMatch[0 .. matchLength], encodedReplacement[0 .. replacementLength]); + } + } + } + + return source; +} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/RegisterWrapper.d --- a/orange/serialization/RegisterWrapper.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/RegisterWrapper.d Wed Aug 03 21:58:21 2011 +0200 @@ -6,8 +6,9 @@ */ module orange.serialization.RegisterWrapper; -import orange.serialization.archives.Archive; -import orange.serialization.Serializer; +//import orange.serialization.archives.Archive; +//import orange.serialization.Serializer; +import orange.core.string; class RegisterBase { @@ -16,22 +17,21 @@ class SerializeRegisterWrapper (T, SerializerType) : RegisterBase { - private alias SerializerType.DataType DataType; - private void delegate (T, SerializerType, DataType) dg; + private void delegate (T, SerializerType, string) dg; private bool isDelegate; - this (void delegate (T, SerializerType, DataType) dg) + this (void delegate (T, SerializerType, string) dg) { isDelegate = true; this.dg = dg; } - this (void function (T, SerializerType, DataType) func) + this (void function (T, SerializerType, string) func) { dg.funcptr = func; } - void opCall (T value, SerializerType archive, DataType key) + void opCall (T value, SerializerType archive, string key) { if (dg && isDelegate) dg(value, archive, key); @@ -43,22 +43,21 @@ class DeserializeRegisterWrapper (T, SerializerType) : RegisterBase { - private alias SerializerType.DataType DataType; - private void delegate (ref T, SerializerType, DataType) dg; + private void delegate (ref T, SerializerType, string) dg; private bool isDelegate; - this (void delegate (ref T, SerializerType, DataType) dg) + this (void delegate (ref T, SerializerType, string) dg) { isDelegate = true; this.dg = dg; } - this (void function (ref T, SerializerType, DataType) func) + this (void function (ref T, SerializerType, string) func) { dg.funcptr = func; } - void opCall (ref T value, SerializerType archive, DataType key) + void opCall (ref T value, SerializerType archive, string key) { if (dg && isDelegate) dg(value, archive, key); diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/Serializable.d --- a/orange/serialization/Serializable.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/Serializable.d Wed Aug 03 21:58:21 2011 +0200 @@ -53,6 +53,19 @@ const field = f.stringof; } -package const nonSerializedField = "__nonSerialized"; -package const serializedField = "__serialized"; -package const internalFields = [nonSerializedField[], onDeserializedField, onDeserializingField, onSerializedField, onSerializingField]; \ No newline at end of file +package: + +version (Tango) +{ + const nonSerializedField = "__nonSerialized"; + const serializedField = "__serialized"; + const internalFields = [nonSerializedField[], onDeserializedField, onDeserializingField, onSerializedField, onSerializingField]; +} + +else +{ + mixin( + `enum nonSerializedField = "__nonSerialized"; + enum serializedField = "__serialized"; + enum internalFields = [nonSerializedField[], onDeserializedField, onDeserializingField, onSerializedField, onSerializingField];`); +} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/SerializationException.d --- a/orange/serialization/SerializationException.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/SerializationException.d Wed Aug 03 21:58:21 2011 +0200 @@ -6,7 +6,7 @@ */ module orange.serialization.SerializationException; -import orange.util.string; +import orange.core.string; version (Tango) alias Exception ExceptionBase; diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/Serializer.d --- a/orange/serialization/Serializer.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/Serializer.d Wed Aug 03 21:58:21 2011 +0200 @@ -12,11 +12,13 @@ else { import std.conv; - alias ConvError ConversionException; + alias ConvException ConversionException; } +import orange.core._; import orange.serialization._; -import orange.serialization.archives._; +import orange.serialization.archives.Archive; +import orange.serialization.archives.ArchiveException; import orange.util._; private @@ -31,78 +33,76 @@ alias Mode.serializing serializing; alias Mode.deserializing deserializing; + + private char toUpper (char c) + { + if (c >= 'a' && c <= 'z') + return cast(char) (c - 32); + + return c; + } } -class Serializer (ArchiveType : IArchive) +class Serializer { - static assert(isArchive!(ArchiveType), format!(`The type "`, ArchiveType, `" does not implement the necessary methods to be an archive.`)); - - alias ArchiveType.ErrorCallback ErrorCallback; - alias ArchiveType.DataType DataType; + alias void delegate (ArchiveException exception, string[] data) ErrorCallback; + alias Archive.UntypedData Data; + alias Archive.Id Id; private { - ArchiveType archive; + struct ValueMeta + { + Id id; + string key; + } + + ErrorCallback errorCallback_; + Archive archive; + + size_t keyCounter; + Id idCounter; RegisterBase[string] serializers; RegisterBase[string] deserializers; - size_t keyCounter; + Id[void*] serializedReferences; + void*[Id] deserializedReferences; + + Array[Id] serializedArrays; + void[][Id] deserializedSlices; + + void*[Id] serializedPointers; + void**[Id] deserializedPointers; + + ValueMeta[void*] serializedValues; + void*[Id] deserializedValues; bool hasBegunSerializing; bool hasBegunDeserializing; - void delegate (ArchiveException exception, DataType[] data) throwOnErrorCallback; - void delegate (ArchiveException exception, DataType[] data) doNothingOnErrorCallback; + void delegate (ArchiveException exception, string[] data) throwOnErrorCallback; + void delegate (ArchiveException exception, string[] data) doNothingOnErrorCallback; } - this () + this (Archive archive) { - archive = new ArchiveType; + this.archive = archive; - throwOnErrorCallback = (ArchiveException exception, DataType[] data) { throw exception; }; - doNothingOnErrorCallback = (ArchiveException exception, DataType[] data) { /* do nothing */ }; + throwOnErrorCallback = (ArchiveException exception, string[] data) { throw exception; }; + doNothingOnErrorCallback = (ArchiveException exception, string[] data) { /* do nothing */ }; setThrowOnErrorCallback(); } - - void registerSerializer (T) (string type, void delegate (T, Serializer, DataType) dg) - { - serializers[type] = toSerializeRegisterWrapper(dg); - } - - void registerSerializer (T) (string type, void function (T, Serializer, DataType) func) - { - serializers[type] = toSerializeRegisterWrapper(func); - } - - void registerDeserializer (T) (string type, void delegate (ref T, Serializer, DataType) dg) - { - deserializers[type] = toDeserializeRegisterWrapper(dg); - } - - void registerDeserializer (T) (string type, void function (ref T, Serializer, DataType) func) - { - deserializers[type] = toDeserializeRegisterWrapper(func); - } - - void reset () - { - hasBegunSerializing = false; - hasBegunDeserializing = false; - resetCounters(); - - archive.reset; - } ErrorCallback errorCallback () { - return archive.errorCallback; + return errorCallback_; } ErrorCallback errorCallback (ErrorCallback errorCallback) { - return archive.errorCallback = errorCallback; + return errorCallback_ = errorCallback; } void setThrowOnErrorCallback () @@ -115,44 +115,69 @@ errorCallback = doNothingOnErrorCallback; } - DataType serialize (T) (T value, DataType key = null) + void reset () + { + resetCounters(); + + serializers = null; + deserializers = null; + + serializedReferences = null; + deserializedReferences = null; + + serializedArrays = null; + deserializedSlices = null; + + serializedValues = null; + serializedPointers = null; + + hasBegunSerializing = false; + hasBegunDeserializing = false; + + archive.reset; + } + + Data serialize (T) (T value, string key = null) { if (!hasBegunSerializing) hasBegunSerializing = true; serializeInternal(value, key); - archive.postProcess; + postProcess; - return archive.data; + return archive.untypedData; } - private void serializeInternal (T) (T value, DataType key = null) + private void serializeInternal (T) (T value, string key = null, Id id = Id.max) { if (!key) key = nextKey; - + + if (id == Id.max) + id = nextId; + archive.beginArchiving(); + + static if ( is(T == typedef) ) + serializeTypedef(value, key, id); - static if (isTypeDef!(T)) - serializeTypeDef(value, key); - - static if (isObject!(T)) - serializeObject(value, key); + else static if (isObject!(T)) + serializeObject(value, key, id); else static if (isStruct!(T)) - serializeStruct(value, key); + serializeStruct(value, key, id); else static if (isString!(T)) - serializeString(value, key); + serializeString(value, key, id); else static if (isArray!(T)) - serializeArray(value, key); + serializeArray(value, key, id); else static if (isAssociativeArray!(T)) - serializeAssociativeArray(value, key); + serializeAssociativeArray(value, key, id); else static if (isPrimitive!(T)) - serializePrimitive(value, key); + serializePrimitive(value, key, id); else static if (isPointer!(T)) { @@ -160,11 +185,11 @@ goto error; else - serializePointer(value, key); - } + serializePointer(value, key, id); + } else static if (isEnum!(T)) - serializeEnum(value, key); + serializeEnum(value, key, id); else { @@ -172,13 +197,23 @@ throw new SerializationException(format!(`The type "`, T, `" cannot be serialized.`), __FILE__, __LINE__); } } - - private void serializeObject (T) (T value, DataType key) - { - triggerEvents(serializing, value, { - archive.archive(value, key, { - auto runtimeType = value.classinfo.name; + private void serializeObject (T) (T value, string key, Id id) + { + if (!value) + return archive.archiveNull(T.stringof, key); + + auto reference = getSerializedReference(value); + + if (reference != Id.max) + return archive.archiveReference(key, reference); + + auto runtimeType = value.classinfo.name; + + addSerializedReference(value, id); + + triggerEvents(serializing, value, { + archive.archiveObject(runtimeType, T.stringof, key, id, { if (runtimeType in serializers) { auto wrapper = getSerializerWrapper!(T)(runtimeType); @@ -192,19 +227,19 @@ { if (isBaseClass(value)) throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); - + objectStructSerializeHelper(value); } }); }); } - - private void serializeStruct (T) (T value, DataType key) - { + + private void serializeStruct (T) (T value, string key, Id id) + { + string type = T.stringof; + triggerEvents(serializing, value, { - archive.archive(value, key, { - auto type = toDataType(T.stringof); - + archive.archiveStruct(type, key, id, { if (type in serializers) { auto wrapper = getSerializerWrapper!(T)(type); @@ -223,30 +258,69 @@ }); } - private void serializeString (T) (T value, DataType key) + private void serializeString (T) (T value, string key, Id id) { - archive.archive(value, key); + auto array = Array(cast(void*) value.ptr, value.length, ElementTypeOfArray!(T).sizeof); + + archive.archive(value, key, id); + addSerializedArray(array, id); } + + private void serializeArray (T) (T value, string key, Id id) + { + auto array = Array(value.ptr, value.length, ElementTypeOfArray!(T).sizeof); + + archive.archiveArray(array, arrayToString!(T), key, id, { + foreach (i, e ; value) + serializeInternal(e, toData(i)); + }); - private void serializeArray (T) (T value, DataType key) + addSerializedArray(array, id); + } + + private void serializeAssociativeArray (T) (T value, string key, Id id) { - archive.archive(value, key, { - foreach (i, e ; value) - serializeInternal(e, toDataType(i)); + auto reference = getSerializedReference(value); + + if (reference != Id.max) + return archive.archiveReference(key, reference); + + addSerializedReference(value, id); + + string keyType = KeyTypeOfAssociativeArray!(T).stringof; + string valueType = ValueTypeOfAssociativeArray!(T).stringof; + + archive.archiveAssociativeArray(keyType, valueType, value.length, key, id, { + size_t i; + + foreach(k, v ; value) + { + archive.archiveAssociativeArrayKey(toData(i), { + serializeInternal(k, toData(i)); + }); + + archive.archiveAssociativeArrayValue(toData(i), { + serializeInternal(v, toData(i)); + }); + + i++; + } }); } - - private void serializeAssociativeArray (T) (T value, DataType key) + + private void serializePointer (T) (T value, string key, Id id) { - archive.archive(value, key, { - foreach(k, v ; value) - serializeInternal(v, toDataType(k)); - }); - } + if (!value) + return archive.archiveNull(T.stringof, key); + + auto reference = getSerializedReference(value); + + if (reference != Id.max) + return archive.archiveReference(key, reference); - private void serializePointer (T) (T value, DataType key) - { - archive.archive(value, key, { + addSerializedReference(value, id); + + archive.archivePointer(key, id, { if (key in serializers) { auto wrapper = getSerializerWrapper!(T)(key); @@ -262,29 +336,35 @@ throw new SerializationException(`The value with the key "` ~ to!(string)(key) ~ `"` ~ format!(` of the type "`, T, `" cannot be serialized on its own, either implement orange.serialization.Serializable.isSerializable or register a serializer.`), __FILE__, __LINE__); else - serializeInternal(*value, key); - } + serializeInternal(*value, nextKey); + } + }); + + addSerializedPointer(value, id); + } + + private void serializeEnum (T) (T value, string key, Id id) + { + alias BaseTypeOfEnum!(T) EnumBaseType; + auto val = cast(EnumBaseType) value; + string type = T.stringof; + + archive.archiveEnum(val, type, key, id); + } + + private void serializePrimitive (T) (T value, string key, Id id) + { + archive.archive(value, key, id); + } + + private void serializeTypedef (T) (T value, string key, Id id) + { + archive.archiveTypedef(T.stringof, key, nextId, { + serializeInternal!(BaseTypeOfTypedef!(T))(value, nextKey); }); } - private void serializeEnum (T) (T value, DataType key) - { - archive.archive(value, key); - } - - private void serializePrimitive (T) (T value, DataType key) - { - archive.archive(value, key); - } - - private void serializeTypeDef (T) (T value, DataType key) - { - archive.archive(value, key, { - serializeInternal!(BaseTypeOfTypeDef!(T))(value, key); - }); - } - - T deserialize (T) (DataType data, DataType key = null) + T deserialize (T) (Data data, string key = null) { if (hasBegunSerializing && !hasBegunDeserializing) resetCounters(); @@ -294,87 +374,103 @@ if (!key) key = nextKey; - + archive.beginUnarchiving(data); - return deserializeInternal!(T)(key); + auto value = deserializeInternal!(T)(key); + deserializingPostProcess; + + return value; } - private T deserializeInternal (T) (DataType key) - { - static if (isTypeDef!(T)) - return deserializeTypeDef!(T)(key); - + private T deserializeInternal (T, U) (U keyOrId) + { + static if (isTypedef!(T)) + return deserializeTypedef!(T)(keyOrId); + else static if (isObject!(T)) - return deserializeObject!(T)(key); + return deserializeObject!(T)(keyOrId); else static if (isStruct!(T)) - return deserializeStruct!(T)(key); + return deserializeStruct!(T)(keyOrId); else static if (isString!(T)) - return deserializeString!(T)(key); - + return deserializeString!(T)(keyOrId); + else static if (isArray!(T)) - return deserializeArray!(T)(key); + return deserializeArray!(T)(keyOrId); else static if (isAssociativeArray!(T)) - return deserializeAssociativeArray!(T)(key); + return deserializeAssociativeArray!(T)(keyOrId); else static if (isPrimitive!(T)) - return deserializePrimitive!(T)(key); + return deserializePrimitive!(T)(keyOrId); else static if (isPointer!(T)) { static if (isFunctionPointer!(T)) goto error; - - return deserializePointer!(T)(key); + Id id; + return deserializePointer!(T)(keyOrId, id); } - + else static if (isEnum!(T)) - return deserializeEnum!(T)(key); - + return deserializeEnum!(T)(keyOrId); + else { error: throw new SerializationException(format!(`The type "`, T, `" cannot be deserialized.`), __FILE__, __LINE__); } } + + private T deserializeObject (T, U) (U keyOrId) + { + auto id = deserializeReference(keyOrId); - private T deserializeObject (T) (DataType key) - { - T value = archive.unarchive!(T)(key, (T value) { + if (auto reference = getDeserializedReference!(T)(id)) + return *reference; + + T value; + Object val = value; + nextId; + + archive.unarchiveObject(keyOrId, id, val, { triggerEvents(deserializing, value, { + value = cast(T) val; auto runtimeType = value.classinfo.name; if (runtimeType in deserializers) { auto wrapper = getDeserializerWrapper!(T)(runtimeType); - wrapper(value, this, key); + wrapper(value, this, keyOrId); } else static if (isSerializable!(T, Serializer)) - value.fromData(this, key); + value.fromData(this, keyOrId); else { if (isBaseClass(value)) throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a deserializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); - + objectStructDeserializeHelper(value); } }); - - return value; }); + addDeserializedReference(value, id); + return value; } - - private T deserializeStruct (T) (DataType key) - { - return archive.unarchive!(T)(key, (T value) { + + private T deserializeStruct (T) (string key) + { + T value; + nextId; + + archive.unarchiveStruct(key, { triggerEvents(deserializing, value, { - auto type = toDataType(T.stringof); + auto type = toData(T.stringof); if (type in deserializers) { @@ -391,39 +487,132 @@ objectStructDeserializeHelper(value); } }); - - return value; }); + + return value; } - private T deserializeString (T) (DataType key) + private T deserializeString (T) (string key) { - return archive.unarchive!(T)(key); - } + auto slice = deserializeSlice(key); + + if (auto tmp = getDeserializedSlice!(T)(slice)) + return *tmp; + + T value; + + if (slice.id != size_t.max) + { + static if (is(T == string)) + value = toSlice(archive.unarchiveString(slice.id), slice); + + else static if (is(T == wstring)) + value = toSlice(archive.unarchiveWstring(slice.id), slice); + + else static if (is(T == dstring)) + value = toSlice(archive.unarchiveDstring(slice.id), slice); + } + + else + { + static if (is(T == string)) + value = archive.unarchiveString(key, slice.id); + + else static if (is(T == wstring)) + value = archive.unarchiveWstring(key, slice.id); + + else static if (is(T == dstring)) + value = archive.unarchiveDstring(key, slice.id); + } - private T deserializeArray (T) (DataType key) + addDeserializedSlice(value, slice.id); + + return value; + } + + private T deserializeArray (T) (string key) { - return archive.unarchive!(T)(key, (T value) { + auto slice = deserializeSlice(key); + + if (auto tmp = getDeserializedSlice!(T)(slice)) + return *tmp; + + T value; + + auto dg = (size_t length) { + value.length = length; + foreach (i, ref e ; value) - e = deserializeInternal!(typeof(e))(toDataType(i)); + e = deserializeInternal!(typeof(e))(toData(i)); + }; + + if (slice.id != size_t.max) + { + archive.unarchiveArray(slice.id, dg); + addDeserializedSlice(value, slice.id); + + return toSlice(value, slice); + } + + else + { + slice.id = archive.unarchiveArray(key, dg); + + if (auto a = slice.id in deserializedSlices) + return cast(T) *a; + + addDeserializedSlice(value, slice.id); return value; - }); + } } + + private T deserializeAssociativeArray (T) (string key) + { + auto id = deserializeReference(key); + + if (auto reference = getDeserializedReference!(T)(id)) + return *reference; + + T value; + + alias KeyTypeOfAssociativeArray!(T) Key; + alias ValueTypeOfAssociativeArray!(T) Value; + + id = archive.unarchiveAssociativeArray(key, (size_t length) { + for (size_t i = 0; i < length; i++) + { + Key aaKey; + Value aaValue; + auto k = toData(i); + + archive.unarchiveAssociativeArrayKey(k, { + aaKey = deserializeInternal!(Key)(k); + }); + + archive.unarchiveAssociativeArrayValue(k, { + aaValue = deserializeInternal!(Value)(k); + }); + + value[aaKey] = aaValue; + } + }); + + addDeserializedReference(value, id); + + return value; + } + + private T deserializePointer (T) (string key, out Id id) + { + id = deserializeReference(key); - private T deserializeAssociativeArray (T) (DataType key) - { - return archive.unarchive!(T)(key, (T value) { - foreach (k, v ; archive.unarchiveAssociativeArrayVisitor!(T)) - value[k] = v; - - return value; - }); - } - - private T deserializePointer (T) (DataType key) - { - return archive.unarchive!(T)(key, (T value) { + if (auto reference = getDeserializedReference!(T)(id)) + return *reference; + + T value = new BaseTypeOfPointer!(T); + + auto pointerId = archive.unarchivePointer(key, { if (key in deserializers) { auto wrapper = getDeserializerWrapper!(T)(key); @@ -439,45 +628,85 @@ throw new SerializationException(`The value with the key "` ~ to!(string)(key) ~ `"` ~ format!(` of the type "`, T, `" cannot be deserialized on its own, either implement orange.serialization.Serializable.isSerializable or register a deserializer.`), __FILE__, __LINE__); else - *value = deserializeInternal!(BaseTypeOfPointer!(T))(key); + { + auto k = nextKey; + id = deserializeReference(k); + + if (id != Id.max) + return; + + *value = deserializeInternal!(BaseTypeOfPointer!(T))(k); + } } - - return value; }); + + addDeserializedReference(value, pointerId); + + return value; + } + + private T deserializeEnum (T) (string key) + { + alias BaseTypeOfEnum!(T) Enum; + + const functionName = toUpper(Enum.stringof[0]) ~ Enum.stringof[1 .. $]; + mixin("return cast(T) archive.unarchiveEnum" ~ functionName ~ "(key);"); } - private T deserializeEnum (T) (DataType key) + private T deserializePrimitive (T, U) (U keyOrId) { - return archive.unarchive!(T)(key); - } - - private T deserializePrimitive (T) (DataType key) - { - return archive.unarchive!(T)(key); + const functionName = toUpper(T.stringof[0]) ~ T.stringof[1 .. $]; + mixin("return archive.unarchive" ~ functionName ~ "(keyOrId);"); } - private T deserializeTypeDef (T) (DataType key) + private T deserializeTypedef (T, U) (U keyOrId) { - return archive.unarchive!(T)(key, (T value) { - return deserializeInternal!(BaseTypeOfTypeDef!(T))(key); + T value; + + archive.unarchiveTypedef!(T)(key, { + value = cast(T) deserializeInternal!(BaseTypeOfTypedef!(T))(nextKey); }); + + return value; + } + + private Id deserializeReference (string key) + { + return archive.unarchiveReference(key); + } + + private Slice deserializeSlice (string key) + { + return archive.unarchiveSlice(key); } private void objectStructSerializeHelper (T) (ref T value) { static assert(isStruct!(T) || isObject!(T), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are objects and structs.`)); - const nonSerializedFields = collectAnnotations!(nonSerializedField, T); + + version (Tango) + const nonSerializedFields = collectAnnotations!(nonSerializedField, T); + + else + mixin(`enum nonSerializedFields = collectAnnotations!(nonSerializedField, T);`); foreach (i, dummy ; typeof(T.tupleof)) { - const field = nameOfFieldAt!(T, i); + version (Tango) + const field = nameOfFieldAt!(T, i); + + else + mixin(`enum field = nameOfFieldAt!(T, i);`); - static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) + static if (!ctfeContains!(string)(internalFields, field) && !ctfeContains!(string)(nonSerializedFields, field)) { alias typeof(T.tupleof[i]) Type; - Type v = value.tupleof[i]; - serializeInternal(v, toDataType(field)); - } + Type v = value.tupleof[i]; + auto id = nextId; + + addSerializedValue(value.tupleof[i], id, toData(keyCounter)); + serializeInternal(v, toData(field), id); + } } static if (isObject!(T) && !is(T == Object)) @@ -487,20 +716,42 @@ private void objectStructDeserializeHelper (T) (ref T value) { static assert(isStruct!(T) || isObject!(T), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are objects and structs.`)); - const nonSerializedFields = collectAnnotations!(nonSerializedField, T); + + version (Tango) + const nonSerializedFields = collectAnnotations!(nonSerializedField, T); + + else + mixin(`enum nonSerializedFields = collectAnnotations!(nonSerializedField, T);`); foreach (i, dummy ; typeof(T.tupleof)) { - const field = nameOfFieldAt!(T, i); + version (Tango) + const field = nameOfFieldAt!(T, i); + + else + mixin(`enum field = nameOfFieldAt!(T, i);`); - static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) + static if (!ctfeContains!(string)(internalFields, field) && !ctfeContains!(string)(nonSerializedFields, field)) { alias TypeOfField!(T, field) Type; - auto fieldValue = deserializeInternal!(Type)(toDataType(field)); - value.tupleof[i] = fieldValue; + + static if (isPointer!(Type)) + { + Id id; + value.tupleof[i] = deserializePointer!(Type)(toData(field), id); + addDeserializedPointer(value.tupleof[i], id); + } + + else + { + auto fieldValue = deserializeInternal!(Type)(toData(field)); + value.tupleof[i] = fieldValue; + } + + addDeserializedValue(value.tupleof[i], nextId); } } - + static if (isObject!(T) && !is(T == Object)) deserializeBaseTypes(value); } @@ -508,10 +759,10 @@ private void serializeBaseTypes (T : Object) (T value) { alias BaseTypeTupleOf!(T)[0] Base; - + static if (!is(Base == Object)) { - archive.archiveBaseClass!(Base)(nextKey); + archive.archiveBaseClass(Base.stringof, nextKey, nextId); Base base = value; objectStructSerializeHelper(base); } @@ -527,6 +778,90 @@ Base base = value; objectStructDeserializeHelper(base); } + } + + private void addSerializedReference (T) (T value, Id id) + { + static assert(isReference!(T) || isAssociativeArray!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object, pointer or associative array.`)); + + serializedReferences[cast(void*) value] = id; + } + + private void addDeserializedReference (T) (T value, Id id) + { + static assert(isReference!(T) || isAssociativeArray!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object, pointer or associative array.`)); + + deserializedReferences[id] = cast(void*) value; + } + + private void addDeserializedSlice (T) (T value, Id id) + { + static assert(isArray!(T) || isString!(T), format!(`The given type "`, T, `" is not a slice type, i.e. array or string.`)); + + deserializedSlices[id] = cast(void[]) value; + } + + private void addSerializedValue (T) (ref T value, Id id, string key) + { + serializedValues[&value] = ValueMeta(id, key); + } + + private void addDeserializedValue (T) (ref T value, Id id) + { + deserializedValues[id] = &value; + } + + private void addSerializedPointer (T) (T value, Id id) + { + serializedPointers[id] = value; + } + + private void addDeserializedPointer (T) (ref T value, Id id) + { + deserializedPointers[id] = cast(void**) &value; + } + + private Id getSerializedReference (T) (T value) + { + if (auto tmp = cast(void*) value in serializedReferences) + return *tmp; + + return Id.max; + } + + private T* getDeserializedReference (T) (Id id) + { + if (auto reference = id in deserializedReferences) + return cast(T*) reference; + + return null; + } + + private T* getDeserializedSlice (T) (Slice slice) + { + if (auto array = slice.id in deserializedSlices) + return &(cast(T) *array)[slice.offset .. slice.offset + slice.length]; // dereference the array, cast it to the right type, + // slice it and then return a pointer to the result + return null; + } + + private T* getDeserializedArray (T) (Id id) + { + if (auto array = id in deserializedSlices) + return cast(T*) array; + } + + private T* getDeserializedValue (T) (Id id) + { + if (auto value = id in deserializedValues) + return cast(T*) value; + + return null; + } + + private T[] toSlice (T) (T[] array, Slice slice) + { + return array[slice.offset .. slice.offset + slice.length]; } private SerializeRegisterWrapper!(T, Serializer) getSerializerWrapper (T) (string type) @@ -549,33 +884,95 @@ assert(false, "throw exception here"); } - private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, DataType) dg) + private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, string) dg) { return new SerializeRegisterWrapper!(T, Serializer)(dg); } - private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void function (T, Serializer, DataType) func) + private SerializeRegisterWrapper!(T, Serializer) toSerializeRegisterWrapper (T) (void function (T, Serializer, string) func) { return new SerializeRegisterWrapper!(T, Serializer)(func); } - private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, DataType) dg) + private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, string) dg) { return new DeserializeRegisterWrapper!(T, Serializer)(dg); } - private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, DataType) func) + private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, string) func) { return new DeserializeRegisterWrapper!(T, Serializer)(func); } - private DataType toDataType (T) (T value) + private void addSerializedArray (Array array, Id id) + { + serializedArrays[id] = array; + } + + private void postProcess () + { + postProcessArrays(); + postProcessPointers(); + } + + private void postProcessArrays () { - try - return to!(DataType)(value); + bool foundSlice = true; - catch (ConversionException e) - throw new SerializationException(e); + foreach (sliceKey, slice ; serializedArrays) + { + foreach (arrayKey, array ; serializedArrays) + { + if (slice.isSliceOf(array) && slice != array) + { + auto s = Slice(slice.length, (slice.ptr - array.ptr) / slice.elementSize); + archive.archiveSlice(s, sliceKey, arrayKey); + foundSlice = true; + break; + } + + else + foundSlice = false; + } + + if (!foundSlice) + archive.postProcessArray(sliceKey); + } + } + + private void postProcessPointers () + { + foreach (pointerId, value ; serializedPointers) + { + if (auto valueMeta = value in serializedValues) + archive.archivePointer(valueMeta.id, valueMeta.key, pointerId); + + else + archive.postProcessPointer(pointerId); + } + } + + private void deserializingPostProcess () + { + deserializingPostProcessPointers; + } + + private void deserializingPostProcessPointers () + { + foreach (pointeeId, pointee ; deserializedValues) + { + if (auto pointer = pointeeId in deserializedPointers) + **pointer = pointee; + } + } + + private template arrayToString (T) + { + version (Tango) + const arrayToString = ElementTypeOfArray!(T).stringof; + + else + mixin(`enum arrayToString = ElementTypeOfArray!(T).stringof;`); } private bool isBaseClass (T) (T value) @@ -586,14 +983,30 @@ return T.stringof != name[index + 1 .. $]; } - private DataType nextKey () + private Id nextId () + { + return idCounter++; + } + + private string nextKey () { - return toDataType(keyCounter++); + return toData(keyCounter++); + } + + private string prevKey () + { + return toData(--keyCounter); } private void resetCounters () { keyCounter = 0; + idCounter = 0; + } + + private string toData (T) (T value) + { + return to!(string)(value); } private void triggerEvent (string name, T) (T value) @@ -620,9 +1033,9 @@ else triggerEvent!(onDeserializingField)(value); - + dg(); - + if (mode == serializing) triggerEvent!(onSerializedField)(value); diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/archives/Archive.d --- a/orange/serialization/archives/Archive.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/archives/Archive.d Wed Aug 03 21:58:21 2011 +0200 @@ -12,56 +12,198 @@ else { import std.conv; - alias ConvError ConversionException; + alias ConvException ConversionException; } import orange.serialization.archives.ArchiveException; +import orange.core.string; + +private enum ArchiveMode +{ + archiving, + unarchiving +} + +struct Array +{ + version (Tango) + void* ptr; + + else + mixin(`const(void)* ptr;`); + + size_t length; + size_t elementSize; + + bool isSliceOf (Array b) + { + return ptr >= b.ptr && ptr + length * elementSize <= b.ptr + b.length * b.elementSize; + } +} struct Slice { size_t length; - void* ptr; - - static Slice opCall (T) (T[] value) - { - Slice slice; - slice.length = value.length; - slice.ptr = value.ptr; - - return slice; - } -} - -interface IArchive -{ - void beginArchiving (); - void reset (); + size_t offset; + size_t id = size_t.max; } -abstract class Archive (U) : IArchive +interface Archive { - version (Tango) alias U[] DataType; - else mixin ("alias immutable(U)[] DataType;"); + alias size_t Id; + + version (Tango) alias void[] UntypedData; + else mixin ("alias immutable(void)[] UntypedData;"); + + void beginArchiving (); + void beginUnarchiving (UntypedData data); + + UntypedData untypedData (); + void reset (); + + void archiveArray (Array array, string type, string key, Id id, void delegate () dg); + void archiveAssociativeArray (string keyType, string valueType, size_t length, string key, Id id, void delegate () dg); + void archiveAssociativeArrayKey (string key, void delegate () dg); + void archiveAssociativeArrayValue (string key, void delegate () dg); + + void archiveEnum (bool value, string baseType, string key, Id id); + void archiveEnum (byte value, string baseType, string key, Id id); + void archiveEnum (char value, string baseType, string key, Id id); + void archiveEnum (dchar value, string baseType, string key, Id id); + void archiveEnum (int value, string baseType, string key, Id id); + void archiveEnum (long value, string baseType, string key, Id id); + void archiveEnum (short value, string baseType, string key, Id id); + void archiveEnum (ubyte value, string baseType, string key, Id id); + void archiveEnum (uint value, string baseType, string key, Id id); + void archiveEnum (ulong value, string baseType, string key, Id id); + void archiveEnum (ushort value, string baseType, string key, Id id); + void archiveEnum (wchar value, string baseType, string key, Id id); - alias void delegate (ArchiveException exception, DataType[] data) ErrorCallback; + void archiveBaseClass (string type, string key, Id id); + void archiveNull (string type, string key); + void archiveObject (string runtimeType, string type, string key, Id id, void delegate () dg); + void archivePointer (string key, Id id, void delegate () dg); + void archivePointer (Id pointeeId, string key, Id id); + void archiveReference (string key, Id id); + void archiveSlice (Slice slice, Id sliceId, Id arrayId); + void archiveStruct (string type, string key, Id id, void delegate () dg); + void archiveTypedef (string type, string key, Id id, void delegate () dg); + + void archive (string value, string key, Id id); + void archive (wstring value, string key, Id id); + void archive (dstring value, string key, Id id); + void archive (bool value, string key, Id id); + void archive (byte value, string key, Id id); + //void archive (cdouble value, string key, Id id); // currently not supported by to!() + //void archive (cent value, string key, Id id); + //void archive (cfloat value, string key, Id id); // currently not supported by to!() + void archive (char value, string key, Id id); // currently not implemented but a reserved keyword + //void archive (creal value, string key, Id id); // currently not supported by to!() + void archive (dchar value, string key, Id id); + void archive (double value, string key, Id id); + void archive (float value, string key, Id id); + //void archive (idouble value, string key, Id id); // currently not supported by to!() + //void archive (ifloat value, string key, Id id); // currently not supported by to!() + void archive (int value, string key, Id id); + //void archive (ireal value, string key, Id id); // currently not supported by to!() + void archive (long value, string key, Id id); + void archive (real value, string key, Id id); + void archive (short value, string key, Id id); + void archive (ubyte value, string key, Id id); + //void archive (ucent value, string key, Id id); // currently not implemented but a reserved keyword + void archive (uint value, string key, Id id); + void archive (ulong value, string key, Id id); + void archive (ushort value, string key, Id id); + void archive (wchar value, string key, Id id); + + Id unarchiveArray (string key, void delegate (size_t length) dg); + void unarchiveArray (Id id, void delegate (size_t length) dg); + Id unarchiveAssociativeArray (string type, void delegate (size_t length) dg); + void unarchiveAssociativeArrayKey (string key, void delegate () dg); + void unarchiveAssociativeArrayValue (string key, void delegate () dg); - ErrorCallback errorCallback; + bool unarchiveEnumBool (string key); + byte unarchiveEnumByte (string key); + char unarchiveEnumChar (string key); + dchar unarchiveEnumDchar (string key); + int unarchiveEnumInt (string key); + long unarchiveEnumLong (string key); + short unarchiveEnumShort (string key); + ubyte unarchiveEnumUbyte (string key); + uint unarchiveEnumUint (string key); + ulong unarchiveEnumUlong (string key); + ushort unarchiveEnumUshort (string key); + wchar unarchiveEnumWchar (string key); + + // Object unarchiveBaseClass (string key); + // void unarchiveNull (string key); + void unarchiveObject (string key, out Id id, out Object result, void delegate () dg); + Id unarchivePointer (string key, void delegate () dg); + Id unarchiveReference (string key); + Slice unarchiveSlice (string key); + void unarchiveStruct (string key, void delegate () dg); + void unarchiveTypedef (string key, void delegate () dg); + + string unarchiveString (string key, out Id id); + wstring unarchiveWstring (string key, out Id id); + dstring unarchiveDstring (string key, out Id id); - abstract void beginArchiving (); - abstract void beginUnarchiving (DataType data); - abstract DataType data (); - abstract void reset (); + string unarchiveString (Id id); + wstring unarchiveWstring (Id id); + dstring unarchiveDstring (Id id); + bool unarchiveBool (string key); + byte unarchiveByte (string key); + //cdouble unarchiveCdouble (string key); // currently not supported by to!() + //cent unarchiveCent (string key); // currently not implemented but a reserved keyword + //cfloat unarchiveCfloat (string key); // currently not supported by to!() + char unarchiveChar (string key); // currently not implemented but a reserved keyword + //creal unarchiveCreal (string key); // currently not supported by to!() + dchar unarchiveDchar (string key); + double unarchiveDouble (string key); + float unarchiveFloat (string key); + //idouble unarchiveIdouble (string key); // currently not supported by to!() + //ifloat unarchiveIfloat (string key); // currently not supported by to!()*/ + int unarchiveInt (string key); + //int unarchiveInt (Id id); + //ireal unarchiveIreal (string key); // currently not supported by to!() + long unarchiveLong (string key); + real unarchiveReal (string key); + short unarchiveShort (string key); + ubyte unarchiveUbyte (string key); + //ucent unarchiveCcent (string key); // currently not implemented but a reserved keyword + uint unarchiveUint (string key); + ulong unarchiveUlong (string key); + ushort unarchiveUshort (string key); + wchar unarchiveWchar (string key); - protected DataType toDataType (T) (T value) + void postProcessArray (Id id); + void postProcessPointer (Id id); +} + +abstract class Base (U) : Archive +{ + version (Tango) alias U[] Data; + else mixin ("alias immutable(U)[] Data;"); + + alias void delegate (ArchiveException exception, string[] data) ErrorCallback; + + protected ErrorCallback errorCallback; + + protected this (ErrorCallback errorCallback) + { + this.errorCallback = errorCallback; + } + + protected Data toData (T) (T value) { try - return to!(DataType)(value); + return to!(Data)(value); catch (ConversionException e) throw new ArchiveException(e); } - protected T fromDataType (T) (DataType value) + protected T fromData (T) (Data value) { try return to!(T)(value); @@ -70,6 +212,11 @@ throw new ArchiveException(e); } + protected Id toId (Data value) + { + return fromData!(Id)(value); + } + protected bool isSliceOf (T, U = T) (T[] a, U[] b) { void* aPtr = a.ptr; diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/archives/ArchiveException.d --- a/orange/serialization/archives/ArchiveException.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/archives/ArchiveException.d Wed Aug 03 21:58:21 2011 +0200 @@ -7,7 +7,7 @@ module orange.serialization.archives.ArchiveException; import orange.serialization.SerializationException; -import orange.util.string; +import orange.core.string; class ArchiveException : SerializationException { diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/serialization/archives/XMLArchive.d --- a/orange/serialization/archives/XMLArchive.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/serialization/archives/XMLArchive.d Wed Aug 03 21:58:21 2011 +0200 @@ -12,112 +12,79 @@ else import std.conv; +import orange.core._; import orange.serialization.archives._; +import orange.serialization.Serializer; import orange.util._; import orange.xml.XMLDocument; -private enum ArchiveMode +final class XMLArchive (U = char) : Base!(U) { - archiving, - unarchiving -} - -class XMLArchive (U = char) : Archive!(U) -{ - static assert (isChar!(U), format!(`The given type "`, U, `" is not a valid type. Valid types are: "char", "wchar" and "dchar".`)); - + private alias Archive.Id Id; + private struct Tags { - static const DataType structTag = "struct"; - static const DataType dataTag = "data"; - static const DataType archiveTag = "archive"; - static const DataType arrayTag = "array"; - static const DataType objectTag = "object"; - static const DataType baseTag = "base"; - static const DataType stringTag = "string"; - static const DataType referenceTag = "reference"; - static const DataType pointerTag = "pointer"; - static const DataType associativeArrayTag = "associativeArray"; - static const DataType typedefTag = "typedef"; - static const DataType nullTag = "null"; - static const DataType enumTag = "enum"; - static const DataType sliceTag = "slice"; + static const Data structTag = "struct"; + static const Data dataTag = "data"; + static const Data archiveTag = "archive"; + static const Data arrayTag = "array"; + static const Data objectTag = "object"; + static const Data baseTag = "base"; + static const Data stringTag = "string"; + static const Data referenceTag = "reference"; + static const Data pointerTag = "pointer"; + static const Data associativeArrayTag = "associativeArray"; + static const Data typedefTag = "typedef"; + static const Data nullTag = "null"; + static const Data enumTag = "enum"; + static const Data sliceTag = "slice"; + static const Data elementTag = "element"; + static const Data keyTag = "key"; + static const Data valueTag = "value"; } private struct Attributes { - static const DataType typeAttribute = "type"; - static const DataType versionAttribute = "version"; - static const DataType lengthAttribute = "length"; - static const DataType keyAttribute = "key"; - static const DataType runtimeTypeAttribute = "runtimeType"; - static const DataType idAttribute = "id"; - static const DataType keyTypeAttribute = "keyType"; - static const DataType valueTypeAttribute = "valueType"; - static const DataType offsetAttribute = "offset"; + static const Data invalidAttribute = "\0"; + static const Data typeAttribute = "type"; + static const Data versionAttribute = "version"; + static const Data lengthAttribute = "length"; + static const Data keyAttribute = "key"; + static const Data runtimeTypeAttribute = "runtimeType"; + static const Data idAttribute = "id"; + static const Data keyTypeAttribute = "keyType"; + static const Data valueTypeAttribute = "valueType"; + static const Data offsetAttribute = "offset"; + static const Data baseTypeAttribute = "baseType"; + } + + private struct Node + { + XMLDocument!(U).Node parent; + XMLDocument!(U).Node node; + Id id; + string key; } private { - struct ArrayNode - { - XMLDocument!(U).Node parent; - XMLDocument!(U).Node node; - DataType id; - DataType key; - } - - struct Array - { - void* ptr; - size_t length; - size_t elementSize; - - static Array opCall (T) (T[] value) - { - Array array; - array.ptr = value.ptr; - array.length = value.length; - array.elementSize = T.sizeof; - - return array; - } - - bool isSliceOf (Array b) - { - return ptr >= b.ptr && ptr + length * elementSize <= b.ptr + b.length * b.elementSize; - } - } - - struct Slice - { - size_t length; - size_t offset; - DataType id; - } - - DataType archiveType = "org.dsource.orange.xml"; - DataType archiveVersion = "0.1"; + Data archiveType = "org.dsource.orange.xml"; + Data archiveVersion = "1.0.0"; XMLDocument!(U) doc; doc.Node lastElement; - //DocPrinter!(U) printer; - doc.Node lastElementSaved; bool hasBegunArchiving; bool hasBegunUnarchiving; - DataType[void*] archivedReferences; - void*[DataType] unarchivedReferences; - - ArrayNode[Array] arraysToBeArchived; - void[][DataType] unarchivedSlices; - - size_t idCounter; + Node[Id] archivedArrays; + Node[Id] archivedPointers; + void[][Data] unarchivedSlices; } - this () + this (ErrorCallback errorCallback = null) { + super(errorCallback); doc = new XMLDocument!(U); } @@ -135,8 +102,10 @@ } } - public void beginUnarchiving (DataType data) + public void beginUnarchiving (UntypedData untypedData) { + auto data = cast(Data) untypedData; + if (!hasBegunUnarchiving) { doc.parse(data); @@ -151,411 +120,683 @@ { if (errorCallback) { + auto dataTag = to!(string)(Tags.dataTag); + if (set.nodes.length == 0) - errorCallback(new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `The "` ~ to!(string)(Tags.dataTag) ~ `" tag could not be found.`, __FILE__, __LINE__), [Tags.dataTag]); + errorCallback(new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `The "` ~ to!(string)(Tags.dataTag) ~ `" tag could not be found.`, __FILE__, __LINE__), [dataTag]); else - errorCallback(new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, __FILE__, __LINE__), [Tags.dataTag]); + errorCallback(new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, __FILE__, __LINE__), [dataTag]); } } } } - public DataType data () + UntypedData untypedData () { - /*if (!printer) - printer = new DocPrinter!(U); - - return printer.print(doc);*/ - return doc.toString(); } - public void reset () + Data data () + { + return doc.toString; + } + + void reset () { hasBegunArchiving = false; hasBegunUnarchiving = false; - idCounter = 0; doc.reset; } - private void begin () - { - lastElementSaved = lastElement; - } - - private void end () + void archiveArray (Array array, string type, string key, Id id, void delegate () dg) { - lastElement = lastElementSaved; - } - - public void archive (T) (T value, DataType key, void delegate () dg = null) - { - if (!hasBegunArchiving) - beginArchiving(); - restore(lastElement) in { - bool callDelegate = true; - - static if (isTypeDef!(T)) - archiveTypeDef(value, key); - - else static if (isObject!(T)) - archiveObject(value, key, callDelegate); - - else static if (isStruct!(T)) - archiveStruct(value, key); - - else static if (isString!(T)) - archiveString(value, key); - - else static if (isArray!(T)) - archiveArray(value, key); - - else static if (isAssociativeArray!(T)) - archiveAssociativeArray(value, key); - - else static if (isPrimitive!(T)) - archivePrimitive(value, key); - - else static if (isPointer!(T)) - archivePointer(value, key, callDelegate); - - else static if (isEnum!(T)) - archiveEnum(value, key); - - else - static assert(false, format!(`The type "`, T, `" cannot be archived.`)); - - if (callDelegate && dg) - dg(); + internalArchiveArray(array, type, key, id, Tags.arrayTag); + dg(); }; } - private void archiveObject (T) (T value, DataType key, ref bool callDelegate) - { - if (!value) - { - lastElement.element(Tags.nullTag) - .attribute(Attributes.typeAttribute, toDataType(T.stringof)) - .attribute(Attributes.keyAttribute, key); - callDelegate = false; - } - - else if (auto reference = getArchivedReference(value)) - { - archiveReference(key, reference); - callDelegate = false; - } - - else - { - DataType id = nextId; - - lastElement = lastElement.element(Tags.objectTag) - .attribute(Attributes.runtimeTypeAttribute, toDataType(value.classinfo.name)) - .attribute(Attributes.typeAttribute, toDataType(T.stringof)) - .attribute(Attributes.keyAttribute, key) - .attribute(Attributes.idAttribute, id); - - addArchivedReference(value, id); - } - } - - private void archiveStruct (T) (T value, DataType key) + private void internalArchiveArray(Array array, string type, string key, Id id, Data tag, Data content = null) { - lastElement = lastElement.element(Tags.structTag) - .attribute(Attributes.typeAttribute, toDataType(T.stringof)) - .attribute(Attributes.keyAttribute, key); - } - - private void archiveString (T) (T value, DataType key) - { - archiveArrayImpl(value, key, Tags.stringTag, toDataType(value)); - } - - private void archiveArray (T) (T value, DataType key) - { - archiveArrayImpl(value, key, Tags.arrayTag); - } - - private void archiveArrayImpl (T) (T value, DataType key, DataType tag, DataType content = null) - { - DataType id = nextId; auto parent = lastElement; - if (value.length == 0) + if (array.length == 0) lastElement = lastElement.element(tag); else lastElement = doc.createNode(tag, content); - lastElement.attribute(Attributes.typeAttribute, toDataType(BaseTypeOfArray!(T).stringof)) - .attribute(Attributes.lengthAttribute, toDataType(value.length)) - .attribute(Attributes.keyAttribute, key) - .attribute(Attributes.idAttribute, id); - - arraysToBeArchived[Array(value)] = ArrayNode(parent, lastElement, id, key); + lastElement.attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.lengthAttribute, toData(array.length)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + + addArchivedArray(id, parent, lastElement, key); + } + + void archiveAssociativeArray (string keyType, string valueType, size_t length, string key, Id id, void delegate () dg) + { + restore(lastElement) in { + lastElement = lastElement.element(Tags.associativeArrayTag) + .attribute(Attributes.keyTypeAttribute, toData(keyType)) + .attribute(Attributes.valueTypeAttribute, toData(valueType)) + .attribute(Attributes.lengthAttribute, toData(length)) + .attribute(Attributes.keyAttribute, key) + .attribute(Attributes.idAttribute, toData(id)); + + dg(); + }; + } + + void archiveAssociativeArrayKey (string key, void delegate () dg) + { + internalArchiveAAKeyValue(key, Tags.keyTag, dg); + } + + void archiveAssociativeArrayValue (string key, void delegate () dg) + { + internalArchiveAAKeyValue(key, Tags.valueTag, dg); + } + + private void internalArchiveAAKeyValue (string key, Data tag, void delegate () dg) + { + restore(lastElement) in { + lastElement = lastElement.element(tag) + .attribute(Attributes.keyAttribute, toData(key)); + + dg(); + }; + } + + void archiveEnum (bool value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); } - private void archiveAssociativeArray (T) (T value, DataType key) + void archiveEnum (byte value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (char value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (dchar value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (int value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (long value, string type, string key, Id id) { - lastElement = lastElement.element(Tags.associativeArrayTag) - .attribute(Attributes.keyTypeAttribute, toDataType(KeyTypeOfAssociativeArray!(T).stringof)) - .attribute(Attributes.valueTypeAttribute, toDataType(ValueTypeOfAssociativeArray!(T).stringof)) - .attribute(Attributes.lengthAttribute, toDataType(value.length)) - .attribute(Attributes.keyAttribute, key); + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (short value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (ubyte value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (uint value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (ulong value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); } - private void archivePointer (T) (T value, DataType key, ref bool callDelegate) + void archiveEnum (ushort value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + void archiveEnum (wchar value, string type, string key, Id id) + { + internalArchiveEnum(value, type, key, id); + } + + private void internalArchiveEnum (T) (T value, string type, string key, Id id) + { + lastElement.element(Tags.enumTag, toData(value)) + .attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.baseTypeAttribute, toData(T.stringof)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + } + + void archiveBaseClass (string type, string key, Id id) + { + restore(lastElement) in { + lastElement = lastElement.element(Tags.baseTag) + .attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + }; + } + + void archiveNull (string type, string key) { - if (auto reference = getArchivedReference(value)) + lastElement.element(Tags.nullTag) + .attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.keyAttribute, toData(key)); + } + + void archiveObject (string runtimeType, string type, string key, Id id, void delegate () dg) + { + restore(lastElement) in { + lastElement = lastElement.element(Tags.objectTag) + .attribute(Attributes.runtimeTypeAttribute, toData(runtimeType)) + .attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + + dg(); + }; + } + + void archivePointer (string key, Id id, void delegate () dg) + { + restore(lastElement) in { + auto parent = lastElement; + lastElement = doc.createNode(Tags.pointerTag); + + lastElement.attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + + addArchivedPointer(id, parent, lastElement, key); + dg(); + }; + } + + void archivePointer (Id pointeeId, string key, Id id) + { + if (auto pointerNode = getArchivedPointer(id)) { - archiveReference(key, reference); - callDelegate = false; + pointerNode.parent.element(Tags.pointerTag) + .attribute(Attributes.keyAttribute, toData(pointerNode.key)) + .attribute(Attributes.idAttribute, toData(id)) + .element(Tags.referenceTag, toData(pointeeId)) + .attribute(Attributes.keyAttribute, toData(key)); } - - else + } + + void archiveReference (string key, Id id) + { + lastElement.element(Tags.referenceTag, toData(id)) + .attribute(Attributes.keyAttribute, toData(key)); + } + + void archiveSlice (Slice slice, Id sliceId, Id arrayId) + { + if (auto sliceNode = getArchivedArray(sliceId)) { - DataType id = nextId; - - lastElement = lastElement.element(Tags.pointerTag) - .attribute(Attributes.keyAttribute, key) - .attribute(Attributes.idAttribute, id); - - addArchivedReference(value, id); + if (auto arrayNode = getArchivedArray(arrayId)) + { + sliceNode.parent.element(Tags.sliceTag, toData(arrayNode.id)) + .attribute(Attributes.keyAttribute, toData(sliceNode.key)) + .attribute(Attributes.offsetAttribute, toData(slice.offset)) + .attribute(Attributes.lengthAttribute, toData(slice.length)); + } } } - private void archiveEnum (T) (T value, DataType key) + void archiveStruct (string type, string key, Id id, void delegate () dg) { - lastElement.element(Tags.enumTag, toDataType(value)) - .attribute(Attributes.typeAttribute, toDataType(T.stringof)) - .attribute(Attributes.keyAttribute, key); + restore(lastElement) in { + lastElement = lastElement.element(Tags.structTag) + .attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + + dg(); + }; } - - private void archivePrimitive (T) (T value, DataType key) + + void archiveTypedef (string type, string key, Id id, void delegate () dg) { - lastElement.element(toDataType(T.stringof), toDataType(value)) - .attribute(Attributes.keyAttribute, key); + restore(lastElement) in { + lastElement = lastElement.element(Tags.typedefTag) + .attribute(Attributes.typeAttribute, toData(type)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + + dg(); + }; } - private void archiveTypeDef (T) (T value, DataType key) + void archive (string value, string key, Id id) + { + archiveString(value, key, id); + } + + void archive (wstring value, string key, Id id) + { + archiveString(value, key, id); + } + + void archive (dstring value, string key, Id id) { - lastElement = lastElement.element(Tags.typedefTag) - .attribute(Attributes.typeAttribute, toDataType(BaseTypeOfTypeDef!(T).stringof)); - .attribute(Attributes.key, key); + archiveString(value, key, id); + } + + private void archiveString (T) (T value, string key, Id id) + { + restore(lastElement) in { + alias ElementTypeOfArray!(T) ElementType; + auto array = Array(value.ptr, value.length, ElementType.sizeof); + + internalArchiveArray(array, ElementType.stringof, key, id, Tags.stringTag, toData(value)); + }; } - public T unarchive (T) (DataType key, T delegate (T) dg = null) + void archive (bool value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (byte value, string key, Id id) { - if (!hasBegunUnarchiving) - beginUnarchiving(data); - - return restore!(T)(lastElement) in { - T value; - - bool callDelegate = true; - - static if (isTypeDef!(T)) - value = unarchiveTypeDef!(T)(key); - - else static if (isObject!(T)) - value = unarchiveObject!(T)(key, callDelegate); + archivePrimitive(value, key, id); + } + + //currently not suppported by to!() + /*void archive (cdouble value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ + + //currently not implemented but a reserved keyword + /*void archive (cent value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ - else static if (isStruct!(T)) - value = unarchiveStruct!(T)(key); - - else static if (isString!(T)) - value = unarchiveString!(T)(key); - - else static if (isArray!(T)) - value = unarchiveArray!(T)(key, callDelegate); + //currently not suppported by to!() + /*void archive (cfloat value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ - else static if (isAssociativeArray!(T)) - value = unarchiveAssociativeArray!(T)(key); - - else static if (isPrimitive!(T)) - value = unarchivePrimitive!(T)(key); + void archive (char value, string key, Id id) + { + archivePrimitive(value, key, id); + } - else static if (isPointer!(T)) - value = unarchivePointer!(T)(key, callDelegate); - - else static if (isEnum!(T)) - value = unarchiveEnum!(T)(key); - - else - static assert(false, format!(`The type "`, T, `" cannot be unarchived.`)); + //currently not suppported by to!() + /*void archive (creal value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ - if (callDelegate && dg) - return dg(value); - - return value; - }; + void archive (dchar value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (double value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (float value, string key, Id id) + { + archivePrimitive(value, key, id); } - private T unarchiveObject (T) (DataType key, ref bool callDelegate) - { - DataType id = unarchiveReference(key); - - if (auto reference = getUnarchivedReference!(T)(id)) - { - callDelegate = false; - return *reference; - } - - auto tmp = getElement(Tags.objectTag, key, Attributes.keyAttribute, false); + //currently not suppported by to!() + /*void archive (idouble value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ + + //currently not suppported by to!() + /*void archive (ifloat value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ + + void archive (int value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + //currently not suppported by to!() + /*void archive (ireal value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ + + void archive (long value, string key, Id id) + { + archivePrimitive(value, key, id); + } - if (!tmp.isValid) - { - lastElement = getElement(Tags.nullTag, key); - callDelegate = false; - return null; - } - - lastElement = tmp; - - auto runtimeType = getValueOfAttribute(Attributes.runtimeTypeAttribute); - - if (!runtimeType) - return T.init; - - auto name = fromDataType!(string)(runtimeType); - id = getValueOfAttribute(Attributes.idAttribute); - - if (!id) - return T.init; - - T result = cast(T) newInstance(name); - - addUnarchivedReference(result, id); - - return result; + void archive (real value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (short value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (ubyte value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + //currently not implemented but a reserved keyword + /*void archive (ucent value, string key, Id id) + { + archivePrimitive(value, key, id); + }*/ + + void archive (uint value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (ulong value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + void archive (ushort value, string key, Id id) + { + archivePrimitive(value, key, id); } - private T unarchiveStruct (T) (DataType key) + void archive (wchar value, string key, Id id) + { + archivePrimitive(value, key, id); + } + + private void archivePrimitive (T) (T value, string key, Id id) + { + lastElement.element(toData(T.stringof), toData(value)) + .attribute(Attributes.keyAttribute, toData(key)) + .attribute(Attributes.idAttribute, toData(id)); + } + + Id unarchiveArray (string key, void delegate (size_t) dg) + { + return restore!(Id)(lastElement) in { + auto element = getElement(Tags.arrayTag, key); + + if (!element.isValid) + return Id.max; + + lastElement = element; + auto len = getValueOfAttribute(Attributes.lengthAttribute); + + if (!len) + return Id.max; + + auto length = fromData!(size_t)(len); + auto id = getValueOfAttribute(Attributes.idAttribute); + + if (!id) + return Id.max; + + dg(length); + + return toId(id); + }; + } + + void unarchiveArray (Id id, void delegate (size_t) dg) { - auto element = getElement(Tags.structTag, key); - - if (element.isValid) + restore(lastElement) in { + auto element = getElement(Tags.arrayTag, to!(string)(id), Attributes.idAttribute); + + if (!element.isValid) + return; + lastElement = element; - - return T.init; + auto len = getValueOfAttribute(Attributes.lengthAttribute); + + if (!len) + return; + + auto length = fromData!(size_t)(len); + auto stringId = getValueOfAttribute(Attributes.idAttribute); + + if (!stringId) + return; + + dg(length); + }; + } + + Id unarchiveAssociativeArray (string key, void delegate (size_t length) dg) + { + return restore!(Id)(lastElement) in { + auto element = getElement(Tags.associativeArrayTag, key); + + if (!element.isValid) + return Id.max; + + lastElement = element; + auto len = getValueOfAttribute(Attributes.lengthAttribute); + + if (!len) + return Id.max; + + auto length = fromData!(size_t)(len); + auto id = getValueOfAttribute(Attributes.idAttribute); + + if (!id) + return Id.max; + + dg(length); + + return toId(id); + }; } - private T unarchiveString (T) (DataType key) + void unarchiveAssociativeArrayKey (string key, void delegate () dg) { - auto slice = unarchiveSlice(key); - - if (auto tmp = getUnarchivedSlice!(T)(slice)) - return *tmp; - - auto element = getElement(Tags.stringTag, key); - - if (!element.isValid) - return T.init; - - auto value = fromDataType!(T)(element.value); - slice.id = getValueOfAttribute(Attributes.idAttribute, element); - - if (!slice.id) - return T.init; - - addUnarchivedSlice(value, slice.id); - - return value; + internalUnarchiveAAKeyValue(key, Tags.keyTag, dg); + } + + void unarchiveAssociativeArrayValue (string key, void delegate () dg) + { + internalUnarchiveAAKeyValue(key, Tags.valueTag, dg); } - - private T unarchiveArray (T) (DataType key, ref bool callDelegate) - { - auto slice = unarchiveSlice(key); - - if (auto tmp = getUnarchivedSlice!(T)(slice)) - { - callDelegate = false; - return *tmp; - } - - T value; - - auto element = getElement(Tags.arrayTag, key); - - if (!element.isValid) - return T.init; - - lastElement = element; - auto length = getValueOfAttribute(Attributes.lengthAttribute); - - if (!length) - return T.init; - - value.length = fromDataType!(size_t)(length); - slice.id = getValueOfAttribute(Attributes.idAttribute); - - if (!slice.id) - return T.init; - - addUnarchivedSlice(value, slice.id); - - return value; + + private void internalUnarchiveAAKeyValue (string key, Data tag, void delegate () dg) + { + restore(lastElement) in { + auto element = getElement(tag, key); + + if (!element.isValid) + return; + + lastElement = element; + + dg(); + }; } - - private T unarchiveAssociativeArray (T) (DataType key) - { - auto element = getElement(Tags.associativeArrayTag, key); - - if (element.isValid) - lastElement = element; - - return T.init; + + bool unarchiveEnumBool (string key) + { + return unarchiveEnum!(bool)(key); } - - private T unarchivePointer (T) (DataType key, ref bool callDelegate) + + byte unarchiveEnumByte (string key) { - DataType id = unarchiveReference(key); - - if (auto reference = getUnarchivedReference!(T)(id)) - { - callDelegate = false; - return *reference; - } - - auto element = getElement(Tags.pointerTag, key); - - if (!element.isValid) - return T.init; - - lastElement = element; - id = getValueOfAttribute(Attributes.idAttribute); - - if (!id) - return T.init; - - T result = new BaseTypeOfPointer!(T); - - addUnarchivedReference(result, id); - - return result; + return unarchiveEnum!(byte)(key); + } + + char unarchiveEnumChar (string key) + { + return unarchiveEnum!(char)(key); + } + + dchar unarchiveEnumDchar (string key) + { + return unarchiveEnum!(dchar)(key); } - private T unarchiveEnum (T) (DataType key) + int unarchiveEnumInt (string key) + { + return unarchiveEnum!(int)(key); + } + + long unarchiveEnumLong (string key) + { + return unarchiveEnum!(long)(key); + } + + short unarchiveEnumShort (string key) + { + return unarchiveEnum!(short)(key); + } + + ubyte unarchiveEnumUbyte (string key) + { + return unarchiveEnum!(ubyte)(key); + } + + uint unarchiveEnumUint (string key) + { + return unarchiveEnum!(uint)(key); + } + + ulong unarchiveEnumUlong (string key) + { + return unarchiveEnum!(ulong)(key); + } + + ushort unarchiveEnumUshort (string key) + { + return unarchiveEnum!(ushort)(key); + } + + wchar unarchiveEnumWchar (string key) + { + version (Tango) + return unarchiveEnum!(wchar)(key); + + else + return wchar.init; + } + + private T unarchiveEnum (T) (string key) { auto element = getElement(Tags.enumTag, key); if (!element.isValid) return T.init; - return fromDataType!(T)(element.value); + return fromData!(T)(element.value); } + + void unarchiveObject (string key, out Id id, out Object result, void delegate () dg) + { + restore(lastElement) in { + auto tmp = getElement(Tags.objectTag, key, Attributes.keyAttribute, false); + + if (!tmp.isValid) + { + lastElement = getElement(Tags.nullTag, key); + return; + } + + lastElement = tmp; + + auto runtimeType = getValueOfAttribute(Attributes.runtimeTypeAttribute); + + if (!runtimeType) + return; + + auto name = fromData!(string)(runtimeType); + auto stringId = getValueOfAttribute(Attributes.idAttribute); + + if (!stringId) + return; + + id = toId(stringId); + result = newInstance(name); + dg(); + }; + } + + Id unarchivePointer (string key, void delegate () dg) + { + return restore!(Id)(lastElement) in { + auto tmp = getElement(Tags.pointerTag, key, Attributes.keyAttribute, false); + + if (!tmp.isValid) + { + lastElement = getElement(Tags.nullTag, key); + return Id.max; + } + + lastElement = tmp; + auto id = getValueOfAttribute(Attributes.idAttribute); - private T unarchivePrimitive (T) (DataType key) - { - auto element = getElement(toDataType(T.stringof), key); + if (!id) + return Id.max; + + dg(); + + return toId(id); + }; + } + + Id unarchiveReference (string key) + { + auto element = getElement(Tags.referenceTag, key, Attributes.keyAttribute, false); + + if (element.isValid) + return toId(element.value); - if (!element.isValid) - return T.init; + return Id.max; + } + + Slice unarchiveSlice (string key) + { + auto element = getElement(Tags.sliceTag, key, Attributes.keyAttribute, false); + + if (element.isValid) + { + auto length = fromData!(size_t)(getValueOfAttribute(Attributes.lengthAttribute, element)); + auto offset = fromData!(size_t)(getValueOfAttribute(Attributes.offsetAttribute, element)); + auto id = toId(element.value); + + return Slice(length, offset, id); + } - return fromDataType!(T)(element.value); + return Slice.init; + } + + void unarchiveStruct (string key, void delegate () dg) + { + restore(lastElement) in { + auto element = getElement(Tags.structTag, key); + + if (!element.isValid) + return; + + lastElement = element; + dg(); + }; } private T unarchiveTypeDef (T) (DataType key) @@ -568,53 +809,258 @@ return T.init; } - public AssociativeArrayVisitor!(KeyTypeOfAssociativeArray!(T), ValueTypeOfAssociativeArray!(T)) unarchiveAssociativeArrayVisitor (T) () + void unarchiveTypedef (string key, void delegate () dg) + { + restore(lastElement) in { + auto element = getElement(Tags.typedefTag, key); + + if (!element.isValid) + return; + + lastElement = element; + dg(); + }; + } + + string unarchiveString (string key, out Id id) + { + return internalUnarchiveString!(string)(key, id); + } + + wstring unarchiveWstring (string key, out Id id) + { + return internalUnarchiveString!(wstring)(key, id); + } + + dstring unarchiveDstring (string key, out Id id) { - return AssociativeArrayVisitor!(KeyTypeOfAssociativeArray!(T), ValueTypeOfAssociativeArray!(T))(this); + return internalUnarchiveString!(dstring)(key, id); + } + + private T internalUnarchiveString (T) (string key, out Id id) + { + auto element = getElement(Tags.stringTag, key); + + if (!element.isValid) + return T.init; + + auto value = fromData!(T)(element.value); + auto stringId = getValueOfAttribute(Attributes.idAttribute, element); + + if (!stringId) + return T.init; + + id = toId(stringId); + return value; + } + + string unarchiveString (Id id) + { + return internalUnarchiveString!(string)(id); + } + + wstring unarchiveWstring (Id id) + { + return internalUnarchiveString!(wstring)(id); } - public void archiveBaseClass (T : Object) (DataType key) + dstring unarchiveDstring (Id id) + { + return internalUnarchiveString!(dstring)(id); + } + + private T internalUnarchiveString (T) (Id id) { - lastElement = lastElement.element(Tags.baseTag) - .attribute(Attributes.typeAttribute, toDataType(T.stringof)) - .attribute(Attributes.keyAttribute, key); + auto element = getElement(Tags.stringTag, to!(string)(id), Attributes.idAttribute); + + if (!element.isValid) + return T.init; + + return fromData!(T)(element.value); + } + + bool unarchiveBool (string key) + { + return unarchivePrimitive!(bool)(key); + } + + byte unarchiveByte (string key) + { + return unarchivePrimitive!(byte)(key); } - public void unarchiveBaseClass (T : Object) (DataType key) + //currently not suppported by to!() + /*cdouble unarchiveCdouble (string key) + { + return unarchivePrimitive!(cdouble)(key); + }*/ + + //currently not implemented but a reserved keyword + /*cent unarchiveCent (string key) + { + return unarchivePrimitive!(cent)(key); + }*/ + + // currently not suppported by to!() + /*cfloat unarchiveCfloat (string key) { - auto element = getElement(Tags.baseTag, key); - - if (element.isValid) - lastElement = element; + return unarchivePrimitive!(cfloat)(key); + }*/ + + char unarchiveChar (string key) + { + return unarchivePrimitive!(char)(key); + } + + //currently not implemented but a reserved keyword + /*creal unarchiveCreal (string key) + { + return unarchivePrimitive!(creal)(key); + }*/ + + dchar unarchiveDchar (string key) + { + return unarchivePrimitive!(dchar)(key); + } + + double unarchiveDouble (string key) + { + return unarchivePrimitive!(double)(key); } - version (Tango) + float unarchiveFloat (string key) + { + return unarchivePrimitive!(float)(key); + } + + //currently not suppported by to!() + /*idouble unarchiveIdouble (string key) + { + return unarchivePrimitive!(idouble)(key); + }*/ + + // currently not suppported by to!()*/ + /*ifloat unarchiveIfloat (string key) + { + return unarchivePrimitive!(ifloat)(key); + }*/ + + int unarchiveInt (string key) + { + return unarchivePrimitive!(int)(key); + } + + // currently not suppported by to!() + /*ireal unarchiveIreal (string key) + { + return unarchivePrimitive!(ireal)(key); + }*/ + + long unarchiveLong (string key) { - template errorMessage (ArchiveMode mode = ArchiveMode.archiving) - { - static if (mode == ArchiveMode.archiving) - const errorMessage = "Could not continue archiving due to unrecognized data format: "; - - else static if (mode == ArchiveMode.unarchiving) - const errorMessage = "Could not continue unarchiving due to unrecognized data format: "; - } + return unarchivePrimitive!(long)(key); + } + + real unarchiveReal (string key) + { + return unarchivePrimitive!(real)(key); + } + + short unarchiveShort (string key) + { + return unarchivePrimitive!(short)(key); + } + + ubyte unarchiveUbyte (string key) + { + return unarchivePrimitive!(ubyte)(key); + } + + // currently not implemented but a reserved keyword + /*ucent unarchiveCcent (string key) + { + return unarchivePrimitive!(ucent)(key); + }*/ + + uint unarchiveUint (string key) + { + return unarchivePrimitive!(uint)(key); + } + + ulong unarchiveUlong (string key) + { + return unarchivePrimitive!(ulong)(key); } - else + ushort unarchiveUshort (string key) + { + return unarchivePrimitive!(ushort)(key); + } + + wchar unarchiveWchar (string key) { - mixin( - `template errorMessage (ArchiveMode mode = ArchiveMode.archiving) - { - static if (mode == ArchiveMode.archiving) - enum errorMessage = "Could not continue archiving due to unrecognized data format: "; - - else static if (mode == ArchiveMode.unarchiving) - enum errorMessage = "Could not continue unarchiving due to unrecognized data format: "; - }` - ); + version (Tango) + return unarchivePrimitive!(wchar)(key); + + else + return wchar.init; + } + + T unarchivePrimitive (T) (string key) + { + auto element = getElement(toData(T.stringof), key); + + if (!element.isValid) + return T.init; + + return fromData!(T)(element.value); + } + + void postProcessArray (Id id) + { + if (auto array = getArchivedArray(id)) + array.parent.attach(array.node); } - private doc.Node getElement (DataType tag, DataType key, DataType attribute = Attributes.keyAttribute, bool throwOnError = true) + void postProcessPointer (Id id) + { + if (auto pointer = getArchivedPointer(id)) + pointer.parent.attach(pointer.node); + } + + private void addArchivedArray (Id id, doc.Node parent, doc.Node element, string key) + { + archivedArrays[id] = Node(parent, element, id, key); + } + + private Node* getArchivedArray (Id id) + { + if (auto array = id in archivedArrays) + return array; + + if (errorCallback) + errorCallback(new ArchiveException(`Could not continue archiving due to no array with the Id "` ~ to!(string)(id) ~ `" was found.`, __FILE__, __LINE__), [to!(string)(id)]); + + return null; + } + + private void addArchivedPointer (Id id, doc.Node parent, doc.Node element, string key) + { + archivedPointers[id] = Node(parent, element, id, key); + } + + private Node* getArchivedPointer (Id id) + { + if (auto pointer = id in archivedPointers) + return pointer; + + if (errorCallback) + errorCallback(new ArchiveException(`Could not continue archiving due to no pointer with the Id "` ~ to!(string)(id) ~ `" was found.`, __FILE__, __LINE__), [to!(string)(id)]); + + return null; + } + + private doc.Node getElement (Data tag, string key, Data attribute = Attributes.keyAttribute, bool throwOnError = true) { auto set = lastElement.query[tag].attribute((doc.Node node) { if (node.name == attribute && node.value == key) @@ -622,7 +1068,7 @@ return false; }); - + if (set.nodes.length == 1) return set.nodes[0].parent; @@ -641,9 +1087,10 @@ } } - private DataType getValueOfAttribute (DataType attribute, doc.Node element = doc.Node.invalid) + private Data getValueOfAttribute (Data attribute, doc.Node element = doc.Node.invalid) { - if (!element.isValid) element = lastElement; + if (!element.isValid) + element = lastElement; auto set = element.query.attribute(attribute); @@ -665,150 +1112,29 @@ return null; } - private void addArchivedReference (T) (T value, DataType id) - { - static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`)); - - archivedReferences[cast(void*) value] = id; - } - - private void addUnarchivedReference (T) (T value, DataType id) - { - static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`)); - - unarchivedReferences[id] = cast(void*) value; - } - - private void addUnarchivedSlice (T) (T value, DataType id) - { - static assert(isArray!(T) || isString!(T), format!(`The given type "`, T, `" is not a slice type, i.e. array or string.`)); - - unarchivedSlices[id] = value; - } - - private DataType getArchivedReference (T) (T value) - { - if (auto tmp = cast(void*) value in archivedReferences) - return *tmp; - - return null; - } - - private T* getUnarchivedReference (T) (DataType id) - { - if (auto reference = id in unarchivedReferences) - return cast(T*) reference; - - return null; - } - - private T* getUnarchivedSlice (T) (Slice slice) - { - if (auto array = slice.id in unarchivedSlices) - return &(cast(T) *array)[slice.offset .. slice.length + 1]; // dereference the array, cast it to the right type, - // slice it and then return a pointer to the result - return null; - } - - private DataType nextId () + version (Tango) { - return toDataType(idCounter++); - } - - private void archiveReference (DataType key, DataType id) - { - lastElement.element(Tags.referenceTag, id) - .attribute(Attributes.keyAttribute, key); - } - - private DataType unarchiveReference (DataType key) - { - auto element = getElement(Tags.referenceTag, key, Attributes.keyAttribute, false); - - if (element.isValid) - return element.value; - - return cast(DataType) null; - } - - private Slice unarchiveSlice (DataType key) - { - auto element = getElement(Tags.sliceTag, key, Attributes.keyAttribute, false); - - if (element.isValid) + private template errorMessage (ArchiveMode mode = ArchiveMode.archiving) { - auto length = fromDataType!(size_t)(getValueOfAttribute(Attributes.lengthAttribute, element)); - auto offset = fromDataType!(size_t)(getValueOfAttribute(Attributes.offsetAttribute, element)); - - return Slice(length, offset, element.value); - } - - return Slice.init; - } - - private struct AssociativeArrayVisitor (Key, Value) - { - private XMLArchive archive; - - static AssociativeArrayVisitor opCall (XMLArchive archive) - { - AssociativeArrayVisitor aai; - aai.archive = archive; - - return aai; - } - - int opApply(int delegate(ref Key, ref Value) dg) - { - int result; - - foreach (node ; archive.lastElement.children) - { - restore(archive.lastElement) in { - archive.lastElement = node; - - if (node.attributes.exist) - { - Key key = to!(Key)(archive.getValueOfAttribute(Attributes.keyAttribute)); - Value value = to!(Value)(node.value); - - result = dg(key, value); - } - }; + static if (mode == ArchiveMode.archiving) + const errorMessage = "Could not continue archiving due to unrecognized data format: "; - if (result) - break; - } - - return result; + else static if (mode == ArchiveMode.unarchiving) + const errorMessage = "Could not continue unarchiving due to unrecognized data format: "; } } - public void postProcess () + else { - bool foundSlice = true; - - foreach (slice, sliceNode ; arraysToBeArchived) - { - foreach (array, arrayNode ; arraysToBeArchived) + mixin( + `private template errorMessage (ArchiveMode mode = ArchiveMode.archiving) { - if (slice.isSliceOf(array) && slice != array) - { - sliceNode.parent.element(Tags.sliceTag, arrayNode.id) - .attribute(Attributes.keyAttribute, sliceNode.key) - .attribute(Attributes.offsetAttribute, toDataType((slice.ptr - array.ptr) / slice.elementSize)) - .attribute(Attributes.lengthAttribute, toDataType(slice.length)); + static if (mode == ArchiveMode.archiving) + enum errorMessage = "Could not continue archiving due to unrecognized data format: "; - foundSlice = true; - break; - } - - else - foundSlice = false; - } - - if (!foundSlice) - sliceNode.parent.attach(sliceNode.node); - } - } + else static if (mode == ArchiveMode.unarchiving) + enum errorMessage = "Could not continue unarchiving due to unrecognized data format: "; + }` + ); + } } \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/test/UnitTester.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/test/UnitTester.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,567 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: Oct 17, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.test.UnitTester; + +version (Tango) +{ + import tango.core.Exception; + import tango.io.device.File; + import tango.io.FilePath; + import tango.io.stream.Lines; + import tango.sys.Environment; + import tango.util.Convert; +} + + +else +{ + import core.exception; + import std.conv; + + private alias AssertError AssertException; +} + + +import orange.core._; +import orange.util._; + +Use!(void delegate (), string) describe (string message) +{ + return UnitTester.instance.describe(message); +} + +Use!(void delegate (), string) it (string message) +{ + return UnitTester.instance.test(message); +} + +void delegate () before () +{ + return UnitTester.instance.before; +} + +void delegate () before (void delegate () before) +{ + return UnitTester.instance.before = before; +} + +void delegate () after () +{ + return UnitTester.instance.after; +} + +void delegate () after (void delegate () after) +{ + return UnitTester.instance.after = after; +} + +void run () +{ + UnitTester.instance.run; +} + +private: + +class UnitTester +{ + private: + + struct DescriptionManager + { + Description[] descriptions; + size_t lastIndex = size_t.max; + + void opCatAssign (Description description) + { + descriptions ~= description; + lastIndex++; + } + + void opCatAssign (Test test) + { + last.tests ~= test; + } + + Description opIndex (size_t i) + { + return descriptions[i]; + } + + Description last () + { + return descriptions[$ - 1]; + } + + Description first () + { + return descriptions[0]; + } + + int opApply (int delegate(ref Description) dg) + { + int result = 0; + + foreach (desc ; descriptions) + { + result = dg(desc); + + if (result) + return result; + } + + return result; + } + + size_t length () + { + return descriptions.length; + } + } + + class Description + { + private + { + DescriptionManager descriptions; + Test[] tests; + Test[] failures; + Test[] pending; + size_t lastIndex = size_t.max; + string message; + void delegate () description; + } + + this (string message) + { + this.message = message; + } + + void run () + { + if (shouldRun) + description(); + } + + bool shouldRun () + { + return description !is null; + } + } + + struct Test + { + void delegate () test; + string message; + AssertException exception; + + bool failed () + { + return !succeeded; + } + + bool succeeded () + { + if (exception is null) + return true; + + return false; + } + + void run () + { + if (!isPending) + test(); + } + + bool isPending () + { + return test is null; + } + } + + static UnitTester instance_; + + DescriptionManager descriptions; + Description currentDescription; + + void delegate () before_; + void delegate () after_; + + size_t numberOfFailures; + size_t numberOfPending; + size_t numberOfTests; + size_t failureId; + + string defaultIndentation = " "; + string indentation; + + static UnitTester instance () + { + if (instance_) + return instance_; + + return instance_ = new UnitTester; + } + + Use!(void delegate (), string) describe (string message) + { + addDescription(message); + Use!(void delegate (), string) use; + + use.args[0] = &internalDescribe; + use.args[1] = message; + + return use; + } + + Use!(void delegate (), string) test (string message) + { + addTest(message); + Use!(void delegate (), string) use; + + use.args[0] = &internalTest; + use.args[1] = message; + + return use; + } + + void run () + { + foreach (description ; descriptions) + runDescription(description); + + printResult; + } + + void runDescription (Description description) + { + restore(currentDescription) in { + currentDescription = description; + description.run; + + foreach (desc ; description.descriptions) + runDescription(desc); + + foreach (test ; description.tests) + { + if (test.isPending) + addPendingTest(description, test); + + try + { + execute in { + test.run(); + }; + } + + catch (AssertException e) + handleFailure(description, test, e); + } + }; + } + + void delegate () before () + { + return before_; + } + + void delegate () before (void delegate () before) + { + return before_ = before; + } + + void delegate () after () + { + return after_; + } + + void delegate () after (void delegate () after) + { + return after_ = after; + } + + void addTest (string message) + { + numberOfTests++; + currentDescription.tests ~= Test(null, message); + } + + void addDescription (string message) + { + if (currentDescription) + currentDescription.descriptions ~= new Description(message); + + else + descriptions ~= new Description(message); + } + + void addPendingTest (Description description, ref Test test) + { + numberOfPending++; + description.pending ~= test; + } + + void handleFailure (Description description, ref Test test, AssertException exception) + { + numberOfFailures++; + test.exception = exception; + description.failures ~= test; + } + + void internalDescribe (void delegate () dg, string message) + { + if (currentDescription) + currentDescription.descriptions.last.description = dg; + + else + descriptions.last.description = dg; + } + + void internalTest (void delegate () dg, string message) + { + currentDescription.tests[$ - 1] = Test(dg, message); + } + + void printResult () + { + if (isAllTestsSuccessful) + return printSuccess(); + + foreach (description ; descriptions) + { + printDescription(description); + printResultImpl(description.descriptions); + } + + failureId = 0; + + printPending; + printFailures; + + print("\n", numberOfTests, " ", pluralize("test", numberOfTests),", ", numberOfFailures, " ", pluralize("failure", numberOfFailures)); + printNumberOfPending; + println(); + } + + void printResultImpl (DescriptionManager descriptions) + { + restore(indentation) in { + indentation ~= defaultIndentation; + + foreach (description ; descriptions) + { + printDescription(description); + printResultImpl(description.descriptions); + } + }; + } + + void printDescription (Description description) + { + println(indentation, description.message); + + restore(indentation) in { + indentation ~= defaultIndentation; + + foreach (i, ref test ; description.tests) + { + print(indentation, "- ", test.message); + + if (test.isPending) + print(" (PENDING: Not Yet Implemented)"); + + if (test.failed) + print(" (FAILED - ", ++failureId, ')'); + + println(); + } + }; + } + + void printPending () + { + if (!hasPending) + return; + + println("\nPending:"); + + restore(indentation) in { + indentation ~= defaultIndentation; + + foreach (description ; descriptions) + { + printPendingDescription(description); + printPendingImpl(description.descriptions); + } + }; + } + + void printPendingImpl (DescriptionManager descriptions) + { + foreach (description ; descriptions) + { + printPendingDescription(description); + printPendingImpl(description.descriptions); + } + } + + void printPendingDescription (Description description) + { + foreach (test ; description.pending) + println(indentation, description.message, " ", test.message, "\n", indentation, indentation, "# Not Yet Implemented"); + } + + void printFailures () + { + if (!hasFailures) + return; + + println("\nFailures:"); + + restore(indentation) in { + indentation ~= defaultIndentation; + + foreach (description ; descriptions) + { + printFailuresDescription(description); + printFailuresImpl(description.descriptions); + } + }; + } + + void printFailuresImpl (DescriptionManager descriptions) + { + foreach (description ; descriptions) + { + printFailuresDescription(description); + printFailuresImpl(description.descriptions); + } + } + + void printFailuresDescription (Description description) + { + foreach (test ; description.failures) + { + auto str = indentation ~ to!(string)(++failureId) ~ ") "; + auto whitespace = toWhitespace(str.length); + + println(str, description.message, " ", test.message); + println(whitespace, "# ", test.exception.file, ".d:", test.exception.line); + println(whitespace, "Stack trace:"); + print(whitespace); + + version (Tango) + { + test.exception.writeOut(&printStackTrace); + println(); + println(readFailedTest(test)); + } + } + } + + void printStackTrace (string str) + { + return print(str); + + /*if (str.find("start") < size_t.max || + str.find("main") < size_t.max || + str.find("rt.compiler.") < size_t.max || + str.find("orange.") || + str.find(":0") || + str.find("_d_assert") || + str.find("onAssertError") || + str.find("tango.core.Exception.AssertException._ctor ") || + str.find("object.") || + str.find("tango.core.tools.")) + return;*/ + } + + version (Tango) + { + string readFailedTest (ref Test test, int numberOfSurroundingLines = 3) + { + auto filename = test.exception.file.dup.replace('.', '/'); + + filename ~= ".d"; + filename = Environment.toAbsolute(filename); + auto lineNumber = test.exception.line; + string str; + auto file = new File(filename); + + foreach (i, line ; new Lines!(char)(file)) + if (i >= (lineNumber - 1) - numberOfSurroundingLines && i <= (lineNumber - 1) + numberOfSurroundingLines) + str ~= line ~ '\n'; + + file.close; + + return str; + } + } + + void printNumberOfPending () + { + if (hasPending) + print(", ", numberOfPending, " pending"); + } + + void printSuccess () + { + println("All ", numberOfTests, pluralize(" test", numberOfTests), " passed successfully."); + } + + bool isAllTestsSuccessful () + { + return !hasPending && !hasFailures; + } + + bool hasPending () + { + return numberOfPending > 0; + } + + bool hasFailures () + { + return numberOfFailures > 0; + } + + Use!(void delegate ()) execute () + { + Use!(void delegate ()) use; + + use.args[0] = &executeImpl; + + return use; + } + + void executeImpl (void delegate () dg) + { + auto before = this.before; + auto after = this.after; + + if (before) before(); + if (dg) dg(); + if (after) after(); + } + + string toWhitespace (size_t value) + { + string str; + + for (size_t i = 0; i < value; i++) + str ~= ' '; + + return str; + } + + string pluralize (string str, int value) + { + if (value == 1) + return str; + + return str ~ "s"; + } +} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/CTFE.d --- a/orange/util/CTFE.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/util/CTFE.d Wed Aug 03 21:58:21 2011 +0200 @@ -6,7 +6,7 @@ */ module orange.util.CTFE; -import orange.util.string; +import orange.core.string; import orange.util.Traits; template format (ARGS...) @@ -101,7 +101,7 @@ */ bool contains (T) (T[] arr, T element) { - return arr.indexOf(element) != size_t.max; + return indexOf(arr, element) != size_t.max; } /** diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/Reflection.d --- a/orange/util/Reflection.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/util/Reflection.d Wed Aug 03 21:58:21 2011 +0200 @@ -6,8 +6,8 @@ */ module orange.util.Reflection; +import orange.core.string; import orange.util.CTFE; -import orange.util.string; /** * Returns the name of the given function diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/Traits.d --- a/orange/util/Traits.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/util/Traits.d Wed Aug 03 21:58:21 2011 +0200 @@ -28,6 +28,8 @@ version = Phobos; } +import orange.core.string; + template isPrimitive (T) { const bool isPrimitive = is(T == bool) || @@ -91,7 +93,7 @@ template isString (T) { - const bool isString = is(T : char[]) || is(T : wchar[]) || is(T : dchar[]); + const bool isString = is(T : string) || is(T : wstring) || is(T : dstring); } template isAssociativeArray (T) @@ -123,9 +125,9 @@ const bool isReference = isObject!(T) || isPointer!(T); } -template isTypeDef (T) +template isTypedef (T) { - const bool isTypeDef = is(T == typedef); + const bool isTypedef = is(T == typedef); } template isVoid (T) @@ -133,13 +135,9 @@ const bool isVoid = is(T == void); } -template BaseTypeOfArray (T) +template ElementTypeOfArray(T : T[]) { - static if (is(T U : U[])) - alias BaseTypeOfArray!(U) BaseTypeOfArray; - - else - alias T BaseTypeOfArray; + alias T ElementTypeOfArray; } template BaseTypeOfPointer (T) @@ -151,13 +149,22 @@ alias T BaseTypeOfPointer; } -template BaseTypeOfTypeDef (T) +template BaseTypeOfTypedef (T) { static if (is(T U == typedef)) - alias BaseTypeOfTypeDef!(U) BaseTypeOfTypeDef; + alias BaseTypeOfTypedef!(U) BaseTypeOfTypedef; else - alias T BaseTypeOfTypeDef; + alias T BaseTypeOfTypedef; +} + +template BaseTypeOfEnum (T) +{ + static if (is(T U == enum)) + alias BaseTypeOfEnum!(U) BaseTypeOfEnum; + + else + alias T BaseTypeOfEnum; } template KeyTypeOfAssociativeArray (T) diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/Use.d --- a/orange/util/Use.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/util/Use.d Wed Aug 03 21:58:21 2011 +0200 @@ -15,13 +15,14 @@ else { import std.typetuple; + import std.typecons; import std.traits; alias ReturnType ReturnTypeOf; alias ParameterTypeTuple ParameterTupleOf; } -struct OpInStruct (ARGS...) +struct Use (ARGS...) { static assert (ARGS.length > 0); @@ -46,7 +47,13 @@ return args[0](dg); else - return args[0](dg, args[1 .. $]); + { + version (Tango) + return args[0](dg, args[1 .. $]); + + else + return args[0](dg, args.expand[1 .. $]); + } } } diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/_.d --- a/orange/util/_.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/util/_.d Wed Aug 03 21:58:21 2011 +0200 @@ -9,8 +9,6 @@ public: import orange.util.CTFE; -import orange.util.io; import orange.util.Reflection; -import orange.util.string; import orange.util.Traits; import orange.util.Use; \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/collection/Array.d --- a/orange/util/collection/Array.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/util/collection/Array.d Wed Aug 03 21:58:21 2011 +0200 @@ -19,7 +19,11 @@ version = Phobos; import std.c.string : memmove; -} + import algorithm = std.algorithm; +} + +import orange.core.string; +import orange.util.Traits; /** * Inserts the specified element at the specified position in the array. Shifts the @@ -276,6 +280,36 @@ } /** + * Returns $(D_KEYWORD true) if the array contains the given pattern. + * + * Params: + * arr = the array to check if it contains the element + * pattern = the pattern whose presence in the array is to be tested + * + * Returns: $(D_KEYWORD true) if the array contains the given pattern + */ +bool contains (T) (T[] arr, T[] pattern) +{ + static if (isChar!(T)) + { + version (Tango) + return tango.text.Util.containsPattern(arr, pattern); + + else + return stdString.indexOf(arr, element) != -1; + } + + else + { + version (Tango) + return tango.core.Array.contains(arr, pattern); + + else + return !algorithm.find(arr, pattern).empty; + } +} + +/** * Returns $(D_KEYWORD true) if this array contains no elements. * * Params: @@ -433,7 +467,7 @@ * * Returns: the array */ -T[] replace (T, U = size_t) (ref T[] arr, U pos, U n, T[] elements) +/*T[] replace (T, U = size_t) (ref T[] arr, U pos, U n, T[] elements) in { assert(pos <= arr.length, "mambo.collection.Array.replace: The position was greter than the length of the array"); @@ -491,9 +525,54 @@ } return arr; +}*/ + +/** + * Replaces all the occurences of $(D_PARAM pattern) with $(D_PARAM replacement) + * + * Params: + * arr = the array to do the raplce in + * pattern = the pattern to match + * replacement = the values to subsitute + * + * Returns: the passed in array with all the patterns subsituted + */ +T[] replace (T : wchar) (T[] arr, dchar pattern, dchar replacement) +{ + foreach (i, dchar e ; arr) + if (e == pattern) + arr[i] = replacement; + + return arr; } /** + * Replaces all the occurences of $(D_PARAM pattern) with $(D_PARAM replacement) + * + * Params: + * arr = the array to do the raplce in + * pattern = the pattern to match + * replacement = the values to subsitute + * + * Returns: the passed in array with all the patterns subsituted + */ +/*T[] replace (T) (T[] arr, T pattern, T replacement) +{ + version (Tango) + tango.core.Array.replace(arr, pattern, replacement); + + + else + { + foreach (ref e ; arr) + if (e == pattern) + e = replacement; + } + + return arr; +}*/ + +/** * Erases a part of the array content, shortening the length of the array. * * Params: diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/collection/_.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/collection/_.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,11 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: Nov 21, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.util.collection._; + +public: + +import orange.util.collection.Array; \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/io.d --- a/orange/util/io.d Mon Oct 04 20:45:32 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/** - * Copyright: Copyright (c) 2007-2008 Jacob Carlborg. All rights reserved. - * Authors: Jacob Carlborg - * Version: Initial created: 2007 - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) - * - */ -module orange.util.io; - -version (Tango) -{ - import tango.io.Stdout; - import tango.io.Console; - - import orange.util.string; -} - -else - import std.stdio; - -/** - * Print to the standard output - * - * Params: - * args = what to print - */ -void print (A...)(A args) -{ - version (Tango) - { - const string fmt = "{}{}{}{}{}{}{}{}" - "{}{}{}{}{}{}{}{}" - "{}{}{}{}{}{}{}{}"; - - static assert (A.length <= fmt.length / 2, "mambo.io.print :: too many arguments"); - - Stdout.format(fmt[0 .. args.length * 2], args).flush; - } - - - else - write(args); -} - -/** - * Print to the standard output, adds a new line - * - * Params: - * args = what to print - */ -void println (A...)(A args) -{ - version (Tango) - { - const string fmt = "{}{}{}{}{}{}{}{}" - "{}{}{}{}{}{}{}{}" - "{}{}{}{}{}{}{}{}"; - - static assert (A.length <= fmt.length / 2, "mambo.io.println :: too many arguments"); - - Stdout.formatln(fmt[0 .. args.length * 2], args); - } - - else - writeln(args); -} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/util/string.d --- a/orange/util/string.d Mon Oct 04 20:45:32 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,888 +0,0 @@ -/** - * Copyright: Copyright (c) 2008-2009 Jacob Carlborg. All rights reserved. - * Authors: Jacob Carlborg - * Version: Initial created: 2008 - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) - * - */ -module orange.util.string; - -public import orange.util.collection.Array; - -version (Tango) -{ - static import tango.stdc.stringz; - import tango.text.Unicode : toFold, isDigit; - import tango.text.convert.Utf; - import tango.text.Util; - - alias tango.stdc.stringz.toStringz toStringz; - alias tango.stdc.stringz.toString16z toString16z; - alias tango.stdc.stringz.toString32z toString32z; - - alias tango.stdc.stringz.fromStringz fromStringz; - alias tango.stdc.stringz.fromString16z fromString16z; - alias tango.stdc.stringz.fromString32z fromString32z; - - alias tango.text.convert.Utf.toString16 toString16; - alias tango.text.convert.Utf.toString32 toString32; -} - -else -{ - import std.string; - import std.utf; - import std.ctype : isxdigit; - - version = Phobos; - - private alias std.string.tolower toFold; - - alias std.utf.toUTF8 toString; - alias std.utf.toUTF16 toString16; - alias std.utf.toUTF32 toString32; - - alias std.string.toStringz toStringz; - alias std.utf.toUTF16z toString16z; - - alias std.string.toString fromStringz; -} - -version (Tango) -{ - /** - * string alias - */ - alias char[] string; - - /** - * wstring alias - */ - alias wchar[] wstring; - - /** - * dstring alias - */ - alias dchar[] dstring; -} - -/** - * Compares the $(D_PSYMBOL string) to another $(D_PSYMBOL string), ignoring case - * considerations. Two strings are considered equal ignoring case if they are of the - * same length and corresponding characters in the two strings are equal ignoring case. - * - * Params: - * str = The $(D_PSYMBOL string) first string to compare to - * anotherString = The $(D_PSYMBOL string) to compare the first $(D_PSYMBOL string) with - * - * Returns: $(D_KEYWORD true) if the arguments is not $(D_KEYWORD null) and it - * represents an equivalent $(D_PSYMBOL string) ignoring case; $(D_KEYWORD false) otherwise - * - * Throws: AssertException if the length of any of the strings is 0 - * - * See_Also: opEquals(Object) - */ -bool equalsIgnoreCase (string str, string anotherString) -in -{ - assert(str.length > 0, "mambo.string.equalsIgnoreCase: The length of the first string was 0"); - assert(anotherString.length > 0, "mambo.string.equalsIgnoreCase: The length of the second string was 0"); -} -body -{ - if (str == anotherString) - return true; - - return toFold(str) == toFold(anotherString); -} - -/** - * Compares the $(D_PSYMBOL wstring) to another $(D_PSYMBOL wstring), ignoring case - * considerations. Two wstrings are considered equal ignoring case if they are of the - * same length and corresponding characters in the two wstrings are equal ignoring case. - * - * Params: - * str = The $(D_PSYMBOL wstring) first string to compre to - * anotherString = The $(D_PSYMBOL wstring) to compare the first $(D_PSYMBOL wstring) against - * - * Returns: $(D_KEYWORD true) if the argument is not $(D_KEYWORD null) and it - * represents an equivalent $(D_PSYMBOL wstring) ignoring case; (D_KEYWORD - * false) otherwise - * - * Throws: AssertException if the length of any of the wstrings is 0 - * - * See_Also: opEquals(Object) - */ -bool equalsIgnoreCase (wstring str, wstring anotherString) -in -{ - assert(str.length > 0, "mambo.string.equalsIgnoreCase: The length of the first string was 0"); - assert(anotherString.length > 0, "mambo.string.equalsIgnoreCase: The length of the second string was 0"); -} -body -{ - if (str == anotherString) - return true; - - version (Tango) - return toFold(str) == toFold(anotherString); - - else - return toFold(toUTF8(str)) == toFold(toUTF8(anotherString)); -} - -/** - * Compares the $(D_PSYMBOL dstring) to another $(D_PSYMBOL dstring), ignoring case - * considerations. Two wstrings are considered equal ignoring case if they are of the - * same length and corresponding characters in the two wstrings are equal ignoring case. - * - * Params: - * str = The $(D_PSYMBOL dstring) first string to compare to - * anotherString = The $(D_PSYMBOL wstring) to compare the first $(D_PSYMBOL dstring) against - * - * Returns: $(D_KEYWORD true) if the argument is not $(D_KEYWORD null) and it - * represents an equivalent $(D_PSYMBOL dstring) ignoring case; $(D_KEYWORD false) otherwise - * - * Throws: AssertException if the length of any of the dstrings are 0 - * - * See_Also: opEquals(Object) - */ -bool equalsIgnoreCase (dstring str, dstring anotherString) -in -{ - assert(str.length > 0, "mambo.string.equalsIgnoreCase: The length of the first string was 0"); - assert(anotherString.length > 0, "mambo.string.equalsIgnoreCase: The length of the second string was 0"); -} -body -{ - if (str == anotherString) - return true; - - version (Tango) - return toFold(str) == toFold(anotherString); - - else - return toFold(toUTF8(str)) == toFold(toUTF8(anotherString)); -} - -/** - * Returns the char value at the specified index. An index ranges from 0 to length - 1. - * The first $(D_KEYWORD char) value of the sequence is at index 0, the next at index 1, - * and so on, as for array indexing. - * - * Params: - * str = the string to get the $(D_KEYWORD char) from - * index = the index of the $(D_KEYWORD char) value. - * - * Returns: the $(D_KEYWORD char) value at the specified index of the string. - * The first $(D_KEYWORD char) value is at index 0. - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_CODE index) argument is - * not less than the length of the string. - */ -char charAt (string str, size_t index) -in -{ - assert(str.length > 0, "mambo.string.charAt: The length of the string was 0"); - assert(index <= str.length, "mambo.string.charAt: The index was to greater than the length of the string"); -} -body -{ - return str[index]; -} - -/** - * Returns the $(D_KEYWORD char) value at the specified index. An index ranges from 0 to - * length - 1. The first $(D_KEYWORD char) value of the sequence is at index 0, the next - * at index 1, and so on, as for array indexing. - * - * Params: - * str = the wstring to get the $(D_KEYWORD char) from - * index = the index of the $(D_KEYWORD char) value. - * - * Returns: the $(D_KEYWORD char) value at the specified index of the wstring. - * The first $(D_KEYWORD char) value is at index 0. - * - * Throws: AssertException if the length of the wstring is 0 - * Throws: AssertException if the $(D_CODE index) argument is - * not less than the length of the wstring. - */ -wchar charAt (wstring str, size_t index) -in -{ - assert(str.length > 0, "mambo.string.charAt: The length of the string was 0"); - assert(index <= str.length, "mambo.string.charAt: The index was to greater than the length of the string"); -} -body -{ - return str[index]; -} - -/** - * Returns the $(D_KEYWORD char) value at the specified index. An index ranges from 0 to - * length - 1. The first $(D_KEYWORD char) value of the sequence is at index 0, the next - * at index 1, and so on, as for array indexing. - * - * Params: - * str = the dstring to get the $(D_KEYWORD char) from - * index = the index of the $(D_KEYWORD char) value. - * - * Returns: the $(D_KEYWORD char) value at the specified index of the dstring. - * The first $(D_KEYWORD char) value is at index 0. - * - * Throws: AssertException if the length of the dstring is 0 - * Throws: AssertException if the $(D_CODE index) argument is - * not less than the length of the dstring. - */ -dchar charAt (dstring str, size_t index) -in -{ - assert(str.length > 0, "mambo.string.charAt: The length of the string was 0"); - assert(index <= str.length, "mambo.string.charAt: The index was to greater than the length of the string"); -} -body -{ - return str[index]; -} - -/** - * Returns a new string that is a substring of the specified string. The substring begins - * at the specified $(D_PARAM beginIndex) and extends to the character at index - * $(D_PARAM endIndex) - 1. Thus the length of the substring is $(D_PARAM endIndex - beginIndex). - * - * Examples: - * --- - * "hamburger".substring(4, 8) returns "urge" - * "smiles".substring(1, 5) returns "mile" - * --- - * - * Params: - * str = the string to get the substring from - * beginIndex = the beginning index, inclusive. - * endIndex = the ending index, exclusive. - * - * Returns: the specified substring. - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_PARAM beginIndex) is - * larger than $(D_PARAM endIndex). - * Throws: AssertException if $(D_PARAM endIndex) is larger than the - * length of the $(D_PSYMBOL string). - */ -string substring (string str, size_t beginIndex, size_t endIndex) -in -{ - assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); - assert(beginIndex < endIndex, "mambo.string.substring: The first index was greater the second"); - assert(endIndex <= str.length, "mambo.string.substring: The second index was greater then the length of the string"); -} -body -{ - version (Tango) return str[beginIndex .. endIndex].dup; - else return str[beginIndex .. endIndex].idup; -} - -/** - * Returns a new string that is a substring of the specified string. The substring begins - * at the specified $(D_PARAM beginIndex) and extends to the character at index - * $(D_PARAM endIndex) - 1. Thus the length of the substring is $(D_PARAM endIndex - beginIndex). - * - * Examples: - * --- - * "hamburger".substring(4, 8) returns "urge" - * "smiles".substring(1, 5) returns "mile" - * --- - * - * Params: - * str = the string to get the substring from - * beginIndex = the beginning index, inclusive. - * endIndex = the ending index, exclusive. - * - * Returns: the specified substring. - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_PARAM beginIndex) is - * larger than $(D_PARAM endIndex). - * Throws: AssertException if $(D_PARAM endIndex) is larger than the - * length of the $(D_PSYMBOL string). - */ -wstring substring (wstring str, size_t beginIndex, size_t endIndex) -in -{ - assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); - assert(beginIndex < endIndex, "mambo.string.substring: The first index was greater the second"); - assert(endIndex <= str.length, "mambo.string.substring: The second index was greater then the length of the string"); -} -body -{ - version (Tango) return str[beginIndex .. endIndex].dup; - else return str[beginIndex .. endIndex].idup; -} - -/** - * Returns a new string that is a substring of the specified string. The substring begins - * at the specified $(D_PARAM beginIndex) and extends to the character at index - * $(D_PARAM endIndex) - 1. Thus the length of the substring is $(D_PARAM endIndex - beginIndex). - * - * Examples: - * --- - * "hamburger".substring(4, 8) returns "urge" - * "smiles".substring(1, 5) returns "mile" - * --- - * - * Params: - * str = the string to get the substring from - * beginIndex = the beginning index, inclusive. - * endIndex = the ending index, exclusive. - * - * Returns: the specified substring. - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_PARAM beginIndex) is - * larger than $(D_PARAM endIndex). - * Throws: AssertException if $(D_PARAM endIndex) is larger than the - * length of the $(D_PSYMBOL string). - */ -dstring substring (dstring str, size_t beginIndex, size_t endIndex) -in -{ - assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); - assert(beginIndex < endIndex, "mambo.string.substring: The first index was greater the second"); - assert(endIndex <= str.length, "mambo.string.substring: The second index was greater then the length of the string"); -} -body -{ - version (Tango) return str[beginIndex .. endIndex].dup; - else return str[beginIndex .. endIndex].idup; -} - -/** - * Returns a new string that is a substring of the specified string. The substring begins - * with the character at the specified index and extends to the end of the string. - * - * Examples: - * --- - * "unhappy".substring(2) returns "happy" - * "Harbison".substring(3) returns "bison" - * "emptiness".substring(9) returns "" (an empty string) - * --- - * - * Params: - * str = the string to get the substring from - * beginIndex = the beginning index, inclusive - * - * Returns: the specified substring - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_PARAM beginIndex) is - * larger than the length of the string. - */ -string substring (string str, size_t index) -in -{ - assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); - assert(index < str.length, "mambo.string.substring: The index was greater than the length of the string"); -} -body -{ - return str.substring(index, str.length); -} - -/** - * Returns a new string that is a substring of the specified string. The substring begins - * with the character at the specified index and extends to the end of the string. - * - * Examples: - * --- - * "unhappy".substring(2) returns "happy" - * "Harbison".substring(3) returns "bison" - * "emptiness".substring(9) returns "" (an empty string) - * --- - * - * Params: - * str = the string to get the substring from - * beginIndex = the beginning index, inclusive - * - * Returns: the specified substring - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_PARAM beginIndex) is - * larger than the length of the string. - */ -wstring substring (wstring str, size_t index) -in -{ - assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); - assert(index < str.length, "mambo.string.substring: The index was greater than the length of the string"); -} -body -{ - return str.substring(index, str.length); -} - -/** - * Returns a new string that is a substring of the specified string. The substring begins - * with the character at the specified index and extends to the end of the string. - * - * Examples: - * --- - * "unhappy".substring(2) returns "happy" - * "Harbison".substring(3) returns "bison" - * "emptiness".substring(9) returns "" (an empty string) - * --- - * - * Params: - * str = the string to get the substring from - * beginIndex = the beginning index, inclusive - * - * Returns: the specified substring - * - * Throws: AssertException if the length of the string is 0 - * Throws: AssertException if the $(D_PARAM beginIndex) is - * larger than the length of the string. - */ -dstring substring (dstring str, size_t index) -in -{ - assert(str.length > 0, "mambo.string.substring: The length of the string was 0"); - assert(index < str.length, "mambo.string.substring: The index was greater than the length of the string"); -} -body -{ - return str.substring(index, str.length); -} - -/** - * Returns a new string that is a substring of the given string. - * - * This substring is the character sequence that starts at character - * position pos and has a length of n characters. - * - * Params: - * str = the string to get the substring from - * pos = position of a character in the current string to be used - * as starting character for the substring. - * n = Length of the substring. If this value would make the - * substring to span past the end of the current string content, - * only those characters until the end of the string are used. - * size_t.max is the greatest possible value for an element of - * type size_t, therefore, when this value is used, all the - * characters between pos and the end of the string are used as - * the initialization substring. - * - * Returns: a string containing a substring of the given string - * - * Throws: AssertException if pos is greater than the length of the string - */ -string substr (string str, size_t pos = 0, size_t n = size_t.max) -in -{ - assert(pos < str.length, "mambo.string.substr: The given position was greater than the length of the string."); -} -body -{ - size_t end; - - if (n == size_t.max) - end = str.length; - - else - { - end = pos + n; - - if (end > str.length) - end = str.length; - } - - version (Tango) return str[pos .. end].dup; - else return str[pos .. end].idup; -} - -/** - * Returns a new string that is a substring of the given string. - * - * This substring is the character sequence that starts at character - * position pos and has a length of n characters. - * - * Params: - * str = the string to get the substring from - * pos = position of a character in the current string to be used - * as starting character for the substring. - * n = Length of the substring. If this value would make the - * substring to span past the end of the current string content, - * only those characters until the end of the string are used. - * size_t.max is the greatest possible value for an element of - * type size_t, therefore, when this value is used, all the - * characters between pos and the end of the string are used as - * the initialization substring. - * - * Returns: a string containing a substring of the given string - * - * Throws: AssertException if pos is greater than the length of the string - */ -wstring substr (wstring str, size_t pos = 0, size_t n = size_t.max) -in -{ - assert(pos < str.length, "mambo.string.substr: The given position was greater than the length of the string."); -} -body -{ - size_t end; - - if (n == size_t.max) - end = str.length; - - else - { - end = pos + n; - - if (end > str.length) - end = str.length; - } - - version (Tango) return str[pos .. end].dup; - else return str[pos .. end].idup; -} - -/** - * Returns a new string that is a substring of the given string. - * - * This substring is the character sequence that starts at character - * position pos and has a length of n characters. - * - * Params: - * str = the string to get the substring from - * pos = position of a character in the current string to be used - * as starting character for the substring. - * n = Length of the substring. If this value would make the - * substring to span past the end of the current string content, - * only those characters until the end of the string are used. - * size_t.max is the greatest possible value for an element of - * type size_t, therefore, when this value is used, all the - * characters between pos and the end of the string are used as - * the initialization substring. - * - * Returns: a string containing a substring of the given string - * - * Throws: AssertException if pos is greater than the length of the string - */ -dstring substr (dstring str, size_t pos = 0, size_t n = size_t.max) -in -{ - assert(pos < str.length, "mambo.string.substr: The given position was greater than the length of the string."); -} -body -{ - size_t end; - - if (n == size_t.max) - end = str.length; - - else - { - end = pos + n; - - if (end > str.length) - end = str.length; - } - - version (Tango) return str[pos .. end].dup; - else return str[pos .. end].idup; -} - -/** - * Finds the first occurence of sub in str - * - * Params: - * str = the string to find in - * sub = the substring to find - * start = where to start finding - * - * Returns: the index of the substring or size_t.max when nothing was found - */ -size_t find (string str, string sub) -{ - version (Tango) - { - size_t index = str.locatePattern(sub); - - if (index == str.length) - return size_t.max; - - return index; - } - - else - return std.string.indexOf(str, sub); - -} - -/** - * Finds the first occurence of sub in str - * - * Params: - * str = the string to find in - * sub = the substring to find - * start = where to start finding - * - * Returns: the index of the substring or size_t.max when nothing was found - */ -size_t find (wstring str, wstring sub) -{ - version (Tango) - { - size_t index = str.locatePattern(sub); - - if (index == str.length) - return size_t.max; - - return index; - } - - else - return std.string.indexOf(str, sub); -} - -/** - * Finds the first occurence of sub in str - * - * Params: - * str = the string to find in - * sub = the substring to find - * start = where to start finding - * - * Returns: the index of the substring or size_t.max when nothing was found - */ -size_t find (dstring str, dstring sub) -{ - version (Tango) - { - size_t index = str.locatePattern(sub); - - if (index == str.length) - return size_t.max; - - return index; - } - - else - return std.string.indexOf(str, sub); -} - -/** - * Compares to strings, ignoring case differences. Returns 0 if the content - * matches, less than zero if a is "less" than b, or greater than zero - * where a is "bigger". - * - * Params: - * a = the first array - * b = the second array - * end = the index where the comparision will end - * - * Returns: Returns 0 if the content matches, less than zero if a is - * "less" than b, or greater than zero where a is "bigger". - * - * See_Also: mambo.collection.array.compare - */ -int compareIgnoreCase (U = size_t) (string a, string b, U end = U.max) -{ - return a.toFold().compare(b.toFold(), end); -} - -/** - * Compares to strings, ignoring case differences. Returns 0 if the content - * matches, less than zero if a is "less" than b, or greater than zero - * where a is "bigger". - * - * Params: - * a = the first array - * b = the second array - * end = the index where the comparision will end - * - * Returns: Returns 0 if the content matches, less than zero if a is - * "less" than b, or greater than zero where a is "bigger". - * - * See_Also: mambo.collection.array.compare - */ -int compareIgnoreCase (U = size_t) (wstring a, wstring b, U end = U.max) -{ - return a.toFold().compare(b.toFold(), end); -} - -/** - * Compares to strings, ignoring case differences. Returns 0 if the content - * matches, less than zero if a is "less" than b, or greater than zero - * where a is "bigger". - * - * Params: - * a = the first array - * b = the second array - * end = the index where the comparision will end - * - * Returns: Returns 0 if the content matches, less than zero if a is - * "less" than b, or greater than zero where a is "bigger". - * - * See_Also: mambo.collection.array.compare - */ -int compareIgnoreCase (U = size_t) (dstring a, dstring b, U end = U.max) -{ - return a.toFold().compare(b.toFold(), end); -} - -/** - * Compares to strings, ignoring case differences. Returns 0 if the content - * matches, less than zero if a is "less" than b, or greater than zero - * where a is "bigger". - * - * Params: - * a = the first array - * b = the second array - * end = the index where the comparision will end - * - * Returns: Returns 0 if the content matches, less than zero if a is - * "less" than b, or greater than zero where a is "bigger". - * - * See_Also: mambo.string.compareIgnoreCase - */ -alias compareIgnoreCase icompare; - -/** - * Checks if the given character is a hexdecimal digit character. - * Hexadecimal digits are any of: 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F - * - * Params: - * ch = the character to be checked - * - * Returns: true if the given character is a hexdecimal digit character otherwise false - */ -bool isHexDigit (dchar ch) -{ - version (Tango) - { - switch (ch) - { - case 'A': return true; - case 'B': return true; - case 'C': return true; - case 'D': return true; - case 'E': return true; - case 'F': return true; - - case 'a': return true; - case 'b': return true; - case 'c': return true; - case 'd': return true; - case 'e': return true; - case 'f': return true; - - default: break; - } - - if (isDigit(ch)) - return true; - } - - else - if (isxdigit(ch) != 0) - return true; - - return false; -} - -/*version (Tango) -{ - string toString (string str) - { - return str; - } - - string toString (wstring str) - { - return tango.text.convert.Utf.toString(str); - } - - string toString (dstring str) - { - return tango.text.convert.Utf.toString(str); - } -}*/ - -version (Phobos) -{ - /** - * Converts the given string to C-style 0 terminated string. - * - * Params: - * str = the string to convert - * - * Returns: the a C-style 0 terminated string. - */ - dchar* toString32z (dstring str) - { - return (str ~ '\0').dup.ptr; - } - - /** - * Converts a C-style 0 terminated string to a wstring - * - * Params: - * str = the C-style 0 terminated string - * - * Returns: the converted wstring - */ - wstring fromString16z (wchar* str) - { - return str[0 .. strlen(str)].idup; - } - - /** - * Converts a C-style 0 terminated string to a dstring - * Params: - * str = the C-style 0 terminated string - * - * Returns: the converted dstring - */ - dstring fromString32z (dchar* str) - { - return str[0 .. strlen(str)].idup; - } - - /** - * Gets the length of the given C-style 0 terminated string - * - * Params: - * str = the C-style 0 terminated string to get the length of - * - * Returns: the length of the string - */ - size_t strlen (wchar* str) - { - size_t i = 0; - - if (str) - while(*str++) - ++i; - - return i; - } - - /** - * Gets the length of the given C-style 0 terminated string - * - * Params: - * str = the C-style 0 terminated string to get the length of - * - * Returns: the length of the string - */ - size_t strlen (dchar* str) - { - size_t i = 0; - - if (str) - while(*str++) - ++i; - - return i; - } -} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/xml/PhobosXML.d --- a/orange/xml/PhobosXML.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/xml/PhobosXML.d Wed Aug 03 21:58:21 2011 +0200 @@ -131,7 +131,7 @@ import std.string; import std.encoding; -immutable cdata = ""; } - string toEmptyString() { return toNonEndString() ~ " />"; } + string toEmptyString() { return toNonEndString() ~ "/>"; } } /** diff -r 947c32ec0ea7 -r 90f6a44b9475 orange/xml/XMLDocument.d --- a/orange/xml/XMLDocument.d Mon Oct 04 20:45:32 2010 +0200 +++ b/orange/xml/XMLDocument.d Wed Aug 03 21:58:21 2011 +0200 @@ -12,7 +12,7 @@ import tango.text.xml.Document; import tango.io.Stdout; - import orange.util.string; + import orange.core.string; } else @@ -25,7 +25,7 @@ version = Phobos; } -import orange.util.io; +import orange.core.io; template Char (T) { @@ -228,7 +228,7 @@ void attach (Node node) { version (Tango) this.node.move(node.node); - else this.node.elements ~= node.node; + else this.node ~= node.node; } } diff -r 947c32ec0ea7 -r 90f6a44b9475 tests/Serializer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/Serializer.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,616 @@ + +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: Nov 5, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module tests.Serializer; + +private: + +import orange.serialization.Serializer; +import orange.serialization.archives.XMLArchive; +import orange.core.io; +import orange.core.string; + +bool containsDefaultXmlContent (string source) +{ + return source.containsXmlHeader() && + source.containsArchive() && + source.containsXmlTag("data"); +} + +bool containsXmlHeader (string source) +{ + return source.contains(``); +} + +bool containsArchive (string source) +{ + return source.containsArchiveHeader() && source.contains(""); +} + +bool containsArchiveHeader (string source) +{ + return source.contains(``) || + source.contains(``); +} + +bool containsXmlTag (string source, string tag, bool simple = false) +{ + return source.containsXmlTag(tag, null, null, simple); +} + +bool containsXmlTag (string source, string tag, string attributes, bool simple = false) +{ + return source.containsXmlTag(tag, attributes, null, simple); +} + +bool containsXmlTag (string source, string tag, string attributes, string content, bool simple = false) +{ + string pattern = '<' ~ tag; + + if (attributes.length > 0) + pattern ~= ' ' ~ attributes; + + if (simple) + return source.contains(pattern ~ "/>"); + + if (content.length > 0) + return source.contains(pattern ~ '>' ~ content ~ "'); + + return source.contains(pattern ~ '>') && source.contains("'); +} + +enum Foo { a, b, c } +typedef int Int; + +class A +{ + equals_t opEquals (Object other) + { + if (auto o = cast(A) other) + return true; + + return false; + } +} + +struct B +{ + version (Tango) + { + equals_t opEquals (B b) + { + return true; + } + } + + else + { + mixin(`bool opEquals (ref const B) const + { + return true; + }`); + } +} + +class C { string str; wstring wstr; dstring dstr; } +class D { int[] arr; } +class E { int[int] aa; } +class F { int value; int* ptr; int* ptr2; } +class G { Foo foo; } + +int pointee; + +class H +{ + bool bool_; + byte byte_; + //cdouble cdouble_; // currently not suppported by to!() + //cent cent_; // currently not implemented but a reserved keyword + //cfloat cfloat_; // currently not suppported by to!() + char char_; + //creal creal_; // currently not suppported by to!() + dchar dchar_; + double double_; + float float_; + //idouble idouble_; // currently not suppported by to!() + //ifloat ifloat_; // currently not suppported by to!() + int int_; + //ireal ireal_; // currently not suppported by to!() + long long_; + real real_; + short short_; + ubyte ubyte_; + //ucent ucent_; // currently not implemented but a reserved keyword + uint uint_; + ulong ulong_; + ushort ushort_; + + version (Tango) + wchar wchar_; // Phobos to!() function can't handle string -> wchar + + equals_t opEquals (Object other) + { + if (auto o = cast(H) other) + { + auto result = bool_ == o.bool_ && + byte_ == o.byte_ && + //cdouble_ == o.cdouble_ && // currently not suppported by to!() + //cent_ == o.cent_ && // currently not implemented but a reserved keyword + //cfloat_ == o.cfloat_ && // currently not suppported by to!() + char_ == o.char_ && + //creal_ == o.creal_ && // currently not suppported by to!() + dchar_ == o.dchar_ && + double_ == o.double_ && + float_ == o.float_ && + //idouble_ == o.idouble_ && // currently not suppported by to!() + //ifloat_ == o.ifloat_ && // currently not suppported by to!() + int_ == o.int_ && + //ireal_ == o.ireal_ && // currently not suppported by to!() + long_ == o.long_ && + real_ == o.real_ && + short_ == o.short_ && + ubyte_ == o.ubyte_ && + //ucent_ == o.ucent_ && // currently not implemented but a reserved keyword + uint_ == o.uint_ && + ulong_ == o.ulong_ && + ushort_ == o.ushort_; + + version (Tango) + return result && wchar_ == o.wchar_; + + else + return result; + } + + return false; + } +} + +class I +{ + Int a; +} + +class J +{ + int[] firstSource; + int[] firstSlice; + + int[] secondSlice; + int[] secondSource; +} + +class K +{ + int[int] a; + int[int] b; +} + +import orange.test.UnitTester; +Serializer serializer; +XMLArchive!(char) archive; + +A a; +B b; +C c; +D d; +E e; +F f; +F fDeserialized; +G g; +H h; +I i; +J j; +J jDeserialized; +K k; + +string data; + +unittest +{ + archive = new XMLArchive!(char); + serializer = new Serializer(archive); + + a = new A; + + c = new C; + c.str = "foo"; + c.wstr = "bar"; + c.dstr = "foobar"; + + + d = new D; + d.arr = [27, 382, 283, 3820, 32, 832].dup; + + e = new E; + e.aa = [3 : 4, 1 : 2, 39 : 472, 6 : 7]; + + pointee = 3; + f = new F; + f.value = 9; + f.ptr = &f.value; + f.ptr2 = &pointee; + + g = new G; + g.foo = Foo.b; + + h = new H; + h.bool_ = true; + h.byte_ = 1; + h.char_ = 'a'; + //h.cdouble_ = 0.0 + 0.0 * 1.0i; // currently not supported by to!() + //h.cfloat_ = 0.0f + 0.0f * 1.0i; // currently not supported by to!() + //h.creal_ = 0.0 + 0.0 * 1.0i; // currently not supported by to!() + h.dchar_ = 'b'; + h.double_ = 0.0; + h.float_ = 0.0f; + //h.idouble_ = 0.0 * 1.0i; // currently not supported by to!() + //h.ifloat_ = 0.0f * 1.0i; // currently not supported by to!() + h.int_ = 1; + //h.ireal_ = 0.0 * 1.0i; // currently not supported by to!() + h.long_ = 1L; + h.real_ = 0.0; + h.short_ = 1; + h.ubyte_ = 1U; + h.uint_ = 1U; + h.ulong_ = 1LU; + h.ushort_ = 1U; + + version (Tango) + h.wchar_ = 'c'; + + i = new I; + i.a = 1; + + j = new J; + j.firstSource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].dup; + j.firstSlice = j.firstSource[3 .. 7]; + j.secondSource = [10, 11, 12, 13, 14, 15].dup; + j.secondSlice = j.secondSource[1 .. 4]; + + k = new K; + k.a = [3 : 4, 1 : 2, 39 : 472, 6 : 7]; + k.b = k.a; + + describe("Serializer") in { + describe("serialize object") in { + it("should return a serialized object") in { + serializer.reset; + serializer.serialize(a); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().contains(``)); + }; + }; + + describe("deserialize object") in { + it("should return a deserialized object equal to the original object") in { + auto aDeserialized = serializer.deserialize!(A)(archive.data); + assert(a == aDeserialized); + }; + }; + + describe("serialize struct") in { + it("should return a serialized struct") in { + serializer.reset; + serializer.serialize(B()); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().contains(``)); + }; + }; + + describe("deserialize struct") in { + it("should return a deserialized struct equal to the original struct") in { + auto bDeserialized = serializer.deserialize!(B)(archive.data); + assert(b == bDeserialized); + }; + }; + + describe("serialize strings") in { + it("should return serialized strings") in { + serializer.reset; + serializer.serialize(c); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.C" type="C" key="0" id="0"`)); + + version (Tango) string type = "char"; + else string type = "immutable(char)"; + + assert(archive.data().containsXmlTag("string", `type="` ~ type ~ `" length="3" key="str" id="1"`, "foo")); + + version (Tango) type = "wchar"; + else type = "immutable(wchar)"; + + assert(archive.data().containsXmlTag("string", `type="` ~ type ~ `" length="3" key="wstr" id="2"`, "bar")); + + version (Tango) type = "dchar"; + else type = "immutable(dchar)"; + + assert(archive.data().containsXmlTag("string", `type="` ~ type ~ `" length="6" key="dstr" id="3"`, "foobar")); + }; + }; + + describe("deserialize string") in { + it("should return a deserialized string equal to the original string") in { + auto cDeserialized = serializer.deserialize!(C)(archive.data); + assert(c.str == cDeserialized.str); + }; + }; + + describe("serialize array") in { + it("should return a serialized array") in { + serializer.reset; + serializer.serialize(d); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.D" type="D" key="0" id="0"`)); + assert(archive.data().containsXmlTag("array", `type="int" length="6" key="arr" id="1"`)); + assert(archive.data().containsXmlTag("int", `key="0" id="2"`, "27")); + assert(archive.data().containsXmlTag("int", `key="1" id="3"`, "382")); + assert(archive.data().containsXmlTag("int", `key="2" id="4"`, "283")); + assert(archive.data().containsXmlTag("int", `key="3" id="5"`, "3820")); + assert(archive.data().containsXmlTag("int", `key="4" id="6"`, "32")); + assert(archive.data().containsXmlTag("int", `key="5" id="7"`, "832")); + }; + }; + + describe("deserialize array") in { + it("should return a deserialize array equal to the original array") in { + auto dDeserialized = serializer.deserialize!(D)(archive.data); + assert(d.arr == dDeserialized.arr); + }; + }; + + describe("serialize associative array") in { + it("should return a serialized associative array") in { + serializer.reset(); + serializer.serialize(e); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.E" type="E" key="0" id="0"`)); + version (Tango) assert(archive.data().containsXmlTag("associativeArray", `keyType="int" valueType="int" length="4" key="aa" id="1"`)); + + assert(archive.data().containsXmlTag("key", `key="0"`)); + assert(archive.data().containsXmlTag("int", `key="0" id="2"`, "1")); + assert(archive.data().containsXmlTag("value", `key="0"`)); + assert(archive.data().containsXmlTag("int", `key="0" id="3"`, "2")); + + assert(archive.data().containsXmlTag("key", `key="1"`)); + assert(archive.data().containsXmlTag("int", `key="1" id="4"`, "3")); + assert(archive.data().containsXmlTag("value", `key="1"`)); + assert(archive.data().containsXmlTag("int", `key="1" id="5"`, "4")); + + assert(archive.data().containsXmlTag("key", `key="2"`)); + assert(archive.data().containsXmlTag("int", `key="2" id="6"`, "6")); + assert(archive.data().containsXmlTag("value", `key="2"`)); + assert(archive.data().containsXmlTag("int", `key="2" id="7"`, "7")); + + assert(archive.data().containsXmlTag("key", `key="3"`)); + assert(archive.data().containsXmlTag("int", `key="3" id="8"`, "39")); + assert(archive.data().containsXmlTag("value", `key="3"`)); + assert(archive.data().containsXmlTag("int", `key="3" id="9"`, "472")); + }; + }; + + describe("deserialize associative array") in { + it("should return an associative array equal to the original associative array") in { + auto eDeserialized = serializer.deserialize!(E)(archive.data); + + foreach (k, v ; eDeserialized.aa) + assert(e.aa[k] == v); + + //assert(e.aa == eDeserialized.aa); // cannot compare associative array + }; + }; + + describe("serialize pointer") in { + it("should return a serialized pointer") in { + serializer.reset(); + serializer.serialize(f); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.F" type="F" key="0" id="0"`)); + assert(archive.data().containsXmlTag("pointer", `key="ptr" id="2"`)); + assert(archive.data().containsXmlTag("reference", `key="1"`, "1")); + assert(archive.data().containsXmlTag("int", `key="value" id="1"`, "9")); + }; + }; + + describe("deserialize pointer") in { + fDeserialized = serializer.deserialize!(F)(archive.data); + + it("should return a deserialized pointer equal to the original pointer") in { + assert(*f.ptr == *fDeserialized.ptr); + }; + + it("the pointer should point to the deserialized value") in { + assert(fDeserialized.ptr == &fDeserialized.value); + }; + }; + + describe("serialize enum") in { + it("should return a serialized enum") in { + serializer.reset(); + serializer.serialize(g); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.G" type="G" key="0" id="0"`)); + assert(archive.data().containsXmlTag("enum", `type="Foo" baseType="int" key="foo" id="1"`, "1")); + }; + }; + + + describe("deserialize enum") in { + it("should return an enum equal to the original enum") in { + auto gDeserialized = serializer.deserialize!(G)(archive.data); + assert(g.foo == gDeserialized.foo); + }; + }; + + describe("serialize primitives") in { + it("should return serialized primitives") in { + serializer.reset; + serializer.serialize(h); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.H" type="H" key="0" id="0"`)); + assert(archive.data().containsXmlTag("bool", `key="bool_" id="1"`, "true")); + assert(archive.data().containsXmlTag("byte", `key="byte_" id="2"`, "1")); + assert(archive.data().containsXmlTag("char", `key="char_" id="3"`, "a")); + assert(archive.data().containsXmlTag("dchar", `key="dchar_" id="4"`, "b")); + assert(archive.data().containsXmlTag("double", `key="double_" id="5"`, "0")); + assert(archive.data().containsXmlTag("float", `key="float_" id="6"`, "0")); + assert(archive.data().containsXmlTag("int", `key="int_" id="7"`, "1")); + assert(archive.data().containsXmlTag("long", `key="long_" id="8"`, "1")); + assert(archive.data().containsXmlTag("real", `key="real_" id="9"`, "0")); + assert(archive.data().containsXmlTag("short", `key="short_" id="10"`, "1")); + assert(archive.data().containsXmlTag("ubyte", `key="ubyte_" id="11"`, "1")); + assert(archive.data().containsXmlTag("uint", `key="uint_" id="12"`, "1")); + assert(archive.data().containsXmlTag("ulong", `key="ulong_" id="13"`, "1")); + assert(archive.data().containsXmlTag("ushort", `key="ushort_" id="14"`, "1")); + + version (Tango) + assert(archive.data().containsXmlTag("wchar", `key="wchar_" id="15"`, "c")); + }; + }; + + describe("deserialize primitives") in { + it("should return deserialized primitives equal to the original primitives") in { + auto hDeserialized = serializer.deserialize!(H)(archive.data); + assert(h == hDeserialized); + }; + }; + + describe("serialize typedef") in { + it("should return a serialized typedef") in { + serializer.reset(); + serializer.serialize(i); + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.I" type="I" key="0" id="0"`)); + assert(archive.data().containsXmlTag("typedef", `type="Int" key="a" id="2"`)); + assert(archive.data().containsXmlTag("int", `key="1" id="3"`, "1")); + }; + }; + + // describe("deserialize typedef") in { + // it("should return a deserialized typedef equal to the original typedef") in { + // auto iDeserialized = serializer.deserialize!(I)(archive.data); + // assert(i.a == iDeserialized.a); + // }; + // }; + + describe("serialize slices") in { + it("should return serialized slices") in { + serializer.reset(); + serializer.serialize(j); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.J" type="J" key="0" id="0"`)); + assert(archive.data().containsXmlTag("array", `type="int" length="10" key="firstSource" id="1"`)); + + assert(archive.data().containsXmlTag("int", `key="0" id="2"`, "0")); + assert(archive.data().containsXmlTag("int", `key="1" id="3"`, "1")); + assert(archive.data().containsXmlTag("int", `key="2" id="4"`, "2")); + assert(archive.data().containsXmlTag("int", `key="3" id="5"`, "3")); + assert(archive.data().containsXmlTag("int", `key="4" id="6"`, "4")); + assert(archive.data().containsXmlTag("int", `key="5" id="7"`, "5")); + assert(archive.data().containsXmlTag("int", `key="6" id="8"`, "6")); + assert(archive.data().containsXmlTag("int", `key="7" id="9"`, "7")); + assert(archive.data().containsXmlTag("int", `key="8" id="10"`, "8")); + assert(archive.data().containsXmlTag("int", `key="9" id="11"`, "9")); + + version (Tango) + { + assert(archive.data().containsXmlTag("slice", `key="firstSlice" offset="3" length="4"`, "1")); + assert(archive.data().containsXmlTag("slice", `key="secondSlice" offset="1" length="3"`, "21")); + } + + assert(archive.data().containsXmlTag("array", `type="int" length="6" key="secondSource" id="21"`)); + + assert(archive.data().containsXmlTag("int", `key="0" id="22"`, "10")); + assert(archive.data().containsXmlTag("int", `key="1" id="23"`, "11")); + assert(archive.data().containsXmlTag("int", `key="2" id="24"`, "12")); + assert(archive.data().containsXmlTag("int", `key="3" id="25"`, "13")); + assert(archive.data().containsXmlTag("int", `key="4" id="26"`, "14")); + assert(archive.data().containsXmlTag("int", `key="5" id="27"`, "15")); + }; + }; + + describe("deserialize slices") in { + jDeserialized = serializer.deserialize!(J)(archive.data); + + it("should return deserialized strings equal to the original strings") in { + assert(j.firstSource == jDeserialized.firstSource); + assert(j.secondSource == jDeserialized.secondSource); + }; + + it("should return deserialized slices equal to the original slices") in { + assert(j.firstSlice == jDeserialized.firstSlice); + assert(j.secondSlice == jDeserialized.secondSlice); + }; + + it("the slices should be equal to a slice of the original sources") in { + assert(jDeserialized.firstSource[3 .. 7] == jDeserialized.firstSlice); + assert(jDeserialized.secondSource[1 .. 4] == jDeserialized.secondSlice); + + assert(j.firstSource[3 .. 7] == jDeserialized.firstSlice); + assert(j.secondSource[1 .. 4] == jDeserialized.secondSlice); + }; + + it("the slices should be able to modify the sources") in { + jDeserialized.firstSlice[0] = 55; + jDeserialized.secondSlice[0] = 3; + + assert(jDeserialized.firstSource == [0, 1, 2, 55, 4, 5, 6, 7, 8, 9]); + assert(jDeserialized.secondSource == [10, 3, 12, 13, 14, 15]); + }; + }; + + describe("serialize associative array references") in { + it("should return a serialized associative array and a serialized reference") in { + serializer.reset(); + serializer.serialize(k); + + assert(archive.data().containsDefaultXmlContent()); + assert(archive.data().containsXmlTag("object", `runtimeType="tests.Serializer.K" type="K" key="0" id="0"`)); + version (Tango) assert(archive.data().containsXmlTag("associativeArray", `keyType="int" valueType="int" length="4" key="a" id="1"`)); + + assert(archive.data().containsXmlTag("key", `key="0"`)); + assert(archive.data().containsXmlTag("int", `key="0" id="2"`, "1")); + assert(archive.data().containsXmlTag("value", `key="0"`)); + assert(archive.data().containsXmlTag("int", `key="0" id="3"`, "2")); + + assert(archive.data().containsXmlTag("key", `key="1"`)); + assert(archive.data().containsXmlTag("int", `key="1" id="4"`, "3")); + assert(archive.data().containsXmlTag("value", `key="1"`)); + assert(archive.data().containsXmlTag("int", `key="1" id="5"`, "4")); + + assert(archive.data().containsXmlTag("key", `key="2"`)); + assert(archive.data().containsXmlTag("int", `key="2" id="6"`, "6")); + assert(archive.data().containsXmlTag("value", `key="2"`)); + assert(archive.data().containsXmlTag("int", `key="2" id="7"`, "7")); + + assert(archive.data().containsXmlTag("key", `key="3"`)); + assert(archive.data().containsXmlTag("int", `key="3" id="8"`, "39")); + assert(archive.data().containsXmlTag("value", `key="3"`)); + assert(archive.data().containsXmlTag("int", `key="3" id="9"`, "472")); + + assert(archive.data().containsXmlTag("reference", `key="b"`, "1")); + }; + }; + + describe("deserialize associative array references") in { + it("should return two deserialized associative arrays pointing to the same data") in { + auto kDeserialized = serializer.deserialize!(K)(archive.data); + + assert(kDeserialized.a is kDeserialized.b); + }; + }; + }; +} \ No newline at end of file diff -r 947c32ec0ea7 -r 90f6a44b9475 tests/all.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/all.d Wed Aug 03 21:58:21 2011 +0200 @@ -0,0 +1,15 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. All rights reserved. + * Authors: Jacob Carlborg + * Version: Initial created: Nov 5, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module tests.all; + +import orange.test.UnitTester; +import tests.Serializer; + +void main () +{ + run; +} \ No newline at end of file