Free cookie consent management tool by TermsFeed Policy Generator

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

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

Configurable de-serializers. (#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        IEnumerable<IPrimitiveSerializer> primitiveSerializers,
274        IEnumerable<ICustomSerializer> customSerializers) {
275      id2obj = new Dictionary<int, object>();
276      compositeStack = new Stack<IAccessibleObject>();
277      handlers = new Dictionary<Type, Handler>();
278      handlers.Add(typeof(CompositeStart), new Handler(CompositeStartHandler));
279      handlers.Add(typeof(CompositeEnd), new Handler(CompositeEndHandler));
280      handlers.Add(typeof(Primitive), new Handler(PrimitiveHandler));
281      handlers.Add(typeof(Reference), new Handler(ReferenceHandler));
282      handlers.Add(typeof(Null), new Handler(NullHandler));
283      // TODO: make this configurable
284      this.primitiveSerializers = new Dictionary<Type, IPrimitiveSerializer>();
285      foreach (IPrimitiveSerializer ps in primitiveSerializers) {
286        this.primitiveSerializers.Add(ps.Type, ps);
287      }
288      this.customSerializers = new List<ICustomSerializer>(customSerializers);
289     
290    }
291    public object DeSerialize(IEnumerable<IParseToken> tokens) {
292      foreach (IParseToken token in tokens) {
293        handlers[token.GetType()].Invoke(token);
294      }
295      return compositeStack.Pop().Obj;
296    }
297    private void CompositeStartHandler(IParseToken token) {
298      CompositeStart start = (CompositeStart)token;
299      object instance = null;
300      if (this.FindCustomSerializer(start.Type) != null) {
301        instance = new object();
302        compositeStack.Push(new CustomObject(instance));
303        id2obj.Add(start.Id, instance);
304        // TODO: add warning proxy
305      } else {
306        instance = Activator.CreateInstance(start.Type);
307        Dictionary<string, DataMemberAccessor> accessorDict =
308        StorableAttribute.GetAutostorableAccessors(instance);
309        compositeStack.Push(new CompositeObject(instance, accessorDict));
310        id2obj.Add(start.Id, instance);
311      }
312    }
313    private void CompositeEndHandler(IParseToken token) {
314      CompositeEnd end = (CompositeEnd)token;     
315      ICustomSerializer customSerializer = this.FindCustomSerializer(end.Type);
316      if (customSerializer != null) {
317        CustomObject customObject = (CustomObject)compositeStack.Pop(); 
318        this.SetValue(end.Name,
319          customSerializer.DeSerialize(customObject.customValues, end.Type));
320      } else {
321        CompositeObject compositeObject = (CompositeObject)compositeStack.Pop();
322        this.SetValue(end.Name, compositeObject.obj);
323      }
324    }
325    private ICustomSerializer FindCustomSerializer(Type type) {
326      foreach (ICustomSerializer serializer in customSerializers) {
327        if (serializer.CanSerialize(type))
328          return serializer;
329      }
330      return null;
331    }
332    private void PrimitiveHandler(IParseToken token) {
333      Primitive primitive = (Primitive)token;
334      object value = primitiveSerializers[primitive.Type].DeSerialize(primitive.SerializedValue);
335      this.SetValue(primitive.Name, value);     
336    }
337    private void ReferenceHandler(IParseToken token) {
338      Reference reference = (Reference)token;     
339      this.SetValue(reference.Name, this.id2obj[reference.Id]);           
340    }
341    private void NullHandler(IParseToken token) {
342      Null nil = (Null)token;
343      this.SetValue(nil.Name, null);     
344    }
345    private void SetValue(string name, object value) {
346       if (compositeStack.Count == 0) {
347        compositeStack.Push(new CompositeObject(value, new Dictionary<string,DataMemberAccessor>()));
348       } else {
349         object accessibleObject = compositeStack.Peek();
350         if ( accessibleObject is CompositeObject ) {
351           ((CompositeObject)accessibleObject).SetValue(name, value);
352         } else if (accessibleObject is CustomObject) {           
353           ((CustomObject)accessibleObject).AddValue(value);
354         }
355      }     
356    }
357  }
358
359  public class XmlFormatter {
360
361    delegate string Formatter(ISerializationToken token);
362
363    private Dictionary<Type, Formatter> formatters;
364    private int depth;
365
366    public XmlFormatter() {
367      this.formatters = new Dictionary<Type,Formatter>();
368      this.formatters.Add(typeof(BeginToken), new Formatter(FormatBegin));
369      this.formatters.Add(typeof(EndToken), new Formatter(FormatEnd));
370      this.formatters.Add(typeof(PrimitiveToken), new Formatter(FormatData));
371      this.formatters.Add(typeof(ReferenceToken), new Formatter(FormatReference));
372      this.formatters.Add(typeof(NullReferenceToken), new Formatter(FormatNullReference));
373      this.depth = 0;
374    }
375   
376    public string Format(ISerializationToken token) {
377      return formatters[token.GetType()](token);
378    }
379
380    private string Prefix {
381      get { return new string(' ', this.depth*2); }
382    }
383
384    private string FormatBegin(ISerializationToken token) {
385      BeginToken beginToken = (BeginToken)token;
386      string result =
387        String.Format("{0}<COMPOSITE name=\"{1}\" type=\"{2}\" id=\"{3}\">\n",
388          this.Prefix, beginToken.Accessor.Name, beginToken.Accessor.Type, beginToken.Id);
389      this.depth += 1;
390      return result;
391    }
392
393    private string FormatEnd(ISerializationToken token) {
394      EndToken endToken = (EndToken)token;
395      this.depth -= 1;
396      return Prefix + "</COMPOSITE>\n";
397    }
398
399    private string FormatData(ISerializationToken token) {
400      PrimitiveToken dataToken = (PrimitiveToken)token;
401      return String.Format("{0}<PRIMITIVE name=\"{1}\" type=\"{2}\">{3}</PRIMITIVE>\n",
402        this.Prefix, dataToken.accessor.Name, dataToken.accessor.Type, dataToken.Data);     
403    }
404
405    private string FormatReference(ISerializationToken token) {
406      ReferenceToken refToken = (ReferenceToken)token;
407      return String.Format("{0}<REFERENCE name=\"{1}\" ref=\"{2}\"/>\n",
408        this.Prefix, refToken.Name, refToken.Id);     
409    }
410
411    private string FormatNullReference(ISerializationToken token) {
412      NullReferenceToken nullRefToken = (NullReferenceToken)token;
413      return String.Format("{0}<NULL name=\"{1}\"/>\n",
414        this.Prefix, nullRefToken.Name);
415    }
416
417
418  }
419
420  #region parsing tokens
421  public interface IParseToken { }
422  public class CompositeStart : IParseToken {
423    public string Name;
424    public Type Type;   
425    public int Id;
426    public CompositeStart(string name, Type type, int id) {
427      this.Name = name;
428      this.Type = type;
429      this.Id = id;     
430    }
431  }
432  public class CompositeEnd : IParseToken {
433    public string Name;
434    public Type Type;   
435    public int Id;
436    public CompositeEnd(string name, Type type, int id) {
437      this.Name = name;
438      this.Type = type;
439      this.Id = id;     
440    }
441  }
442  public class Primitive : IParseToken {
443    public string Name;
444    public Type Type;
445    public string SerializedValue;
446    public Primitive(string name, Type type, string serilaizedValue) {
447      this.Name = name;
448      this.Type = type;
449      this.SerializedValue = serilaizedValue;
450    }
451  }
452  public class Reference : IParseToken {
453    public string Name;
454    public int Id;
455    public Reference(string name, int id) {
456      this.Name = name;
457      this.Id = id;
458    }
459  }
460  public class Null : IParseToken {
461    public string Name;
462    public Null(string name) {
463      this.Name = name;
464    }
465  }
466  #endregion
467
468
469  public class XmlParser : IEnumerable<IParseToken> {
470
471    private XmlReader reader;
472    private delegate IEnumerator<IParseToken> Handler();
473    private Dictionary<string, Handler> handlers;
474
475    public XmlParser(StreamReader input) {
476      XmlReaderSettings settings = new XmlReaderSettings();
477      settings.ConformanceLevel = ConformanceLevel.Document;
478      settings.IgnoreWhitespace = true;
479      settings.IgnoreComments = true;
480      this.reader = XmlReader.Create(input, settings);
481      this.handlers = new Dictionary<string, Handler>();
482      this.handlers.Add("PRIMITIVE", new Handler(ParsePrimitive));
483      this.handlers.Add("COMPOSITE", new Handler(ParseComposite));
484      this.handlers.Add("REFERENCE", new Handler(ParseReference));
485      this.handlers.Add("NULL", new Handler(ParseNull));
486    }
487    public IEnumerator<IParseToken> GetEnumerator() {
488      while (this.reader.Read()) {
489        if (!reader.IsStartElement()) {         
490          break;
491        }
492        IEnumerator<IParseToken> iterator;
493        try {                     
494            iterator = handlers[reader.Name].Invoke();         
495        } catch (KeyNotFoundException) {         
496          throw new InvalidOperationException(String.Format(
497            "No handler for XML tag \"{0}\" installed",
498            reader.Name));
499        }
500        while (iterator.MoveNext()) {
501          yield return iterator.Current;
502        }
503      }     
504    }
505    private IEnumerator<IParseToken> ParsePrimitive() {
506      yield return new Primitive(
507        this.reader.GetAttribute("name"),
508        Type.GetType(this.reader.GetAttribute("type")),
509        this.reader.ReadString());     
510    }
511    private IEnumerator<IParseToken> ParseComposite() {
512      string name = this.reader.GetAttribute("name");
513      Type type = Type.GetType(this.reader.GetAttribute("type"));
514      int id = int.Parse(this.reader.GetAttribute("id"));
515      yield return new CompositeStart(name, type, id);       
516      IEnumerator<IParseToken> iterator = this.GetEnumerator();
517      while (iterator.MoveNext())
518        yield return iterator.Current;
519      yield return new CompositeEnd(name, type, id);     
520    }
521    private IEnumerator<IParseToken> ParseReference() {
522      yield return new Reference(
523        this.reader.GetAttribute("name"),
524        int.Parse(this.reader.GetAttribute("ref")));     
525    }
526    private IEnumerator<IParseToken> ParseNull() {
527      yield return new Null(this.reader.GetAttribute("name"));     
528    }       
529    IEnumerator IEnumerable.GetEnumerator() {
530      return this.GetEnumerator();
531    }   
532  } 
533}
Note: See TracBrowser for help on using the repository browser.