diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj
index bb4287be..71579edd 100644
--- a/VocaluxeLib/VocaluxeLib.csproj
+++ b/VocaluxeLib/VocaluxeLib.csproj
@@ -108,9 +108,11 @@
-
+
+
+
@@ -142,7 +144,6 @@
-
diff --git a/VocaluxeLib/Xml/CTypeExtensions.cs b/VocaluxeLib/Xml/CTypeExtensions.cs
new file mode 100644
index 00000000..1d7c6c5a
--- /dev/null
+++ b/VocaluxeLib/Xml/CTypeExtensions.cs
@@ -0,0 +1,200 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Xml.Serialization;
+
+namespace VocaluxeLib.Xml
+{
+ // ReSharper disable InconsistentNaming
+ [AttributeUsage(AttributeTargets.Field)]
+ public class XmlAltNameAttribute : Attribute
+ {
+ public readonly string AltName;
+
+ public XmlAltNameAttribute(string altName)
+ {
+ AltName = altName;
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Field)]
+ public class XmlNormalizedAttribute : Attribute {}
+
+ [AttributeUsage(AttributeTargets.Field)]
+ public class XmlRangedAttribute : Attribute
+ {
+ public readonly int Min;
+ public readonly int Max;
+
+ public XmlRangedAttribute(int min, int max)
+ {
+ Min = min;
+ Max = max;
+ }
+ }
+
+ // ReSharper restore InconsistentNaming
+
+ public struct SFieldInfo
+ {
+ public string Name;
+ public string AltName;
+ public FieldInfo Info;
+ public object DefaultValue;
+ public bool HasDefaultValue;
+ public bool IsAttribute;
+ public bool IsList; //List with child elements (
)
+ public bool IsEmbeddedList; //List w/o child elements(
)
+ public bool IsNullable;
+ public bool IsNormalized;
+ public XmlRangedAttribute Ranged;
+ public Type SubType;
+ public string ArrayItemName;
+ }
+
+ public static class CTypeExtensions
+ {
+ private static readonly Dictionary> _CacheFields = new Dictionary>();
+ private static readonly Dictionary _CacheTypeName = new Dictionary();
+
+ public static IEnumerable GetFieldInfos(this Type type)
+ {
+ List result;
+ if (_CacheFields.TryGetValue(type, out result))
+ return result;
+ result = new List();
+ FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
+ foreach (FieldInfo field in fields)
+ {
+ if (field.HasAttribute())
+ continue;
+ XmlAttributeAttribute attribute = field.GetAttribute();
+ SFieldInfo info = new SFieldInfo {Info = field};
+ if (attribute != null)
+ {
+ info.IsAttribute = true;
+ info.Name = attribute.AttributeName;
+ }
+ else
+ {
+ XmlElementAttribute element = field.GetAttribute();
+ if (element != null)
+ info.Name = element.ElementName;
+ else
+ {
+ XmlArrayAttribute array = field.GetAttribute();
+ if (array != null)
+ {
+ Debug.Assert(field.IsList() || field.FieldType.IsArray, "Only lists and arrays can have the array attribute");
+ Debug.Assert(!info.IsAttribute, "Lists cannot be attributes");
+ info.Name = array.ElementName;
+ info.IsList = true;
+ }
+ }
+ }
+ if (string.IsNullOrEmpty(info.Name))
+ info.Name = field.Name;
+ XmlAltNameAttribute altName = field.GetAttribute();
+ if (altName != null)
+ info.AltName = altName.AltName;
+
+ if (field.FieldType.IsGenericType)
+ info.SubType = field.FieldType.GetGenericArguments()[0];
+ if (field.FieldType.IsArray)
+ info.SubType = field.FieldType.GetElementType();
+ if (field.IsList() || field.FieldType.IsArray)
+ {
+ if (!info.IsList)
+ {
+ Debug.Assert(!field.HasAttribute(), "A field cannot have an XmlElement- and XmlArray-Attribute");
+ info.IsEmbeddedList = true;
+ }
+ else
+ {
+ XmlArrayItemAttribute arrayItem = field.GetAttribute();
+ if (arrayItem != null && !string.IsNullOrEmpty(arrayItem.ElementName))
+ info.ArrayItemName = arrayItem.ElementName;
+ }
+ }
+ else if (field.FieldType.IsNullable())
+ info.IsNullable = true;
+
+ if (field.HasAttribute())
+ {
+ Debug.Assert(field.FieldType == typeof(float) || (info.IsNullable && info.SubType == typeof(float)), "Only floats can be normalized");
+ info.IsNormalized = true;
+ }
+ info.Ranged = field.GetAttribute();
+ Debug.Assert(info.Ranged == null || field.FieldType == typeof(int), "Only ints can be ranged");
+
+ DefaultValueAttribute defAttr = field.GetAttribute();
+ if (defAttr != null)
+ {
+ Debug.Assert(!field.IsList(), "Lists cannot have a default value");
+ info.HasDefaultValue = true;
+ info.DefaultValue = defAttr.Value;
+ }
+ else if (info.IsNullable)
+ {
+ info.HasDefaultValue = true;
+ info.DefaultValue = null;
+ }
+ result.Add(info);
+ }
+ _CacheFields.Add(type, result);
+ return result;
+ }
+
+ public static List GetFields(this Type type, bool attributes)
+ {
+ return GetFieldInfos(type).Where(f => f.IsAttribute == attributes).ToList();
+ }
+
+ public static string GetTypeName(this Type type)
+ {
+ string name;
+ if (_CacheTypeName.TryGetValue(type, out name))
+ return name;
+ XmlTypeAttribute att = type.GetAttribute();
+ if (att != null)
+ {
+ name = att.TypeName;
+ if (string.IsNullOrEmpty(name))
+ name = type.Name;
+ }
+ else
+ name = type.Name;
+ _CacheTypeName.Add(type, name);
+ return name;
+ }
+
+ public static bool HasAttribute(this ICustomAttributeProvider field)
+ {
+ return field.GetCustomAttributes(typeof(T), false).Length > 0;
+ }
+
+ public static T GetAttribute(this ICustomAttributeProvider field) where T : class
+ {
+ object[] attributes = field.GetCustomAttributes(typeof(T), false);
+ return attributes.Length == 0 ? null : (T)attributes[0];
+ }
+
+ public static bool IsNullable(this Type type)
+ {
+ return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
+ }
+
+ public static bool IsList(this Type type)
+ {
+ return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
+ }
+
+ public static bool IsList(this FieldInfo field)
+ {
+ return field.FieldType.IsList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/VocaluxeLib/Xml/CXmlSerializer.cs b/VocaluxeLib/Xml/CXmlSerializer.cs
index 9b1ca0c0..0a5aab48 100644
--- a/VocaluxeLib/Xml/CXmlSerializer.cs
+++ b/VocaluxeLib/Xml/CXmlSerializer.cs
@@ -1,10 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
-using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -13,40 +11,31 @@ using System.Xml.Serialization;
namespace VocaluxeLib.Xml
{
- // ReSharper disable InconsistentNaming
- [AttributeUsage(AttributeTargets.Field)]
- public class XmlNormalizedAttribute : Attribute {}
+ public delegate string GetCommentDelegate(string name);
- // ReSharper restore InconsistentNaming
-
- public static class CXmlSerializer
+ public class CXmlSerializer
{
+ private readonly GetCommentDelegate _GetCommentCallback;
+ private readonly bool _WriteDefaults;
+
+ public CXmlSerializer(bool writeDefaults = false, GetCommentDelegate getCommentCallback = null)
+ {
+ _WriteDefaults = writeDefaults;
+ _GetCommentCallback = getCommentCallback;
+ }
+
///
/// Uniform settings for writing XML files. ALWAYS use this!
///
- private static readonly XmlWriterSettings _XMLSettings = new XmlWriterSettings
+ private readonly XmlWriterSettings _XMLSettings = new XmlWriterSettings
{
Indent = true,
Encoding = Encoding.UTF8,
ConformanceLevel = ConformanceLevel.Document
};
- private struct SFieldInfo
- {
- public string Name;
- public FieldInfo Info;
- public object DefaultValue;
- public bool HasDefaultValue;
- public bool IsAttribute;
- public bool IsList; //List with child elements (
)
- public bool IsEmbeddedList; //List w/o child elements(
)
- public bool IsNullable;
- public bool IsNormalized;
- public Type SubType;
- }
-
#region Debug Helpers
- private static string _FindXPath(XmlNode node)
+ private string _GetXPath(XmlNode node)
{
StringBuilder builder = new StringBuilder();
while (node != null)
@@ -71,7 +60,7 @@ namespace VocaluxeLib.Xml
throw new ArgumentException("Node was not in a document");
}
- private static string _FindElementIndex(XmlNode element)
+ private string _FindElementIndex(XmlNode element)
{
XmlNode parentNode = element.ParentNode;
if (parentNode is XmlDocument)
@@ -90,137 +79,10 @@ namespace VocaluxeLib.Xml
}
throw new ArgumentException("Couldn't find element within parent");
}
- #endregion Debug Helpers
-
- private static bool _HasAttribute(this FieldInfo field)
- {
- return field.GetCustomAttributes(typeof(T), false).Length > 0;
- }
-
- private static T _GetAttribute(this ICustomAttributeProvider field) where T : class
- {
- object[] attributes = field.GetCustomAttributes(typeof(T), false);
- return attributes.Length == 0 ? null : (T)attributes[0];
- }
-
- private static bool _IsList(this Type type)
- {
- return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
- }
-
- private static bool _IsNullable(this Type type)
- {
- return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
- }
-
- private static bool _IsList(this FieldInfo field)
- {
- return field.FieldType._IsList();
- }
-
- private static readonly Dictionary> _CacheFields = new Dictionary>();
- private static readonly Dictionary _CacheTypeName = new Dictionary();
-
- private static List _GetFieldInfos(Type type)
- {
- List result;
- if (_CacheFields.TryGetValue(type, out result))
- return result;
- result = new List();
- FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
- foreach (FieldInfo field in fields)
- {
- if (field._HasAttribute())
- continue;
- XmlAttributeAttribute attribute = field._GetAttribute();
- SFieldInfo info = new SFieldInfo {Info = field};
- if (attribute != null)
- {
- info.IsAttribute = true;
- info.Name = attribute.AttributeName;
- }
- else
- {
- XmlElementAttribute element = field._GetAttribute();
- if (element != null)
- info.Name = element.ElementName;
- else
- {
- XmlArrayAttribute array = field._GetAttribute();
- if (array != null)
- {
- Debug.Assert(field._IsList(), "Only lists can have the array attribute");
- Debug.Assert(!info.IsAttribute, "Lists cannot be attributes");
- info.Name = array.ElementName;
- info.IsList = true;
- }
- }
- }
- if (string.IsNullOrEmpty(info.Name))
- info.Name = field.Name;
-
- if (field.FieldType.IsGenericType)
- {
- if (field._IsList())
- {
- if (!info.IsList)
- {
- Debug.Assert(!field._HasAttribute(), "A field cannot have an XmlElement- and XmlArray-Attribute");
- info.IsEmbeddedList = true;
- }
- }
- else if (field.FieldType._IsNullable())
- info.IsNullable = true;
- info.SubType = field.FieldType.GetGenericArguments()[0];
- }
- if (field._HasAttribute())
- {
- Debug.Assert(field.FieldType == typeof(float) || (info.IsNullable && info.SubType == typeof(float)), "Only floats can be normalized");
- info.IsNormalized = true;
- }
- DefaultValueAttribute defAttr = field._GetAttribute();
- if (defAttr != null)
- {
- Debug.Assert(!field._IsList(), "Lists cannot have a default value");
- info.HasDefaultValue = true;
- info.DefaultValue = defAttr.Value;
- }
- else if (info.IsNullable)
- {
- info.HasDefaultValue = true;
- info.DefaultValue = null;
- }
- result.Add(info);
- }
- _CacheFields.Add(type, result);
- return result;
- }
-
- private static List _GetFields(Type type, bool attributes)
- {
- return _GetFieldInfos(type).Where(f => f.IsAttribute == attributes).ToList();
- }
-
- private static string _GetTypeName(Type type)
- {
- string name;
- if (_CacheTypeName.TryGetValue(type, out name))
- return name;
- XmlTypeAttribute att = type._GetAttribute();
- if (att != null)
- {
- name = att.TypeName;
- if (string.IsNullOrEmpty(name))
- name = type.Name;
- }
- else
- name = type.Name;
- _CacheTypeName.Add(type, name);
- return name;
- }
+ #endregion Debug Helpers
#region Deserialization
- private static object _GetValue(XmlNode node, Type type)
+ private object _GetValue(XmlNode node, Type type, string arrayItemName = null)
{
if (type.IsEnum)
{
@@ -231,56 +93,73 @@ namespace VocaluxeLib.Xml
}
catch (Exception)
{
- throw new XmlException("Invalid value in " + _FindXPath(node));
+ throw new XmlException("Invalid value in " + _GetXPath(node));
}
}
if (type == typeof(string))
return node.InnerText;
if (type.IsPrimitive)
return _GetPrimitiveValue(node, type);
- if (type._IsList())
+ if (type.IsNullable())
{
Type subType = type.GetGenericArguments()[0];
- String subName = _GetTypeName(subType);
+ return _GetValue(node, subType);
+ }
+ if (type.IsList() || type.IsArray)
+ {
+ Type subType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0];
+ String subName = arrayItemName ?? subType.GetTypeName();
List