Free cookie consent management tool by TermsFeed Policy Generator

source: branches/New Persistence Exploration/Persistence/Persistence/PersistenceManager.cs @ 1276

Last change on this file since 1276 was 1276, checked in by epitzer, 15 years ago

Cleaner distinction between custom serialization and composite serialization. (#506)

File size: 18.8 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.IO;
5using System.Collections;
6using System.Reflection;
7using System.Xml;
8
9namespace Persistence {
10
11  #region Tokens
12  public interface ISerializationToken {
13  }
14  public class BeginToken : ISerializationToken {
15    public DataMemberAccessor Accessor;
16    public int Id;
17    public BeginToken(DataMemberAccessor accessor, int id) {
18      this.Accessor = accessor;
19      this.Id = id;
20    }   
21  }
22  public class EndToken : ISerializationToken {
23    public DataMemberAccessor Accessor;
24    public int Id;
25    public EndToken(DataMemberAccessor accessor, int id) {
26      this.Accessor = accessor;
27      this.Id = id;
28    }   
29  }
30  public class PrimitiveToken : ISerializationToken {
31    public DataMemberAccessor accessor;
32    public object Data;
33    public PrimitiveToken(DataMemberAccessor accessor, object data) {
34      this.accessor =  accessor;
35      this.Data = data;
36    }   
37  }
38  public class ReferenceToken : ISerializationToken {
39    public string Name;
40    public int Id;
41    public ReferenceToken(string name, int id) {
42      this.Name = name;
43      this.Id = id;
44    }   
45  }
46  public class NullReferenceToken : ISerializationToken {
47    public string Name;
48    public NullReferenceToken(string name) {
49      this.Name = name;
50    }
51  }
52  #endregion
53
54  #region Primitives
55  public interface IPrimitiveSerializer {
56    Type Type { get; }
57    object Serialize(object o);
58    object DeSerialize(object o);
59  }
60  public class String2XMLSerializer : IPrimitiveSerializer {
61    public Type Type { get { return typeof(string); } }
62    public object Serialize(object o) {
63      return "<![CDATA[" +
64        ((string)o).Replace("]]>", "]]]]><![CDATA[>") +
65        "]]>";
66    }
67    public object DeSerialize(object o) {
68      StringBuilder sb = new StringBuilder();
69      foreach (string s in ((string)o).Split(
70        new string[] { "<![CDATA[", "]]>" },
71        StringSplitOptions.RemoveEmptyEntries)) {
72        sb.Append(s);
73      }
74      return sb.ToString();
75    }
76  }
77  public class Int2XMLSerializer : IPrimitiveSerializer {
78    public Type Type { get { return typeof(int); } }
79    public object Serialize(object o) {
80      return ((int)o).ToString();
81    }
82    public object DeSerialize(object o) {
83      return int.Parse((string)o);
84    }
85  }
86  #endregion
87
88  #region Custom
89  public interface ICustomSerializer {
90    bool CanSerialize(Type type);
91    IEnumerable Serialize(object o);
92    object DeSerialize(IEnumerable o, Type t);
93  }
94  public class EnumerableSerializer : ICustomSerializer {
95    public bool CanSerialize(Type type) {
96      if (type.GetInterface("IEnumerable") == null)
97        return false;
98      if (type.GetMethod("GetEnumerator", new Type[] {}) == null)
99        return false;
100      MethodInfo addMethod = type.GetMethod("Add");
101      if (addMethod == null)
102        return false;
103      if (addMethod.GetParameters().Length != 1)
104        return false;
105      return true;
106    }
107    public IEnumerable Serialize(object o) {
108      return (IEnumerable)o;
109    }
110    public object DeSerialize(IEnumerable objects, Type t) {
111      object instance = Activator.CreateInstance(t);
112      foreach (object o in objects) {
113        t.GetMethod("Add").Invoke(instance, new object[] { o });
114      }
115      return instance;
116    }
117  }
118  public class ArraySerializer : ICustomSerializer {   
119
120    public bool CanSerialize(Type type) {
121      return type.IsArray;     
122    }
123
124    public IEnumerable Serialize(object array) {
125      foreach (object o in (Array)array) {
126        yield return o;
127      }
128    }
129
130    public object DeSerialize(IEnumerable elements, Type t) {
131      List<object> allElements = new List<object>();
132      foreach (object obj in elements) {
133        allElements.Add(obj);
134      }
135      Array array =
136        Array.CreateInstance(t.GetElementType(), allElements.Count);
137      for (int i = 0; i < array.Length; i++) {
138        array.SetValue(allElements[i], i);
139      }
140      return array;
141    }
142
143  } 
144  #endregion
145
146  public class Serializer : IEnumerable<ISerializationToken> {
147
148    private object obj;
149    private string rootName;
150    private Dictionary<object, int> obj2id;
151    private Dictionary<Type, IPrimitiveSerializer> primitiveSerializers;
152    private List<ICustomSerializer> customSerializers;
153
154    public Serializer(object obj, IEnumerable<IPrimitiveSerializer> primitiveSerializers) :
155      this(obj, primitiveSerializers, "ROOT") { }
156
157    public Serializer(object obj, IEnumerable<IPrimitiveSerializer> primitiveSerializers, string rootName) {
158      this.obj = obj;
159      this.rootName = rootName;
160      this.primitiveSerializers = new Dictionary<Type,IPrimitiveSerializer>();
161      foreach (IPrimitiveSerializer serializer in primitiveSerializers) {
162        this.primitiveSerializers.Add(serializer.Type, serializer);
163      }
164      this.customSerializers = new List<ICustomSerializer>();
165      customSerializers.Add(new EnumerableSerializer());
166      customSerializers.Add(new ArraySerializer());
167      this.obj2id = new Dictionary<object, int>();
168      obj2id.Add(new object(), 0);     
169    }   
170
171    IEnumerator IEnumerable.GetEnumerator() {
172      return this.GetEnumerator();
173    }
174
175    public IEnumerator<ISerializationToken> GetEnumerator() {
176      DataMemberAccessor rootAccessor = new DataMemberAccessor(
177        this.rootName, obj.GetType(), null, () => this.obj, null);
178      IEnumerator<ISerializationToken> iterator = Serialize(rootAccessor);
179      while (iterator.MoveNext())
180        yield return iterator.Current;     
181    }
182
183    private IEnumerator<ISerializationToken> Serialize(DataMemberAccessor accessor) {
184      object value = accessor.Get();     
185      if (value == null) {
186        yield return new NullReferenceToken(accessor.Name);
187      } else if (this.primitiveSerializers.ContainsKey(value.GetType())) {
188        yield return new PrimitiveToken(accessor, this.primitiveSerializers[value.GetType()].Serialize(value));
189      } else if (this.obj2id.ContainsKey(value)) {
190        yield return new ReferenceToken(accessor.Name, this.obj2id[value]);
191      } else {
192        int id = obj2id.Count;
193        this.obj2id.Add(value, id);
194        yield return new BeginToken(accessor, id);
195        ICustomSerializer customSerializer = this.FindCustomSerializer(value.GetType());
196        if (customSerializer != null) {
197          foreach (object obj in customSerializer.Serialize(value)) {
198            IEnumerator<ISerializationToken> iterator = this.Serialize(new DataMemberAccessor(obj));
199            while (iterator.MoveNext())
200              yield return iterator.Current;           
201          }
202        } else { // composite serialization
203          foreach (KeyValuePair<string, DataMemberAccessor> mapping in
204            StorableAttribute.GetAutostorableAccessors(value)) {
205            IEnumerator<ISerializationToken> iterator = this.Serialize(mapping.Value);
206            while (iterator.MoveNext())
207              yield return iterator.Current;
208          }
209        }
210        yield return new EndToken(accessor, id);       
211      }
212    }   
213
214    private ICustomSerializer FindCustomSerializer(Type type) {
215      foreach (ICustomSerializer s in customSerializers) {
216        if (s.CanSerialize(type))
217          return s;
218      }
219      return null;
220    }               
221  }
222
223  public class DeSerializer {
224
225    interface IAccessibleObject {
226      object Obj { get; }
227    }
228
229    class CustomObject : IAccessibleObject {
230      public object Obj { get { return this.obj; } }
231      private object obj;
232      public List<object> customValues;
233      public CustomObject(object obj) {
234        this.obj = obj;
235        this.customValues = new List<object>();
236      }
237      public void AddValue(object value) {
238        customValues.Add(value);
239      }
240    }
241
242    class CompositeObject : IAccessibleObject{
243      public object Obj { get { return this.obj; } }
244      public object obj;
245      public Dictionary<string, DataMemberAccessor> accessorDict;
246      public CompositeObject(object obj, Dictionary<string, DataMemberAccessor> accessorDict) {
247        this.obj = obj;
248        this.accessorDict = new Dictionary<string, DataMemberAccessor>();       
249        foreach (KeyValuePair<string, DataMemberAccessor> pair in accessorDict) {
250          this.accessorDict.Add(
251            pair.Value.Name,
252            pair.Value);
253        }
254      }
255      public void SetValue(string name, object value) {
256        accessorDict[name].Set(value);       
257      }
258      public void SetAllDefaultValues() {
259        throw new NotImplementedException();
260      }
261    }
262
263    private delegate void Handler(IParseToken token);
264
265    private Dictionary<int, object> id2obj;
266    private Dictionary<Type, Handler> handlers;
267    private Stack<IAccessibleObject> compositeStack;   
268
269    private Dictionary<Type, IPrimitiveSerializer> primitiveSerializers;
270    private List<ICustomSerializer> customSerializers;
271
272    public DeSerializer() {
273      id2obj = new Dictionary<int, object>();
274      compositeStack = new Stack<IAccessibleObject>();
275      handlers = new Dictionary<Type, Handler>();
276      handlers.Add(typeof(CompositeStart), new Handler(CompositeStartHandler));
277      handlers.Add(typeof(CompositeEnd), new Handler(CompositeEndHandler));
278      handlers.Add(typeof(Primitive), new Handler(PrimitiveHandler));
279      handlers.Add(typeof(Reference), new Handler(ReferenceHandler));
280      handlers.Add(typeof(Null), new Handler(NullHandler));
281      // TODO: make this configurable
282      primitiveSerializers = new Dictionary<Type, IPrimitiveSerializer>();
283      primitiveSerializers.Add(typeof(int), new Int2XMLSerializer());
284      primitiveSerializers.Add(typeof(string), new String2XMLSerializer());
285      customSerializers = new List<ICustomSerializer>();
286      customSerializers.Add(new ArraySerializer());
287      customSerializers.Add(new EnumerableSerializer());     
288    }
289    public object DeSerialize(IEnumerable<IParseToken> tokens) {
290      foreach (IParseToken token in tokens) {
291        handlers[token.GetType()].Invoke(token);
292      }
293      return compositeStack.Pop().Obj;
294    }
295    private void CompositeStartHandler(IParseToken token) {
296      CompositeStart start = (CompositeStart)token;
297      object instance = null;
298      if (this.FindCustomSerializer(start.Type) != null) {
299        instance = new object();
300        compositeStack.Push(new CustomObject(instance));
301        id2obj.Add(start.Id, instance);
302        // TODO: add warning proxy
303      } else {
304        instance = Activator.CreateInstance(start.Type);
305        Dictionary<string, DataMemberAccessor> accessorDict =
306        StorableAttribute.GetAutostorableAccessors(instance);
307        compositeStack.Push(new CompositeObject(instance, accessorDict));
308        id2obj.Add(start.Id, instance);
309      }
310    }
311    private void CompositeEndHandler(IParseToken token) {
312      CompositeEnd end = (CompositeEnd)token;     
313      ICustomSerializer customSerializer = this.FindCustomSerializer(end.Type);
314      if (customSerializer != null) {
315        CustomObject customObject = (CustomObject)compositeStack.Pop(); 
316        this.SetValue(end.Name,
317          customSerializer.DeSerialize(customObject.customValues, end.Type));
318      } else {
319        CompositeObject compositeObject = (CompositeObject)compositeStack.Pop();
320        this.SetValue(end.Name, compositeObject.obj);
321      }
322    }
323    private ICustomSerializer FindCustomSerializer(Type type) {
324      foreach (ICustomSerializer serializer in customSerializers) {
325        if (serializer.CanSerialize(type))
326          return serializer;
327      }
328      return null;
329    }
330    private void PrimitiveHandler(IParseToken token) {
331      Primitive primitive = (Primitive)token;
332      object value = primitiveSerializers[primitive.Type].DeSerialize(primitive.SerializedValue);
333      this.SetValue(primitive.Name, value);     
334    }
335    private void ReferenceHandler(IParseToken token) {
336      Reference reference = (Reference)token;     
337      this.SetValue(reference.Name, this.id2obj[reference.Id]);           
338    }
339    private void NullHandler(IParseToken token) {
340      Null nil = (Null)token;
341      this.SetValue(nil.Name, null);     
342    }
343    private void SetValue(string name, object value) {
344       if (compositeStack.Count == 0) {
345        compositeStack.Push(new CompositeObject(value, new Dictionary<string,DataMemberAccessor>()));
346       } else {
347         object accessibleObject = compositeStack.Peek();
348         if ( accessibleObject is CompositeObject ) {
349           ((CompositeObject)accessibleObject).SetValue(name, value);
350         } else if (accessibleObject is CustomObject) {           
351           ((CustomObject)accessibleObject).AddValue(value);
352         }
353      }     
354    }
355  }
356
357  public class XmlFormatter {
358
359    delegate string Formatter(ISerializationToken token);
360
361    private Dictionary<Type, Formatter> formatters;
362    private int depth;
363
364    public XmlFormatter() {
365      this.formatters = new Dictionary<Type,Formatter>();
366      this.formatters.Add(typeof(BeginToken), new Formatter(FormatBegin));
367      this.formatters.Add(typeof(EndToken), new Formatter(FormatEnd));
368      this.formatters.Add(typeof(PrimitiveToken), new Formatter(FormatData));
369      this.formatters.Add(typeof(ReferenceToken), new Formatter(FormatReference));
370      this.formatters.Add(typeof(NullReferenceToken), new Formatter(FormatNullReference));
371      this.depth = 0;
372    }
373   
374    public string Format(ISerializationToken token) {
375      return formatters[token.GetType()](token);
376    }
377
378    private string Prefix {
379      get { return new string(' ', this.depth*2); }
380    }
381
382    private string FormatBegin(ISerializationToken token) {
383      BeginToken beginToken = (BeginToken)token;
384      string result =
385        String.Format("{0}<COMPOSITE name=\"{1}\" type=\"{2}\" id=\"{3}\">\n",
386          this.Prefix, beginToken.Accessor.Name, beginToken.Accessor.Type, beginToken.Id);
387      this.depth += 1;
388      return result;
389    }
390
391    private string FormatEnd(ISerializationToken token) {
392      EndToken endToken = (EndToken)token;
393      this.depth -= 1;
394      return Prefix + "</COMPOSITE>\n";
395    }
396
397    private string FormatData(ISerializationToken token) {
398      PrimitiveToken dataToken = (PrimitiveToken)token;
399      return String.Format("{0}<PRIMITIVE name=\"{1}\" type=\"{2}\">{3}</PRIMITIVE>\n",
400        this.Prefix, dataToken.accessor.Name, dataToken.accessor.Type, dataToken.Data);     
401    }
402
403    private string FormatReference(ISerializationToken token) {
404      ReferenceToken refToken = (ReferenceToken)token;
405      return String.Format("{0}<REFERENCE name=\"{1}\" ref=\"{2}\"/>\n",
406        this.Prefix, refToken.Name, refToken.Id);     
407    }
408
409    private string FormatNullReference(ISerializationToken token) {
410      NullReferenceToken nullRefToken = (NullReferenceToken)token;
411      return String.Format("{0}<NULL name=\"{1}\"/>\n",
412        this.Prefix, nullRefToken.Name);
413    }
414
415
416  }
417
418  #region parsing tokens
419  public interface IParseToken { }
420  public class CompositeStart : IParseToken {
421    public string Name;
422    public Type Type;   
423    public int Id;
424    public CompositeStart(string name, Type type, int id) {
425      this.Name = name;
426      this.Type = type;
427      this.Id = id;     
428    }
429  }
430  public class CompositeEnd : IParseToken {
431    public string Name;
432    public Type Type;   
433    public int Id;
434    public CompositeEnd(string name, Type type, int id) {
435      this.Name = name;
436      this.Type = type;
437      this.Id = id;     
438    }
439  }
440  public class Primitive : IParseToken {
441    public string Name;
442    public Type Type;
443    public string SerializedValue;
444    public Primitive(string name, Type type, string serilaizedValue) {
445      this.Name = name;
446      this.Type = type;
447      this.SerializedValue = serilaizedValue;
448    }
449  }
450  public class Reference : IParseToken {
451    public string Name;
452    public int Id;
453    public Reference(string name, int id) {
454      this.Name = name;
455      this.Id = id;
456    }
457  }
458  public class Null : IParseToken {
459    public string Name;
460    public Null(string name) {
461      this.Name = name;
462    }
463  }
464  #endregion
465
466
467  public class XmlParser : IEnumerable<IParseToken> {
468
469    private XmlReader reader;
470    private delegate IEnumerator<IParseToken> Handler();
471    private Dictionary<string, Handler> handlers;
472
473    public XmlParser(StreamReader input) {
474      XmlReaderSettings settings = new XmlReaderSettings();
475      settings.ConformanceLevel = ConformanceLevel.Document;
476      settings.IgnoreWhitespace = true;
477      settings.IgnoreComments = true;
478      this.reader = XmlReader.Create(input, settings);
479      this.handlers = new Dictionary<string, Handler>();
480      this.handlers.Add("PRIMITIVE", new Handler(ParsePrimitive));
481      this.handlers.Add("COMPOSITE", new Handler(ParseComposite));
482      this.handlers.Add("REFERENCE", new Handler(ParseReference));
483      this.handlers.Add("NULL", new Handler(ParseNull));
484    }
485    public IEnumerator<IParseToken> GetEnumerator() {
486      while (this.reader.Read()) {
487        if (!reader.IsStartElement()) {         
488          break;
489        }
490        IEnumerator<IParseToken> iterator;
491        try {                     
492            iterator = handlers[reader.Name].Invoke();         
493        } catch (KeyNotFoundException) {         
494          throw new InvalidOperationException(String.Format(
495            "No handler for XML tag \"{0}\" installed",
496            reader.Name));
497        }
498        while (iterator.MoveNext()) {
499          yield return iterator.Current;
500        }
501      }     
502    }
503    private IEnumerator<IParseToken> ParsePrimitive() {
504      yield return new Primitive(
505        this.reader.GetAttribute("name"),
506        Type.GetType(this.reader.GetAttribute("type")),
507        this.reader.ReadString());     
508    }
509    private IEnumerator<IParseToken> ParseComposite() {
510      string name = this.reader.GetAttribute("name");
511      Type type = Type.GetType(this.reader.GetAttribute("type"));
512      int id = int.Parse(this.reader.GetAttribute("id"));
513      yield return new CompositeStart(name, type, id);       
514      IEnumerator<IParseToken> iterator = this.GetEnumerator();
515      while (iterator.MoveNext())
516        yield return iterator.Current;
517      yield return new CompositeEnd(name, type, id);     
518    }
519    private IEnumerator<IParseToken> ParseReference() {
520      yield return new Reference(
521        this.reader.GetAttribute("name"),
522        int.Parse(this.reader.GetAttribute("ref")));     
523    }
524    private IEnumerator<IParseToken> ParseNull() {
525      yield return new Null(this.reader.GetAttribute("name"));     
526    }       
527    IEnumerator IEnumerable.GetEnumerator() {
528      return this.GetEnumerator();
529    }   
530  } 
531}
Note: See TracBrowser for help on using the repository browser.