# HG changeset patch # User Jacob Carlborg # Date 1274887153 -7200 # Node ID f7b078e85f7fd9ab0e810203723d486ce453eb3e First commit diff -r 000000000000 -r f7b078e85f7f .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,16 @@ +syntax: glob +*.DS_Store +*.o +dsss_objs +dsss_docs +dsss_imports +dsss.last +*.orig +*.sh +*.a +*.rf +*.dylib +main.d +main +*.framework +kandil \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f dsss.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dsss.conf Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,1 @@ +[orange] \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/_.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/_.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,13 @@ +/** + * 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 000000000000 -r f7b078e85f7f orange/serialization/Events.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/Events.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,47 @@ +/** + * 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.serialization.Events; + +import orange.util._; + +template OnDeserialized (alias method) +{ + orange.serialization.Events.Event!(method) __onDeserialized; +} + +template OnDeserializing (alias method) +{ + orange.serialization.Events.Event!(method) __onDeserializing; +} + +template OnSerialized (alias method) +{ + orange.serialization.Events.Event!(method) __onSerialized; +} + +template OnSerializing (alias method) +{ + orange.serialization.Events.Event!(method) __onSerializing; +} + +struct Event (alias m) +{ + private const method = &m; + + void opCall (T) (T value) + { + void delegate () dg; + dg.ptr = cast(void*) value; + dg.funcptr = method; + dg(); + } +} + +package const onDeserializedField = "__onDeserialized"; +package const onDeserializingField = "__onDeserializing"; +package const onSerializedField = "__onSerialized"; +package const onSerializingField = "__onSerializing"; \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/RegisterWrapper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/RegisterWrapper.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,69 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 4, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.serialization.RegisterWrapper; + +import orange.serialization.archives.Archive; +import orange.serialization.Serializer; + +class RegisterBase +{ + +} + +class SerializeRegisterWrapper (T, ArchiveType : IArchive) : RegisterBase +{ + private alias Serializer!(ArchiveType) SerializerType; + private alias SerializerType.DataType DataType; + private void delegate (T, SerializerType, DataType) dg; + private void function (T, SerializerType, DataType) func; + + this (void delegate (T, SerializerType, DataType) dg) + { + this.dg = dg; + } + + this (void function (T, SerializerType, DataType) func) + { + this.func = func; + } + + void opCall (T value, SerializerType archive, DataType key) + { + if (dg) + dg(value, archive, key); + + else if (func) + func(value, archive, key); + } +} + +class DeserializeRegisterWrapper (T, ArchiveType : IArchive) : RegisterBase +{ + private alias Serializer!(ArchiveType) SerializerType; + private alias SerializerType.DataType DataType; + private void delegate (ref T, SerializerType, DataType) dg; + private void function (ref T, SerializerType, DataType) func; + + this (void delegate (ref T, SerializerType, DataType) dg) + { + this.dg = dg; + } + + this (void function (ref T, SerializerType, DataType) func) + { + this.func = func; + } + + void opCall (ref T value, SerializerType archive, DataType key) + { + if (dg) + dg(value, archive, key); + + if (func) + func(value, archive, key); + } +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/Serializable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/Serializable.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,58 @@ +/** + * 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.serialization.Serializable; + +import orange.serialization.archives.Archive; +import orange.serialization.Events; +import orange.util.CTFE; + +template Serializable () +{ + void toData (T) (T archive, T.DataType key) + { + alias typeof(this) ThisType; + + foreach (i, dummy ; typeof(T.tupleof)) + { + alias typeof(ThisType.tupleof[i]) FieldType; + + const field = nameOfFieldAt!(ThisType, i); + auto value = getValueOfField!(ThisType, FieldType, field)(this); + + archive.archive(value, field); + } + } + + void fromData (T) (T archive, T.DataType key) + { + alias typeof(this) ThisType; + + foreach (i, dummy ; typeof(ThisType.tupleof)) + { + alias typeof(ThisType.tupleof[i]) FieldType; + + const field = nameOfFieldAt!(ThisType, i); + auto value = archive.unarchive!(FieldType)(field); + + setValueOfField!(FieldType, ThisType, field)(this, value); + } + } +} + +template NonSerialized (alias field) +{ + NonSerializedField!(field) __nonSerialized; +} + +struct NonSerializedField (alias f) +{ + 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 diff -r 000000000000 -r f7b078e85f7f orange/serialization/SerializationException.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/SerializationException.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,31 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Jan 30, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.serialization.SerializationException; + +import orange.util.string; + +class SerializationException : Exception +{ + this (string message) + { + super(message); + } + + this (string message, string file, long line) + { + version (Tango) + super(message, file, line); + + else + super(message); + } + + this (Exception exception) + { + super(exception.msg, exception.file, exception.line, exception.next, exception.info); + } +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/Serializer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/Serializer.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,606 @@ +/** + * 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.serialization.Serializer; + +version (Tango) +{ + import tango.util.Convert : to, ConversionException; +} + +import orange.serialization._; +import orange.serialization.archives._; +import orange.util._; + +private +{ + alias orange.util.CTFE.contains ctfeContains; + + private enum Mode + { + serializing, + deserializing + } + + alias Mode.serializing serializing; + alias Mode.deserializing deserializing; +} + +class Serializer (ArchiveType : IArchive) +{ + static assert(isArchive!(ArchiveType), format!(`The type "`, ArchiveType, `" does not implement the necessary methods to be an archive.`)); + + private + { + ArchiveType archive; + alias ArchiveType.DataType DataType; + + RegisterBase[string] serializers; + RegisterBase[string] deserializers; + + size_t keyCounter; + + bool hasBegunSerializing; + bool hasBegunDeserializing; + } + + this () + { + archive = new ArchiveType; + } + + 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; + } + + DataType serialize (T) (T value, DataType key = null) + { + if (!hasBegunSerializing) + hasBegunSerializing = true; + + if (!key) + key = nextKey; + + archive.beginArchiving(); + + static if (isTypeDef!(T)) + serializeTypeDef(value, key); + + static if (isObject!(T)) + serializeObject(value, key); + + else static if (isStruct!(T)) + serializeStruct(value, key); + + else static if (isString!(T)) + serializeString(value, key); + + else static if (isArray!(T)) + serializeArray(value, key); + + else static if (isAssociativeArray!(T)) + serializeAssociativeArray(value, key); + + else static if (isPrimitive!(T)) + serializePrimitive(value, key); + + else static if (isPointer!(T)) + { + static if (isFunctionPointer!(T)) + static assert(false, format!(`The type "`, T, `" cannot be serialized.`)); + + else + serializePointer(value, key); + } + + + else static if (isEnum!(T)) + serializeEnum(value, key); + + else + static assert(false, format!(`The type "`, T, `" cannot be serialized.`)); + + return archive.data; + } + + private void serializeObject (T) (T value, DataType key) + { + auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); + + triggerEvents(serializing, value, { + archive.archive(value, key, { + auto runtimeType = value.classinfo.name; + + if (runtimeType in serializers) + { + auto wrapper = getSerializerWrapper!(T)(runtimeType); + wrapper(value, this, key); + } + + else static if (isSerializable!(T, ArchiveType)) + value.toData(this, key); + + 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 serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); + + objectStructSerializeHelper(value, nonSerializedFields); + } + }); + }); + } + + private void serializeStruct (T) (T value, DataType key) + { + auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); + + triggerEvents(serializing, value, { + archive.archive(value, key, { + auto type = toDataType(T.stringof); + + if (type in serializers) + { + auto wrapper = getSerializerWrapper!(T)(type); + wrapper(value, this, key); + } + + else + { + static if (isSerializable!(T, ArchiveType)) + value.toData(this, key); + + else + objectStructSerializeHelper(value, nonSerializedFields); + } + }); + }); + } + + private void serializeString (T) (T value, DataType key) + { + archive.archive(value, key); + } + + private void serializeArray (T) (T value, DataType key) + { + archive.archive(value, key, { + foreach (i, e ; value) + archive.archive(e, toDataType(i)); + }); + } + + private void serializeAssociativeArray (T) (T value, DataType key) + { + archive.archive(value, key, { + foreach(k, v ; value) + archive.archive(v, toDataType(k)); + }); + } + + private void serializePointer (T) (T value, DataType key) + { + archive.archive(value, key, { + if (key in serializers) + { + auto wrapper = getSerializerWrapper!(T)(key); + wrapper(value, this, key); + } + + else static if (isSerializable!(T, ArchiveType)) + value.toData(this, key); + + else + { + static if (isVoid!(BaseTypeOfPointer!(T))) + 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 + serialize(*value, key); + } + }); + } + + 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, { + serialize!(BaseTypeOfTypeDef!(T))(value, key); + }); + } + + T deserialize (T) (DataType data, DataType key = null) + { + if (hasBegunSerializing && !hasBegunDeserializing) + resetCounters(); + + if (!hasBegunDeserializing) + hasBegunDeserializing = true; + + if (!key) + key = nextKey; + + archive.beginUnarchiving(data); + return deserializeInternal!(T)(key); + } + + private T deserializeInternal (T) (DataType key) + { + static if (isTypeDef!(T)) + return deserializeTypeDef!(T)(key); + + else static if (isObject!(T)) + return deserializeObject!(T)(key); + + else static if (isStruct!(T)) + return deserializeStruct!(T)(key); + + else static if (isString!(T)) + return deserializeString!(T)(key); + + else static if (isArray!(T)) + return deserializeArray!(T)(key); + + else static if (isAssociativeArray!(T)) + return deserializeAssociativeArray!(T)(key); + + else static if (isPrimitive!(T)) + return deserializePrimitive!(T)(key); + + else static if (isPointer!(T)) + return deserializePointer!(T)(key); + + else static if (isEnum!(T)) + return deserializeEnum!(T)(key); + + else + static assert(false, format!(`The type "`, T, `" cannot be deserialized.`)); + } + + private T deserializeObject (T) (DataType key) + { + T value = archive.unarchive!(T)(key, (T value) { + auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); + + triggerEvents(deserializing, value, { + auto runtimeType = value.classinfo.name; + + if (runtimeType in deserializers) + { + auto wrapper = getDeserializerWrapper!(T)(runtimeType); + wrapper(value, this, key); + } + + else static if (isSerializable!(T, ArchiveType)) + value.fromData(this, key); + + 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, nonSerializedFields); + } + }); + + return value; + }); + + return value; + } + + private T deserializeStruct (T) (DataType key) + { + return archive.unarchive!(T)(key, (T value) { + auto nonSerializedFields = collectAnnotations!(nonSerializedField)(value); + + triggerEvents(deserializing, value, { + auto type = toDataType(T.stringof); + + if (type in deserializers) + { + auto wrapper = getDeserializerWrapper!(T)(type); + wrapper(value, this, key); + } + + else + { + static if (isSerializable!(T, ArchiveType)) + value.fromData(this, key); + + else + objectStructDeserializeHelper(value, nonSerializedFields); + } + }); + + return value; + }); + } + + private T deserializeString (T) (DataType key) + { + return archive.unarchive!(T)(key); + } + + private T deserializeArray (T) (DataType key) + { + return archive.unarchive!(T)(key, (T value) { + foreach (i, ref e ; value) + e = archive.unarchive!(typeof(e))(toDataType(i)); + + return value; + }); + } + + 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 (key in deserializers) + { + auto wrapper = getDeserializerWrapper!(T)(key); + wrapper(value, this, key); + } + + else static if (isSerializable!(T, ArchiveType)) + value.fromData(this, key); + + else + { + static if (isVoid!(BaseTypeOfPointer!(T))) + 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); + } + + return value; + }); + } + + private T deserializeEnum (T) (DataType key) + { + return archive.unarchive!(T)(key); + } + + private T deserializePrimitive (T) (DataType key) + { + return archive.unarchive!(T)(key); + } + + private T deserializeTypeDef (T) (DataType key) + { + return archive.unarchive!(T)(key, (T value) { + return deserializeInternal!(BaseTypeOfTypeDef!(T))(key); + }); + } + + private void objectStructSerializeHelper (T) (T value, string[] nonSerializedFields) + { + foreach (i, dummy ; typeof(T.tupleof)) + { + const field = nameOfFieldAt!(T, i); + + if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) + { + alias typeof(T.tupleof[i]) Type; + + Type v = value.tupleof[i]; + + //Type v = getValueOfField!(T, Type, field)(value); + serialize(v, toDataType(field)); + } + } + + static if (is(T : Object) && !is(T == Object)) + serializeBaseTypes(value, nonSerializedFields); + } + + private void objectStructDeserializeHelper (T) (T value, string[] nonSerializedFields) + { + foreach (i, dummy ; typeof(T.tupleof)) + { + const field = nameOfFieldAt!(T, i); + + if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) + { + alias TypeOfField!(T, field) Type; + auto fieldValue = deserializeInternal!(Type)(toDataType(field)); + setValueOfField!(T, Type, field)(value, fieldValue); + } + } + + static if (is(T : Object) && !is(T == Object)) + deserializeBaseTypes(value, nonSerializedFields); + } + + private void serializeBaseTypes (T : Object) (T value, string[] nonSerializedFields) + { + alias BaseTypeTupleOf!(T)[0] Base; + + static if (!is(Base == Object)) + { + archive.archiveBaseClass!(Base)(nextKey); + objectStructSerializeHelper!(Base)(value, nonSerializedFields); + } + } + + private void deserializeBaseTypes (T : Object) (T value, string[] nonSerializedFields) + { + alias BaseTypeTupleOf!(T)[0] Base; + + static if (!is(Base == Object)) + { + archive.unarchiveBaseClass!(Base)(nextKey); + objectStructDeserializeHelper!(Base)(value, nonSerializedFields); + } + } + + private SerializeRegisterWrapper!(T, ArchiveType) getSerializerWrapper (T) (string type) + { + auto wrapper = cast(SerializeRegisterWrapper!(T, ArchiveType)) serializers[type]; + + if (wrapper) + return wrapper; + + assert(false, "throw exception here"); + } + + private DeserializeRegisterWrapper!(T, ArchiveType) getDeserializerWrapper (T) (string type) + { + auto wrapper = cast(DeserializeRegisterWrapper!(T, ArchiveType)) deserializers[type]; + + if (wrapper) + return wrapper; + + assert(false, "throw exception here"); + } + + private SerializeRegisterWrapper!(T, ArchiveType) toSerializeRegisterWrapper (T) (void delegate (T, Serializer, DataType) dg) + { + return new SerializeRegisterWrapper!(T, ArchiveType)(dg); + } + + private SerializeRegisterWrapper!(T, ArchiveType) toSerializeRegisterWrapper (T) (void function (T, Serializer, DataType) func) + { + return new SerializeRegisterWrapper!(T, ArchiveType)(func); + } + + private DeserializeRegisterWrapper!(T, ArchiveType) toDeserializeRegisterWrapper (T) (void delegate (ref T, Serializer, DataType) dg) + { + return new DeserializeRegisterWrapper!(T, ArchiveType)(dg); + } + + private DeserializeRegisterWrapper!(T, ArchiveType) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, DataType) func) + { + return new DeserializeRegisterWrapper!(T, ArchiveType)(func); + } + + private DataType toDataType (T) (T value) + { + try + return to!(DataType)(value); + + catch (ConversionException e) + throw new SerializationException(e); + } + + private bool isBaseClass (T) (T value) + { + auto name = value.classinfo.name; + auto index = name.lastIndexOf('.'); + + return T.stringof != name[index + 1 .. $]; + } + + private DataType nextKey () + { + return toDataType(keyCounter++); + } + + private void resetCounters () + { + keyCounter = 0; + } + + private void triggerEvent (string name, T) (T value) + { + static assert (is(T == class) || is(T == struct), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are classes and structs.`)); + + foreach (i, dummy ; typeof(T.tupleof)) + { + const field = nameOfFieldAt!(T, i); + + static if (field == name) + { + alias TypeOfField!(T, field) Type; + auto event = getValueOfField!(T, Type, field)(value); + event(value); + } + } + } + + private void triggerEvents (T) (Mode mode, T value, void delegate () dg) + { + if (mode == serializing) + triggerEvent!(onSerializingField)(value); + + else + triggerEvent!(onDeserializingField)(value); + + dg(); + + if (mode == serializing) + triggerEvent!(onSerializedField)(value); + + else + triggerEvent!(onDeserializedField)(value); + } + + private string[] collectAnnotations (string name, T) (T value) + { + static assert (is(T == class) || is(T == struct), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are classes and structs.`)); + + string[] annotations; + + foreach (i, dummy ; typeof(T.tupleof)) + { + const field = nameOfFieldAt!(T, i); + + static if (field == name) + { + alias TypeOfField!(T, field) Type; + auto f = getValueOfField!(T, Type, field)(value); + annotations ~= f.field; + } + } + + return annotations; + } +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/_.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/_.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,15 @@ +/** + * 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.serialization._; + +public: + +import orange.serialization.Events; +import orange.serialization.RegisterWrapper; +import orange.serialization.Serializable; +import orange.serialization.SerializationException; +import orange.serialization.Serializer; \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/archives/Archive.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/archives/Archive.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,46 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Feb 6, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.serialization.archives.Archive; + +version (Tango) + import tango.util.Convert; + +import orange.serialization.archives.ArchiveException; + +interface IArchive +{ + void beginArchiving (); + void reset (); +} + +abstract class Archive (U) : IArchive +{ + alias U[] DataType; + + abstract void beginArchiving (); + abstract void beginUnarchiving (DataType data); + abstract DataType data (); + abstract void reset (); + + protected DataType toDataType (T) (T value) + { + try + return to!(DataType)(value); + + catch (ConversionException e) + throw new ArchiveException(e); + } + + protected T fromDataType (T) (DataType value) + { + try + return to!(T)(value); + + catch (ConversionException e) + throw new ArchiveException(e); + } +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/archives/ArchiveException.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/archives/ArchiveException.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,28 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Jan 30, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.serialization.archives.ArchiveException; + +import orange.serialization.SerializationException; +import orange.util.string; + +class ArchiveException : SerializationException +{ + this (string message) + { + super(message); + } + + this (string message, string file, long line) + { + super(message, file, line); + } + + this (Exception exception) + { + super(exception); + } +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/archives/XMLArchive.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/archives/XMLArchive.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,596 @@ +/** + * 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.serialization.archives.XMLArchive; + +version (Tango) +{ + import tango.text.xml.DocPrinter; + import tango.text.xml.Document; + import tango.util.Convert : to; +} + +import orange.serialization.archives._; +import orange.util._; + +private enum ArchiveMode +{ + 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 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"; + } + + 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"; + } + + private + { + DataType archiveType = "org.dsource.orange.xml"; + DataType archiveVersion = "0.1"; + + Document!(U) doc; + doc.Node lastElement; + DocPrinter!(U) printer; + doc.Node lastElementSaved; + + bool hasBegunArchiving; + bool hasBegunUnarchiving; + + DataType[void*] archivedReferences; + void*[DataType] unarchivedReferences; + + size_t idCounter; + } + + this () + { + doc = new Document!(U); + } + + public void beginArchiving () + { + if (!hasBegunArchiving) + { + doc.header; + lastElement = doc.tree.element(null, Tags.archiveTag) + .attribute(null, Attributes.typeAttribute, archiveType) + .attribute(null, Attributes.versionAttribute, archiveVersion); + lastElement = lastElement.element(null, Tags.dataTag); + + hasBegunArchiving = true; + } + } + + public void beginUnarchiving (DataType data) + { + if (!hasBegunUnarchiving) + { + doc.parse(data); + hasBegunUnarchiving = true; + + auto set = doc.query[Tags.archiveTag][Tags.dataTag]; + + if (set.nodes.length == 1) + lastElement = set.nodes[0]; + + else if (set.nodes.length == 0) + throw new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `The "` ~ to!(string)(Tags.dataTag) ~ `" tag could not be found.`, __FILE__, __LINE__); + + else + throw new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, __FILE__, __LINE__); + } + } + + public DataType data () + { + if (!printer) + printer = new DocPrinter!(U); + + return printer.print(doc); + } + + public void reset () + { + hasBegunArchiving = false; + hasBegunUnarchiving = false; + idCounter = 0; + doc.reset; + } + + private void begin () + { + lastElementSaved = lastElement; + } + + private void end () + { + 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(); + }; + } + + private void archiveObject (T) (T value, DataType key, ref bool callDelegate) + { + if (!value) + { + lastElement.element(null, Tags.nullTag) + .attribute(null, Attributes.typeAttribute, toDataType(T.stringof)) + .attribute(null, Attributes.keyAttribute, key); + callDelegate = false; + } + + else if (auto reference = getArchivedReference(value)) + { + archiveReference(key, reference); + callDelegate = false; + } + + else + { + DataType id = nextId; + + lastElement = lastElement.element(null, Tags.objectTag) + .attribute(null, Attributes.runtimeTypeAttribute, toDataType(value.classinfo.name)) + .attribute(null, Attributes.typeAttribute, toDataType(T.stringof)) + .attribute(null, Attributes.keyAttribute, key) + .attribute(null, Attributes.idAttribute, id); + + addArchivedReference(value, id); + } + } + + private void archiveStruct (T) (T value, DataType key) + { + lastElement = lastElement.element(null, Tags.structTag) + .attribute(null, Attributes.typeAttribute, toDataType(T.stringof)) + .attribute(null, Attributes.keyAttribute, key); + } + + private void archiveString (T) (T value, DataType key) + { + lastElement.element(null, Tags.stringTag, toDataType(value)) + .attribute(null, Attributes.typeAttribute, toDataType(BaseTypeOfArray!(T).stringof)) + .attribute(null, Attributes.keyAttribute, key); + } + + private void archiveArray (T) (T value, DataType key) + { + lastElement = lastElement.element(null, Tags.arrayTag) + .attribute(null, Attributes.typeAttribute, toDataType(BaseTypeOfArray!(T).stringof)) + .attribute(null, Attributes.lengthAttribute, toDataType(value.length)) + .attribute(null, Attributes.keyAttribute, key); + } + + private void archiveAssociativeArray (T) (T value, DataType key) + { + lastElement = lastElement.element(null, Tags.associativeArrayTag) + .attribute(null, Attributes.keyTypeAttribute, toDataType(KeyTypeOfAssociativeArray!(T).stringof)) + .attribute(null, Attributes.valueTypeAttribute, toDataType(ValueTypeOfAssociativeArray!(T).stringof)) + .attribute(null, Attributes.lengthAttribute, toDataType(value.length)) + .attribute(null, Attributes.keyAttribute, key); + } + + private void archivePointer (T) (T value, DataType key, ref bool callDelegate) + { + if (auto reference = getArchivedReference(value)) + { + archiveReference(key, reference); + callDelegate = false; + } + + else + { + DataType id = nextId; + + lastElement = lastElement.element(null, Tags.pointerTag) + .attribute(null, Attributes.keyAttribute, key) + .attribute(null, Attributes.idAttribute, id); + + addArchivedReference(value, id); + } + } + + private void archiveEnum (T) (T value, DataType key) + { + lastElement.element(null, Tags.enumTag, toDataType(value)) + .attribute(null, Attributes.typeAttribute, toDataType(T.stringof)) + .attribute(null, Attributes.keyAttribute, key); + } + + private void archivePrimitive (T) (T value, DataType key) + { + lastElement.element(null, toDataType(T.stringof), toDataType(value)) + .attribute(null, Attributes.keyAttribute, key); + } + + private void archiveTypeDef (T) (T value, DataType key) + { + lastElement = lastElement.element(null, Tags.typedefTag) + .attribute(null, Attributes.typeAttribute, toDataType(BaseTypeOfTypeDef!(T).stringof)); + .attribute(null, Attributes.key, key); + } + + public T unarchive (T) (DataType key, T delegate (T) dg = null) + { + 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); + + 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); + + else static if (isAssociativeArray!(T)) + value = unarchiveAssociativeArray!(T)(key); + + else static if (isPrimitive!(T)) + value = unarchivePrimitive!(T)(key); + + 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.`)); + + if (callDelegate && dg) + return dg(value); + + return value; + }; + } + + 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); + + if (!tmp) + { + lastElement = getElement(Tags.nullTag, key); + callDelegate = false; + return null; + } + + lastElement = tmp; + + auto runtimeType = getValueOfAttribute(Attributes.runtimeTypeAttribute); + auto name = fromDataType!(string)(runtimeType); + id = getValueOfAttribute(Attributes.idAttribute); + + T result; + + static if (is(typeof(T._ctor))) + { + ParameterTupleOf!(typeof(T._ctor)) params; + result = factory!(T, typeof(params))(name, params); + } + + else + result = factory!(T)(name); + + addUnarchivedReference(result, id); + + return result; + } + + private T unarchiveStruct (T) (DataType key) + { + lastElement = getElement(Tags.structTag, key); + + return T.init; + } + + private T unarchiveString (T) (DataType key) + { + return fromDataType!(T)(getElement(Tags.stringTag, key).value); + } + + private T unarchiveArray (T) (DataType key) + { + T value; + + lastElement = getElement(Tags.arrayTag, key); + auto length = getValueOfAttribute(Attributes.lengthAttribute); + value.length = fromDataType!(size_t)(length); + + return value; + } + + private T unarchiveAssociativeArray (T) (DataType key) + { + lastElement = getElement(Tags.associativeArrayTag, key); + + return T.init; + } + + private T unarchivePointer (T) (DataType key, ref bool callDelegate) + { + DataType id = unarchiveReference(key); + + if (auto reference = getUnarchivedReference!(T)(id)) + { + callDelegate = false; + return *reference; + } + + lastElement = getElement(Tags.pointerTag, key); + id = getValueOfAttribute(Attributes.idAttribute); + + T result = new BaseTypeOfPointer!(T); + + addUnarchivedReference(result, id); + + return result; + } + + private T unarchiveEnum (T) (DataType key) + { + return fromDataType!(T)(getElement(Tags.enumTag, key).value); + } + + private T unarchivePrimitive (T) (DataType key) + { + return fromDataType!(T)(getElement(toDataType(T.stringof), key).value); + } + + private T unarchiveTypeDef (T) (DataType key) + { + lastElement = getElement(Tags.typedefTag, key); + + return T.init; + } + + public AssociativeArrayVisitor!(KeyTypeOfAssociativeArray!(T), ValueTypeOfAssociativeArray!(T)) unarchiveAssociativeArrayVisitor (T) () + { + return AssociativeArrayVisitor!(KeyTypeOfAssociativeArray!(T), ValueTypeOfAssociativeArray!(T))(this); + } + + public void archiveBaseClass (T : Object) (DataType key) + { + lastElement = lastElement.element(null, Tags.baseTag) + .attribute(null, Attributes.typeAttribute, toDataType(T.stringof)) + .attribute(null, Attributes.keyAttribute, key); + } + + public void unarchiveBaseClass (T : Object) (DataType key) + { + lastElement = getElement(Tags.baseTag, 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: "; + } + + private doc.Node getElement (DataType tag, DataType key, DataType attribute = Attributes.keyAttribute, bool throwOnError = true) + { + auto set = lastElement.query[tag].attribute((doc.Node node) { + if (node.name == attribute && node.value == key) + return true; + + return false; + }); + + if (set.nodes.length == 1) + return set.nodes[0].parent; + + else + { + if (throwOnError) + { + if (set.nodes.length == 0) + throw new ArchiveException(`Could not find an element "` ~ to!(string)(tag) ~ `" with the attribute "` ~ to!(string)(Attributes.keyAttribute) ~ `" with the value "` ~ to!(string)(key) ~ `".`, __FILE__, __LINE__); + + else + throw new ArchiveException(`Could not unarchive the value with the key "` ~ to!(string)(key) ~ `" due to malformed data.`, __FILE__, __LINE__); + } + + return null; + } + } + + private DataType getValueOfAttribute (DataType attribute) + { + auto set = lastElement.query.attribute(attribute); + + if (set.nodes.length == 1) + return set.nodes[0].value; + + else if (set.nodes.length == 0) + throw new ArchiveException(`Could not find the attribute "` ~ to!(string)(attribute) ~ `".`, __FILE__, __LINE__); + + else + throw new ArchiveException(`Could not unarchive the value of the attribute "` ~ to!(string)(attribute) ~ `" due to malformed data.`, __FILE__, __LINE__); + } + + 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 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 DataType nextId () + { + return toDataType(idCounter++); + } + + private void archiveReference (DataType key, DataType id) + { + lastElement.element(null, Tags.referenceTag, id) + .attribute(null, Attributes.keyAttribute, key); + } + + private DataType unarchiveReference (DataType key) + { + auto element = getElement(Tags.referenceTag, key, Attributes.keyAttribute, false); + + if (element) + return element.value; + + return cast(DataType) null; + } + + 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); + } + }; + + if (result) + break; + } + + return result; + } + } +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/serialization/archives/_.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/serialization/archives/_.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,13 @@ +/** + * 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.serialization.archives._; + +public: + +import orange.serialization.archives.Archive; +import orange.serialization.archives.ArchiveException; +import orange.serialization.archives.XMLArchive; \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/util/CTFE.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/CTFE.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,9 @@ +/** + * 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.util.CTFE; + +public import mambo.util.CTFE; \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/util/Reflection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/Reflection.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,9 @@ +/** + * 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.util.Reflection; + +public import mambo.util.Reflection; \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/util/Traits.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/Traits.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,46 @@ +/** + * 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.util.Traits; + +public import mambo.util.Traits; + +import orange.serialization.Serializable; +import orange.serialization.archives.Archive; +import orange.util._; + +template isArchive (T) +{ + const isArchive = is(typeof({ + alias T.DataType Foo; + })) && + + is(typeof(T.archive(0, TypeOfDataType!(T).init, {}))) && + is(typeof(T.unarchive!(int))) && + is(typeof(T.beginArchiving)) && + is(typeof(T.beginUnarchiving(TypeOfDataType!(T).init))) && + is(typeof(T.archiveBaseClass!(Object))) && + is(typeof(T.unarchiveBaseClass!(Object))) && + is(typeof(T.reset)) && + is(typeof({TypeOfDataType!(T) a = T.data;})) && + is(typeof(T.unarchiveAssociativeArrayVisitor!(int[string]))); + +} + +template isSerializable (T, ArchiveType) +{ + const isSerializable = is(typeof(T.toData(ArchiveType.init, ArchiveType.DataType.init))) && is(typeof(T.fromData(ArchiveType.init, ArchiveType.DataType.init))); +} + +template isISerializable (T) +{ + const isISerializable = is(T : ISerializable); +} + +template TypeOfDataType (T) +{ + alias T.DataType TypeOfDataType; +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/util/Use.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/Use.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,90 @@ +/** + * Copyright: Copyright (c) 2010 Jacob Carlborg. + * Authors: Jacob Carlborg + * Version: Initial created: Jan 29, 2010 + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) + */ +module orange.util.Use; + +import mambo.io; + +version (Tango) +{ + import tango.core.Tuple; + import tango.core.Traits; +} + +else +{ + import std.typetuple; + import std.traits; + + alias ReturnType ReturnTypeOf; + alias ParameterTypeTuple ParameterTupleOf; +} + +struct OpInStruct (ARGS...) +{ + static assert (ARGS.length > 0); + + private + { + alias ReturnTypeOf!(ARGS[0]) ReturnType; + + static if (ARGS.length >= 2) + alias Tuple!(ReturnType delegate (ARGS), ARGS[1 .. $]) NEW_ARGS; + + else + alias Tuple!(ReturnType delegate (ARGS)) NEW_ARGS; + } + + NEW_ARGS args; + + ReturnType opIn (ARGS[0] dg) + { + assert(args[0]); + + static if (NEW_ARGS.length == 1) + return args[0](dg); + + else + return args[0](dg, args[1 .. $]); + } +} + +struct RestoreStruct (U, T) +{ + U delegate(U delegate (), ref T) dg; + T* value; + + U opIn (U delegate () deleg) + { + return dg(deleg, *value); + } +} +RestoreStruct!(U, T) restore (U = void, T) (ref T val) +{ + RestoreStruct!(U, T) restoreStruct; + + restoreStruct.dg = (U delegate () dg, ref T value){ + T t = value; + + static if (is(U == void)) + { + dg(); + value = t; + } + + else + { + auto result = dg(); + value = t; + + return result; + } + }; + + restoreStruct.value = &val; + + return restoreStruct; +} \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/util/_.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/_.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,16 @@ +/** + * 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.util._; + +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 000000000000 -r f7b078e85f7f orange/util/io.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/io.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,3 @@ +module orange.util.io; + +public import mambo.io; \ No newline at end of file diff -r 000000000000 -r f7b078e85f7f orange/util/string.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orange/util/string.d Wed May 26 17:19:13 2010 +0200 @@ -0,0 +1,9 @@ +/** + * 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.util.string; + +public import mambo.string; \ No newline at end of file