view orange/serialization/archives/XMLArchive.d @ 46:d6fbd0b3586e

Issue 8: Wrong results for array of arrays. (Temporary fix).
author Jacob Carlborg <doob@me.com>
date Tue, 09 Aug 2011 11:38:50 +0200
parents 3cd22957e411
children 9c9bbef6bf5e
line wrap: on
line source

/**
 * 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.util.Convert : to;

else
	import std.conv;

import orange.core._;
import orange.serialization.archives._;
import orange.serialization.Serializer;
import orange.util._;
import orange.xml.XMLDocument;

final class XMLArchive (U = char) : Base!(U)
{
	private alias Archive.Id Id;
	
	private struct Tags
	{
		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 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
	{
		Data archiveType = "org.dsource.orange.xml";
		Data archiveVersion = "1.0.0";
		
		XMLDocument!(U) doc;
		doc.Node lastElement;
		
		bool hasBegunArchiving;
		bool hasBegunUnarchiving;
		
		Node[Id] archivedArrays;
		Node[Id] archivedPointers;
		void[][Data] unarchivedSlices;
	}
	
	this (ErrorCallback errorCallback = null)
	{
		super(errorCallback);
		doc = new XMLDocument!(U);
	}
	
	public void beginArchiving ()
	{
		if (!hasBegunArchiving)
		{
			doc.header;
			lastElement = doc.tree.element(Tags.archiveTag)
				.attribute(Attributes.typeAttribute, archiveType)
				.attribute(Attributes.versionAttribute, archiveVersion);
			lastElement = lastElement.element(Tags.dataTag);
			
			hasBegunArchiving = true;
		}		
	}
	
	public void beginUnarchiving (UntypedData untypedData)
	{
		auto data = cast(Data) untypedData;
		
		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 (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__), [dataTag]);
					
					else
						errorCallback(new ArchiveException(errorMessage!(ArchiveMode.unarchiving) ~ `There were more than one "` ~ to!(string)(Tags.dataTag) ~ `" tag.`, __FILE__, __LINE__), [dataTag]);
				}	
			}
		}
	}
	
	UntypedData untypedData ()
	{
		return doc.toString();
	}
	
	Data data ()
	{
		return doc.toString;
	}
	
	void reset ()
	{
		hasBegunArchiving = false;
		hasBegunUnarchiving = false;
		doc.reset;
	}
	
	void archiveArray (Array array, string type, string key, Id id, void delegate () dg)
	{
		restore(lastElement) in {
			internalArchiveArray(array, type, key, id, Tags.arrayTag);
			dg();
		};
	}
	
	private void internalArchiveArray(Array array, string type, string key, Id id, Data tag, Data content = null)
	{
		auto parent = lastElement;
		
		if (array.length == 0)
			lastElement = lastElement.element(tag);
		
		else
			lastElement = doc.createNode(tag, content);			
		
		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);
	}

	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)
	{
		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);
	}

	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)
	{
		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)
	{
		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))
		{
			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));
		}
	}
	
	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))
		{
			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));
			}
		}
	}
	
	void archiveStruct (string type, string key, Id id, void delegate () dg)
	{
		restore(lastElement) in {
			lastElement = lastElement.element(Tags.structTag)
			.attribute(Attributes.typeAttribute, toData(type))
			.attribute(Attributes.keyAttribute, toData(key))
			.attribute(Attributes.idAttribute, toData(id));
			
			dg();
		};
	}
	
	void archiveTypedef (string type, string key, Id id, void delegate () dg)
	{
		restore(lastElement) in {
			lastElement = lastElement.element(Tags.typedefTag)
			.attribute(Attributes.typeAttribute, toData(type))
			.attribute(Attributes.keyAttribute, toData(key))
			.attribute(Attributes.idAttribute, toData(id));
			
			dg();
		};
	}
	
	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)
	{
		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));
		};
	}
	
	void archive (bool value, string key, Id id)
	{
		archivePrimitive(value, key, id);
	}

	void archive (byte value, string key, Id id)
	{
		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);
	}*/

	//currently not suppported by to!()
	/*void archive (cfloat value, string key, Id id)
	{
		archivePrimitive(value, key, id);
	}*/

	void archive (char value, string key, Id id)
	{
		archivePrimitive(value, key, id);
	}

	//currently not suppported by to!()
	/*void archive (creal value, string key, Id id)
	{
		archivePrimitive(value, key, id);
	}*/

	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);
	}

	//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);
	}

	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);
	}

	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)
	{
		restore(lastElement) in {			
			auto element = getElement(Tags.arrayTag, to!(string)(id), Attributes.idAttribute);
			
			if (!element.isValid)
				return;
	
			lastElement = element;
			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);
		};
	}
	
	void unarchiveAssociativeArrayKey (string key, void delegate () dg)
	{
		internalUnarchiveAAKeyValue(key, Tags.keyTag, dg);
	}
	
	void unarchiveAssociativeArrayValue (string key, void delegate () dg)
	{
		internalUnarchiveAAKeyValue(key, Tags.valueTag, dg);
	}
	
	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();
		};
	}
	
	bool unarchiveEnumBool (string key)
	{
		return unarchiveEnum!(bool)(key);
	}
	
	byte unarchiveEnumByte (string key)
	{
		return unarchiveEnum!(byte)(key);
	}
	
	char unarchiveEnumChar (string key)
	{
		return unarchiveEnum!(char)(key);
	}
	
	dchar unarchiveEnumDchar (string key)
	{
		return unarchiveEnum!(dchar)(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)
	{
		return unarchiveEnum!(wchar)(key);
	}	
	
	private T unarchiveEnum (T) (string key)
	{
		auto element = getElement(Tags.enumTag, key);
		
		if (!element.isValid)
			return T.init;
		
		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);

			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);
		
		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 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)
	{
		auto element = getElement(Tags.typedefTag, key);
		
		if (element.isValid)
			lastElement = element;
		
		return T.init;
	}
	
	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 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);
	}
	
	dstring unarchiveDstring (Id id)
	{
		return internalUnarchiveString!(dstring)(id);
	}
	
	private T internalUnarchiveString (T) (Id id)
	{
		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);
	}
	
	//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)
	{
		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);
	}
	
	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)
	{
		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);
	}
	
	ushort unarchiveUshort (string key)
	{
		return unarchivePrimitive!(ushort)(key);
	}
	
	wchar unarchiveWchar (string key)
	{
		return unarchivePrimitive!(wchar)(key);
	}
	
	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);
	}
	
	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)
				return true;
			
			return false;
		});

		version (Tango)
		{
			if (set.nodes.length == 1)
				return set.nodes[0].parent;
		}
		
		else
		{	// Temporary fix, this is probably a problem in the Phobos
			// implementation of the XML query function
			if (set.nodes.length > 0)
				return set.nodes[set.nodes.length - 1].parent;
		}

		if (throwOnError && errorCallback)
		{
			if (set.nodes.length == 0)
				errorCallback(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__), [tag, Attributes.keyAttribute, key]);

			else
				errorCallback(new ArchiveException(`Could not unarchive the value with the key "` ~ to!(string)(key) ~ `" due to malformed data.`, __FILE__, __LINE__), [tag, Attributes.keyAttribute, key]);
		}

		return doc.Node.invalid;
	}
	
	private Data getValueOfAttribute (Data attribute, doc.Node element = doc.Node.invalid)
	{
		if (!element.isValid)
			element = lastElement;
		
		auto set = element.query.attribute(attribute);
		
		if (set.nodes.length == 1)
			return set.nodes[0].value;
		
		else
		{
			if (errorCallback)
			{
				if (set.nodes.length == 0)
					errorCallback(new ArchiveException(`Could not find the attribute "` ~ to!(string)(attribute) ~ `".`, __FILE__, __LINE__), [attribute]);
				
				else
					errorCallback(new ArchiveException(`Could not unarchive the value of the attribute "` ~ to!(string)(attribute) ~ `" due to malformed data.`, __FILE__, __LINE__), [attribute]);
			}
		}

		return null;
	}
	
	version (Tango)
	{
		private 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: ";
		}
	}
	
	else
	{
		mixin(
			`private 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: ";
			}`
		);
	}
}