using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Collections; using System.Reflection; using System.Xml; namespace Persistence { #region Tokens public interface ISerializationToken { } public class BeginToken : ISerializationToken { public DataMemberAccessor Accessor; public int Id; public BeginToken(DataMemberAccessor accessor, int id) { this.Accessor = accessor; this.Id = id; } } public class EndToken : ISerializationToken { public DataMemberAccessor Accessor; public int Id; public EndToken(DataMemberAccessor accessor, int id) { this.Accessor = accessor; this.Id = id; } } public class PrimitiveToken : ISerializationToken { public DataMemberAccessor accessor; public object Data; public PrimitiveToken(DataMemberAccessor accessor, object data) { this.accessor = accessor; this.Data = data; } } public class ReferenceToken : ISerializationToken { public string Name; public int Id; public ReferenceToken(string name, int id) { this.Name = name; this.Id = id; } } public class NullReferenceToken : ISerializationToken { public string Name; public NullReferenceToken(string name) { this.Name = name; } } #endregion #region Primitives public interface IPrimitiveSerializer { Type Type { get; } object Serialize(object o); object DeSerialize(object o); } public class String2XMLSerializer : IPrimitiveSerializer { public Type Type { get { return typeof(string); } } public object Serialize(object o) { return "", "]]]]>") + "]]>"; } public object DeSerialize(object o) { StringBuilder sb = new StringBuilder(); foreach (string s in ((string)o).Split( new string[] { "" }, StringSplitOptions.RemoveEmptyEntries)) { sb.Append(s); } return sb.ToString(); } } public class Int2XMLSerializer : IPrimitiveSerializer { public Type Type { get { return typeof(int); } } public object Serialize(object o) { return ((int)o).ToString(); } public object DeSerialize(object o) { return int.Parse((string)o); } } #endregion #region Custom public interface ICustomSerializer { bool CanSerialize(Type type); IEnumerable Serialize(object o); object DeSerialize(IEnumerable o, Type t); } public class EnumerableSerializer : ICustomSerializer { public bool CanSerialize(Type type) { if (type.GetInterface("IEnumerable") == null) return false; if (type.GetMethod("GetEnumerator", new Type[] {}) == null) return false; MethodInfo addMethod = type.GetMethod("Add"); if (addMethod == null) return false; if (addMethod.GetParameters().Length != 1) return false; return true; } public IEnumerable Serialize(object o) { return (IEnumerable)o; } public object DeSerialize(IEnumerable objects, Type t) { object instance = Activator.CreateInstance(t); foreach (object o in objects) { t.GetMethod("Add").Invoke(instance, new object[] { o }); } return instance; } } public class ArraySerializer : ICustomSerializer { public bool CanSerialize(Type type) { return type.IsArray; } public IEnumerable Serialize(object array) { foreach (object o in (Array)array) { yield return o; } } public object DeSerialize(IEnumerable elements, Type t) { List allElements = new List(); foreach (object obj in elements) { allElements.Add(obj); } Array array = Array.CreateInstance(t.GetElementType(), allElements.Count); for (int i = 0; i < array.Length; i++) { array.SetValue(allElements[i], i); } return array; } } #endregion public class Serializer : IEnumerable { private object obj; private string rootName; private Dictionary obj2id; private Dictionary primitiveSerializers; private List customSerializers; public Serializer(object obj, IEnumerable primitiveSerializers) : this(obj, primitiveSerializers, "ROOT") { } public Serializer(object obj, IEnumerable primitiveSerializers, string rootName) { this.obj = obj; this.rootName = rootName; this.primitiveSerializers = new Dictionary(); foreach (IPrimitiveSerializer serializer in primitiveSerializers) { this.primitiveSerializers.Add(serializer.Type, serializer); } this.customSerializers = new List(); customSerializers.Add(new EnumerableSerializer()); customSerializers.Add(new ArraySerializer()); this.obj2id = new Dictionary(); obj2id.Add(new object(), 0); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } public IEnumerator GetEnumerator() { DataMemberAccessor rootAccessor = new DataMemberAccessor( this.rootName, obj.GetType(), null, () => this.obj, null); IEnumerator iterator = Serialize(rootAccessor); while (iterator.MoveNext()) yield return iterator.Current; } private IEnumerator Serialize(DataMemberAccessor accessor) { object value = accessor.Get(); if (value == null) { yield return new NullReferenceToken(accessor.Name); } else if (this.primitiveSerializers.ContainsKey(value.GetType())) { yield return new PrimitiveToken(accessor, this.primitiveSerializers[value.GetType()].Serialize(value)); } else if (this.obj2id.ContainsKey(value)) { yield return new ReferenceToken(accessor.Name, this.obj2id[value]); } else { int id = obj2id.Count; this.obj2id.Add(value, id); yield return new BeginToken(accessor, id); ICustomSerializer customSerializer = this.FindCustomSerializer(value.GetType()); if (customSerializer != null) { foreach (object obj in customSerializer.Serialize(value)) { IEnumerator iterator = this.Serialize(new DataMemberAccessor(obj)); while (iterator.MoveNext()) yield return iterator.Current; } } else { // composite serialization foreach (KeyValuePair mapping in StorableAttribute.GetAutostorableAccessors(value)) { IEnumerator iterator = this.Serialize(mapping.Value); while (iterator.MoveNext()) yield return iterator.Current; } } yield return new EndToken(accessor, id); } } private ICustomSerializer FindCustomSerializer(Type type) { foreach (ICustomSerializer s in customSerializers) { if (s.CanSerialize(type)) return s; } return null; } } public class DeSerializer { interface IAccessibleObject { object Obj { get; } } class CustomObject : IAccessibleObject { public object Obj { get { return this.obj; } } private object obj; public List customValues; public CustomObject(object obj) { this.obj = obj; this.customValues = new List(); } public void AddValue(object value) { customValues.Add(value); } } class CompositeObject : IAccessibleObject{ public object Obj { get { return this.obj; } } public object obj; public Dictionary accessorDict; public CompositeObject(object obj, Dictionary accessorDict) { this.obj = obj; this.accessorDict = new Dictionary(); foreach (KeyValuePair pair in accessorDict) { this.accessorDict.Add( pair.Value.Name, pair.Value); } } public void SetValue(string name, object value) { accessorDict[name].Set(value); } public void SetAllDefaultValues() { throw new NotImplementedException(); } } private delegate void Handler(IParseToken token); private Dictionary id2obj; private Dictionary handlers; private Stack compositeStack; private Dictionary primitiveSerializers; private List customSerializers; public DeSerializer( IEnumerable primitiveSerializers, IEnumerable customSerializers) { id2obj = new Dictionary(); compositeStack = new Stack(); handlers = new Dictionary(); handlers.Add(typeof(CompositeStart), new Handler(CompositeStartHandler)); handlers.Add(typeof(CompositeEnd), new Handler(CompositeEndHandler)); handlers.Add(typeof(Primitive), new Handler(PrimitiveHandler)); handlers.Add(typeof(Reference), new Handler(ReferenceHandler)); handlers.Add(typeof(Null), new Handler(NullHandler)); // TODO: make this configurable this.primitiveSerializers = new Dictionary(); foreach (IPrimitiveSerializer ps in primitiveSerializers) { this.primitiveSerializers.Add(ps.Type, ps); } this.customSerializers = new List(customSerializers); } public object DeSerialize(IEnumerable tokens) { foreach (IParseToken token in tokens) { handlers[token.GetType()].Invoke(token); } return compositeStack.Pop().Obj; } private void CompositeStartHandler(IParseToken token) { CompositeStart start = (CompositeStart)token; object instance = null; if (this.FindCustomSerializer(start.Type) != null) { instance = new object(); compositeStack.Push(new CustomObject(instance)); id2obj.Add(start.Id, instance); // TODO: add warning proxy } else { instance = Activator.CreateInstance(start.Type); Dictionary accessorDict = StorableAttribute.GetAutostorableAccessors(instance); compositeStack.Push(new CompositeObject(instance, accessorDict)); id2obj.Add(start.Id, instance); } } private void CompositeEndHandler(IParseToken token) { CompositeEnd end = (CompositeEnd)token; ICustomSerializer customSerializer = this.FindCustomSerializer(end.Type); if (customSerializer != null) { CustomObject customObject = (CustomObject)compositeStack.Pop(); this.SetValue(end.Name, customSerializer.DeSerialize(customObject.customValues, end.Type)); } else { CompositeObject compositeObject = (CompositeObject)compositeStack.Pop(); this.SetValue(end.Name, compositeObject.obj); } } private ICustomSerializer FindCustomSerializer(Type type) { foreach (ICustomSerializer serializer in customSerializers) { if (serializer.CanSerialize(type)) return serializer; } return null; } private void PrimitiveHandler(IParseToken token) { Primitive primitive = (Primitive)token; object value = primitiveSerializers[primitive.Type].DeSerialize(primitive.SerializedValue); this.SetValue(primitive.Name, value); } private void ReferenceHandler(IParseToken token) { Reference reference = (Reference)token; this.SetValue(reference.Name, this.id2obj[reference.Id]); } private void NullHandler(IParseToken token) { Null nil = (Null)token; this.SetValue(nil.Name, null); } private void SetValue(string name, object value) { if (compositeStack.Count == 0) { compositeStack.Push(new CompositeObject(value, new Dictionary())); } else { object accessibleObject = compositeStack.Peek(); if ( accessibleObject is CompositeObject ) { ((CompositeObject)accessibleObject).SetValue(name, value); } else if (accessibleObject is CustomObject) { ((CustomObject)accessibleObject).AddValue(value); } } } } public class XmlFormatter { delegate string Formatter(ISerializationToken token); private Dictionary formatters; private int depth; public XmlFormatter() { this.formatters = new Dictionary(); this.formatters.Add(typeof(BeginToken), new Formatter(FormatBegin)); this.formatters.Add(typeof(EndToken), new Formatter(FormatEnd)); this.formatters.Add(typeof(PrimitiveToken), new Formatter(FormatData)); this.formatters.Add(typeof(ReferenceToken), new Formatter(FormatReference)); this.formatters.Add(typeof(NullReferenceToken), new Formatter(FormatNullReference)); this.depth = 0; } public string Format(ISerializationToken token) { return formatters[token.GetType()](token); } private string Prefix { get { return new string(' ', this.depth*2); } } private string FormatBegin(ISerializationToken token) { BeginToken beginToken = (BeginToken)token; string result = String.Format("{0}\n", this.Prefix, beginToken.Accessor.Name, beginToken.Accessor.Type, beginToken.Id); this.depth += 1; return result; } private string FormatEnd(ISerializationToken token) { EndToken endToken = (EndToken)token; this.depth -= 1; return Prefix + "\n"; } private string FormatData(ISerializationToken token) { PrimitiveToken dataToken = (PrimitiveToken)token; return String.Format("{0}{3}\n", this.Prefix, dataToken.accessor.Name, dataToken.accessor.Type, dataToken.Data); } private string FormatReference(ISerializationToken token) { ReferenceToken refToken = (ReferenceToken)token; return String.Format("{0}\n", this.Prefix, refToken.Name, refToken.Id); } private string FormatNullReference(ISerializationToken token) { NullReferenceToken nullRefToken = (NullReferenceToken)token; return String.Format("{0}\n", this.Prefix, nullRefToken.Name); } } #region parsing tokens public interface IParseToken { } public class CompositeStart : IParseToken { public string Name; public Type Type; public int Id; public CompositeStart(string name, Type type, int id) { this.Name = name; this.Type = type; this.Id = id; } } public class CompositeEnd : IParseToken { public string Name; public Type Type; public int Id; public CompositeEnd(string name, Type type, int id) { this.Name = name; this.Type = type; this.Id = id; } } public class Primitive : IParseToken { public string Name; public Type Type; public string SerializedValue; public Primitive(string name, Type type, string serilaizedValue) { this.Name = name; this.Type = type; this.SerializedValue = serilaizedValue; } } public class Reference : IParseToken { public string Name; public int Id; public Reference(string name, int id) { this.Name = name; this.Id = id; } } public class Null : IParseToken { public string Name; public Null(string name) { this.Name = name; } } #endregion public class XmlParser : IEnumerable { private XmlReader reader; private delegate IEnumerator Handler(); private Dictionary handlers; public XmlParser(StreamReader input) { XmlReaderSettings settings = new XmlReaderSettings(); settings.ConformanceLevel = ConformanceLevel.Document; settings.IgnoreWhitespace = true; settings.IgnoreComments = true; this.reader = XmlReader.Create(input, settings); this.handlers = new Dictionary(); this.handlers.Add("PRIMITIVE", new Handler(ParsePrimitive)); this.handlers.Add("COMPOSITE", new Handler(ParseComposite)); this.handlers.Add("REFERENCE", new Handler(ParseReference)); this.handlers.Add("NULL", new Handler(ParseNull)); } public IEnumerator GetEnumerator() { while (this.reader.Read()) { if (!reader.IsStartElement()) { break; } IEnumerator iterator; try { iterator = handlers[reader.Name].Invoke(); } catch (KeyNotFoundException) { throw new InvalidOperationException(String.Format( "No handler for XML tag \"{0}\" installed", reader.Name)); } while (iterator.MoveNext()) { yield return iterator.Current; } } } private IEnumerator ParsePrimitive() { yield return new Primitive( this.reader.GetAttribute("name"), Type.GetType(this.reader.GetAttribute("type")), this.reader.ReadString()); } private IEnumerator ParseComposite() { string name = this.reader.GetAttribute("name"); Type type = Type.GetType(this.reader.GetAttribute("type")); int id = int.Parse(this.reader.GetAttribute("id")); yield return new CompositeStart(name, type, id); IEnumerator iterator = this.GetEnumerator(); while (iterator.MoveNext()) yield return iterator.Current; yield return new CompositeEnd(name, type, id); } private IEnumerator ParseReference() { yield return new Reference( this.reader.GetAttribute("name"), int.Parse(this.reader.GetAttribute("ref"))); } private IEnumerator ParseNull() { yield return new Null(this.reader.GetAttribute("name")); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } }