Added MonoGame Pipeline's IntermediateSerializer and needed classes from the pipeline and MonoGame.Framework.Utilities to make AnimationEditor capable of reading Dictionaries
240 lines
8.8 KiB
C#
240 lines
8.8 KiB
C#
// MonoGame - Copyright (C) The MonoGame Team
|
|
// This file is subject to the terms and conditions defined in
|
|
// file 'LICENSE.txt', which is part of this source code package.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Xml;
|
|
|
|
namespace Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate
|
|
{
|
|
public sealed class IntermediateReader
|
|
{
|
|
private readonly string _filePath;
|
|
|
|
private readonly Dictionary<string, Action<object>> _resourceFixups;
|
|
|
|
private readonly Dictionary<string, List<Action<Type, string>>> _externalReferences;
|
|
|
|
public XmlReader Xml { get; private set; }
|
|
|
|
public IntermediateSerializer Serializer { get; private set; }
|
|
|
|
internal IntermediateReader(IntermediateSerializer serializer, XmlReader xmlReader, string filePath)
|
|
{
|
|
Serializer = serializer;
|
|
Xml = xmlReader;
|
|
_filePath = filePath;
|
|
_resourceFixups = new Dictionary<string, Action<object>>();
|
|
_externalReferences = new Dictionary<string, List<Action<Type, string>>>();
|
|
}
|
|
|
|
public bool MoveToElement(string elementName)
|
|
{
|
|
var nodeType = Xml.MoveToContent();
|
|
return nodeType == XmlNodeType.Element &&
|
|
Xml.Name == elementName;
|
|
}
|
|
|
|
public T ReadObject<T>(ContentSerializerAttribute format)
|
|
{
|
|
return ReadObject(format, Serializer.GetTypeSerializer(typeof(T)), default(T));
|
|
}
|
|
|
|
public T ReadObject<T>(ContentSerializerAttribute format, ContentTypeSerializer typeSerializer)
|
|
{
|
|
return ReadObject(format, typeSerializer, default(T));
|
|
}
|
|
|
|
public T ReadObject<T>(ContentSerializerAttribute format, ContentTypeSerializer typeSerializer, T existingInstance)
|
|
{
|
|
if (!format.FlattenContent)
|
|
{
|
|
if (!MoveToElement(format.ElementName))
|
|
throw NewInvalidContentException(null, "Element '{0}' was not found.", format.ElementName);
|
|
|
|
// Is the object null?
|
|
var isNull = Xml.GetAttribute("Null");
|
|
if (isNull != null && XmlConvert.ToBoolean(isNull))
|
|
{
|
|
if (!format.AllowNull)
|
|
throw NewInvalidContentException(null, "Element '{0}' cannot be null.", format.ElementName);
|
|
|
|
Xml.Skip();
|
|
return default(T);
|
|
}
|
|
|
|
// Is the object overloading the serialized type?
|
|
if (Xml.MoveToAttribute("Type"))
|
|
{
|
|
var type = ReadTypeName();
|
|
if (type == null)
|
|
throw NewInvalidContentException(null, "Could not resolve type '{0}'.", Xml.ReadContentAsString());
|
|
if (!typeSerializer.TargetType.IsAssignableFrom(type))
|
|
throw NewInvalidContentException(null, "Type '{0}' is not assignable to '{1}'.", type.FullName, typeSerializer.TargetType.FullName);
|
|
|
|
typeSerializer = Serializer.GetTypeSerializer(type);
|
|
Xml.MoveToElement();
|
|
}
|
|
}
|
|
|
|
return ReadRawObject(format, typeSerializer, existingInstance);
|
|
}
|
|
|
|
public T ReadObject<T>(ContentSerializerAttribute format, T existingInstance)
|
|
{
|
|
return ReadObject(format, Serializer.GetTypeSerializer(typeof(T)), existingInstance);
|
|
}
|
|
|
|
public T ReadRawObject<T>(ContentSerializerAttribute format)
|
|
{
|
|
return ReadRawObject(format, Serializer.GetTypeSerializer(typeof(T)), default(T));
|
|
}
|
|
|
|
public T ReadRawObject<T>(ContentSerializerAttribute format, ContentTypeSerializer typeSerializer)
|
|
{
|
|
return ReadRawObject(format, typeSerializer, default(T));
|
|
}
|
|
|
|
public T ReadRawObject<T>(ContentSerializerAttribute format, ContentTypeSerializer typeSerializer, T existingInstance)
|
|
{
|
|
if (format.FlattenContent)
|
|
{
|
|
Xml.MoveToContent();
|
|
return (T)typeSerializer.Deserialize(this, format, existingInstance);
|
|
}
|
|
|
|
if (!MoveToElement(format.ElementName))
|
|
throw NewInvalidContentException(null, "Element '{0}' was not found.", format.ElementName);
|
|
|
|
var isEmpty = Xml.IsEmptyElement;
|
|
if (!isEmpty)
|
|
Xml.ReadStartElement();
|
|
|
|
var result = typeSerializer.Deserialize(this, format, existingInstance);
|
|
|
|
if (isEmpty)
|
|
Xml.Skip();
|
|
|
|
if (!isEmpty)
|
|
Xml.ReadEndElement();
|
|
|
|
return (T)result;
|
|
}
|
|
|
|
public T ReadRawObject<T>(ContentSerializerAttribute format, T existingInstance)
|
|
{
|
|
return ReadRawObject(format, Serializer.GetTypeSerializer(typeof(T)), existingInstance);
|
|
}
|
|
|
|
public void ReadSharedResource<T>(ContentSerializerAttribute format, Action<T> fixup)
|
|
{
|
|
string str;
|
|
|
|
if (format.FlattenContent)
|
|
str = Xml.ReadContentAsString();
|
|
else
|
|
{
|
|
if (!MoveToElement(format.ElementName))
|
|
throw NewInvalidContentException(null, "Element '{0}' was not found.", format.ElementName);
|
|
|
|
str = Xml.ReadElementContentAsString();
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(str))
|
|
return;
|
|
|
|
// Do we already have one for this?
|
|
Action<object> prevFixup;
|
|
if (!_resourceFixups.TryGetValue(str, out prevFixup))
|
|
_resourceFixups.Add(str, (o) => fixup((T)o));
|
|
else
|
|
{
|
|
_resourceFixups[str] = (o) =>
|
|
{
|
|
prevFixup(o);
|
|
fixup((T)o);
|
|
};
|
|
}
|
|
}
|
|
|
|
internal void ReadSharedResources()
|
|
{
|
|
if (!MoveToElement("Resources"))
|
|
return;
|
|
|
|
var resources = new Dictionary<string, object>();
|
|
var resourceFormat = new ContentSerializerAttribute { ElementName = "Resource" };
|
|
|
|
// Read all the resources.
|
|
Xml.ReadStartElement();
|
|
while (MoveToElement("Resource"))
|
|
{
|
|
var id = Xml.GetAttribute("ID");
|
|
var resource = ReadObject<object>(resourceFormat);
|
|
resources.Add(id, resource);
|
|
}
|
|
Xml.ReadEndElement();
|
|
|
|
// Execute the fixups.
|
|
foreach (var fixup in _resourceFixups)
|
|
{
|
|
object resource;
|
|
if (!resources.TryGetValue(fixup.Key, out resource))
|
|
throw new InvalidContentException("Missing shared resource \"" + fixup.Key + "\".");
|
|
fixup.Value(resource);
|
|
}
|
|
}
|
|
|
|
internal void ReadExternalReferences()
|
|
{
|
|
if (!MoveToElement("ExternalReferences"))
|
|
return;
|
|
|
|
var currentDir = Path.GetDirectoryName(_filePath);
|
|
|
|
// Read all the external references.
|
|
Xml.ReadStartElement();
|
|
while (MoveToElement("ExternalReference"))
|
|
{
|
|
List<Action<Type, string>> fixups;
|
|
var id = Xml.GetAttribute("ID");
|
|
if (!_externalReferences.TryGetValue(id, out fixups))
|
|
throw NewInvalidContentException(null, "Unknown external reference id '{0}'!", id);
|
|
|
|
Xml.MoveToAttribute("TargetType");
|
|
var targetType = ReadTypeName();
|
|
if (targetType == null)
|
|
throw NewInvalidContentException(null, "Could not resolve type '{0}'.", Xml.ReadContentAsString());
|
|
|
|
Xml.MoveToElement();
|
|
var filename = Xml.ReadElementString();
|
|
filename = Path.Combine(currentDir, filename);
|
|
|
|
// Apply the fixups.
|
|
foreach (var fixup in fixups)
|
|
fixup(targetType, filename);
|
|
}
|
|
Xml.ReadEndElement();
|
|
}
|
|
|
|
internal InvalidContentException NewInvalidContentException(Exception innerException, string message, params object[] args)
|
|
{
|
|
var xmlInfo = (IXmlLineInfo)Xml;
|
|
var lineAndColumn = string.Format("{0},{1}", xmlInfo.LineNumber, xmlInfo.LinePosition);
|
|
var identity = new ContentIdentity(_filePath, string.Empty, lineAndColumn);
|
|
return new InvalidContentException(string.Format(message, args), identity, innerException);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the next type in the
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Type ReadTypeName()
|
|
{
|
|
var typeName = Xml.ReadContentAsString();
|
|
return Serializer.FindType(typeName);
|
|
}
|
|
}
|
|
} |