#region Using Statements
using ANX.Framework.Content.Pipeline.Helpers;
using ANX.Framework.Content.Pipeline.Serialization.Intermediate.SystemTypeSerializers;
using ANX.Framework.NonXNA.Development;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
#endregion
// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license
namespace ANX.Framework.Content.Pipeline.Serialization.Intermediate
{
///
/// Provides methods for reading and writing intermediate XML format.
///
[Developer("KorsarNek")]
[TestState(TestStateAttribute.TestState.Tested)]
[PercentageComplete(100)]
public class IntermediateSerializer
{
private Dictionary genericSerializerTypeHandler = new Dictionary();
private Dictionary serializerInstances = new Dictionary();
private XmlTypeNameContainer xmlTypeNameContainer = new XmlTypeNameContainer();
private string contentTagName = "AnxContent";
//Used for the option to rename the anx namespaces to xna.
private const string XnaFrameworkNamespace = "Microsoft.Xna.Framework";
private const string AnxFrameworkNamespace = "ANX.Framework";
private static readonly ContentSerializerAttribute assetAttribute = new ContentSerializerAttribute() { ElementName = "Asset" };
private static IntermediateSerializer singleton;
internal static IntermediateSerializer Singleton
{
get
{
if (singleton == null)
singleton = new IntermediateSerializer(true, false);
return singleton;
}
}
///
/// Creates a new intermediate serialzier with the option to use xna namespaces and search s.
///
///
///
/// Will be thrown if s are loaded that conform to the requirements.
/// Will be thrown if no parameterless constructor for a can be found.
#if XNAEXT
public IntermediateSerializer(bool searchSerializers = true, bool changeToXnaNamespaces = false)
#else
internal IntermediateSerializer(bool searchSerializers = true, bool changeToXnaNamespaces = false)
#endif
{
if (searchSerializers)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (!AssemblyHelper.IsValidForPipeline(assembly.GetName()))
continue;
this.AddContentTypeSerializerAssembly(assembly);
}
}
if (changeToXnaNamespaces)
{
this.ChangeToXnaNamespaces();
}
}
///
/// The name of the main element around the <Assset> element.
/// If the value is empty or null, no element is used.
///
public string ContentElementName
{
get { return contentTagName; }
set { contentTagName = value; }
}
private void AddContentTypeSerializerAssembly(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
ContentTypeSerializerAttribute[] value = (ContentTypeSerializerAttribute[])type.GetCustomAttributes(typeof(ContentTypeSerializerAttribute), true);
if (value.Length > 0)
{
if (!typeof(ContentTypeSerializer).IsAssignableFrom(type))
continue;
AddTypeSerializer(type);
}
}
}
private static Type ValidateGenericSerializer(Type type)
{
//The only instance when we can know the type of T is when T is also used as the Parameter for the generic ContentTypeSerializer.
Type baseType = type.BaseType;
while (!baseType.IsGenericType || baseType.GetGenericTypeDefinition() != typeof(ContentTypeSerializer<>))
{
baseType = baseType.BaseType;
if (baseType == null)
{
throw new ArgumentException(string.Format("Intermediate {0} {1} is an invalid generic. It should be in the form My{0} : {0}> .", typeof(ContentTypeSerializer<>).Name, type));
}
}
//The generic argument must be itself generic, that way we can distinguish which of the generic handlers to use.
//MySerializer : ContentTypeSerializer> When ever we meet a version of MyGeneric, we can find the responsible serializer.
Type genericArgument = baseType.GetGenericArguments()[0];
if (!genericArgument.IsGenericType)
{
throw new ArgumentException(string.Format("Intermediate {0} {1} is an invalid generic. It should be in the form My{0} : {0}> .", typeof(ContentTypeSerializer<>).Name, type));
}
if (genericArgument.IsByRef || genericArgument.IsPointer)
{
throw new ArgumentException(string.Format("Cannot serialize type {0}. Pointers and references are not supported.", type));
}
if (genericArgument.IsSubclassOf(typeof(Delegate)))
{
throw new ArgumentException(string.Format("Cannot serialize type {0}. Delegates are not supported.", type));
}
if (!type.GetGenericArguments().SequenceEqual(genericArgument.GetGenericArguments()))
{
throw new ArgumentException(string.Format("Intermediate {0} {1} is an invalid generic. It should be in the form My{0} : {0}> .", typeof(ContentTypeSerializer<>).Name, type));
}
return genericArgument;
}
///
/// Adds a to this intermediate serializer instance.
///
///
///
///
///
/// Will be thrown if no parameterless constructor can be found.
public void AddTypeSerializer(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsByRef || type.IsPointer)
throw new ArgumentException(string.Format("Intermediate serializer type {0}. Pointers and references are not supported.", type));
if (!typeof(ContentTypeSerializer).IsAssignableFrom(type))
throw new ArgumentException(string.Format("\"{0}\" must inherit from {1}.", type.FullName, typeof(ContentTypeSerializer).Name));
//If it contains open generic parameters like MySerializer. We can't create the instance until we know T.
if (type.IsGenericTypeDefinition)
{
Type genericArgument = ValidateGenericSerializer(type);
Type genericTypeDefinition = genericArgument.GetGenericTypeDefinition();
if (genericSerializerTypeHandler.ContainsKey(genericTypeDefinition))
{
throw new InvalidOperationException(string.Format("Intermediate {0} \"{1}\" conflicts with existing handler \"{2}\" for {3}.",
typeof(ContentTypeSerializer<>).Name,
type.AssemblyQualifiedName,
genericSerializerTypeHandler[genericTypeDefinition].AssemblyQualifiedName,
genericTypeDefinition));
}
genericSerializerTypeHandler.Add(genericTypeDefinition, type);
}
else
{
try
{
AddTypeSerializer((ContentTypeSerializer)Activator.CreateInstance(type));
}
catch (MissingMethodException exc)
{
throw new ArgumentException(string.Format("\"{0}\" doesn't have a public parameterless constructor.", type.AssemblyQualifiedName), exc);
}
}
}
private void AddTypeSerializer(ContentTypeSerializer serializer)
{
if (serializerInstances.ContainsKey(serializer.TargetType))
throw new InvalidOperationException(string.Format("Intermediate {0} \"{1}\" conflicts with existing handler \"{2}\" for {3}.", typeof(ContentTypeSerializer).Name, serializer.GetType().AssemblyQualifiedName, this.serializerInstances[serializer.TargetType].GetType().AssemblyQualifiedName, serializer.TargetType));
if (serializer.XmlTypeName != null)
{
this.xmlTypeNameContainer.AddSerializer(serializer);
}
serializerInstances.Add(serializer.TargetType, serializer);
serializer.Initialize(this);
}
///
/// Serializes an object into an intermediate XML file.
///
///
/// The output XML stream.
/// The object to be serialized.
/// Final name of the output file, used to relative encode external reference filenames.
///
///
/// Will be thrown if a multidimensional array is passed.
/// Will be thrown if no parameterless constructor for the corresponding can be found.
public virtual void SerializeObject(XmlWriter output, T value, string referenceRelocationPath)
{
if (output == null)
throw new ArgumentNullException("output");
if (value == null)
throw new ArgumentNullException("value");
bool expectsContentTag = !string.IsNullOrWhiteSpace(ContentElementName);
IntermediateWriter intermediateWriter = new IntermediateWriter(this, new IntermediateXmlWriter(output), referenceRelocationPath, xmlTypeNameContainer, expectsContentTag);
if (expectsContentTag)
{
output.WriteStartElement(ContentElementName);
}
intermediateWriter.WriteObject