diff orange/serialization/Serializer.d @ 0:f7b078e85f7f

First commit
author Jacob Carlborg <doob@me.com>
date Wed, 26 May 2010 17:19:13 +0200
parents
children ea37a9470e3e
line wrap: on
line diff
--- /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