Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Persistence/Default/Decomposers/X2StringDecomposer.cs @ 1518

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

Make serialization order of array components explicit (in all array decomposers) (#574)

File size: 9.1 KB
Line 
1using System;
2using HeuristicLab.Persistence.Interfaces;
3using HeuristicLab.Persistence.Core;
4using System.Collections.Generic;
5using System.Reflection;
6using System.Globalization;
7using System.Text;
8
9namespace HeuristicLab.Persistence.Default.Decomposers {
10
11  public class Number2StringDecomposer : IDecomposer {
12
13    private static readonly List<Type> numberTypes =
14      new List<Type> {
15        typeof(bool),
16        typeof(byte),
17        typeof(sbyte),
18        typeof(short),
19        typeof(ushort),
20        typeof(int),
21        typeof(uint),
22        typeof(long),
23        typeof(ulong),
24        typeof(float),
25        typeof(double),
26        typeof(decimal),
27      };
28
29    private static readonly Dictionary<Type, MethodInfo> numberParsers; 
30
31    static Number2StringDecomposer() {
32      numberParsers = new Dictionary<Type, MethodInfo>();
33      foreach ( var type in numberTypes ) {
34        numberParsers[type] = type
35          .GetMethod("Parse", BindingFlags.Static | BindingFlags.Public,
36                     null, new[] {typeof (string)}, null);         
37      }
38    }
39
40    public bool CanDecompose(Type type) {
41      return numberParsers.ContainsKey(type);
42    }
43
44    public string Format(object obj) {
45      if (obj.GetType() == typeof(float))       
46        return ((float)obj).ToString("r", CultureInfo.InvariantCulture);
47      if (obj.GetType() == typeof(double))
48        return ((double)obj).ToString("r", CultureInfo.InvariantCulture);
49      if (obj.GetType() == typeof(decimal))
50        return ((decimal)obj).ToString("r", CultureInfo.InvariantCulture);
51      return obj.ToString();
52    }
53
54    public IEnumerable<Tag> DeCompose(object obj) {     
55      yield return new Tag(Format(obj));     
56    }
57
58    public object CreateInstance(Type type) {
59      return null;
60    }
61
62    public object Parse(string stringValue, Type type) {
63      return numberParsers[type]
64        .Invoke(null,
65            BindingFlags.Static | BindingFlags.PutRefDispProperty,
66                  null, new[] {stringValue}, CultureInfo.InvariantCulture);
67    }
68
69    public object Populate(object instance, IEnumerable<Tag> tags, Type type) {     
70      foreach (Tag tag in tags)
71        return Parse((string)tag.Value, type);
72      throw new ApplicationException("not enough tags to re-compose number.");
73    }
74
75  } 
76
77  public class DateTime2StringDecomposer : IDecomposer {
78
79    public bool CanDecompose(Type type) {
80      return type == typeof(DateTime);
81    }
82
83    public IEnumerable<Tag> DeCompose(object obj) {
84      yield return new Tag(((DateTime)obj).Ticks);
85    }
86
87    public object CreateInstance(Type type) {
88      return null;
89    }
90
91    public object Populate(object instance, IEnumerable<Tag> tags, Type type) {
92      foreach (Tag tag in tags) {
93        return new DateTime((long)tag.Value);
94      }
95      throw new ApplicationException("Not enough components to compose a bool.");
96    }
97
98  } 
99
100  public class CompactNumberArray2StringDecomposer : IDecomposer {
101   
102    private static readonly Number2StringDecomposer numberDecomposer =
103      new Number2StringDecomposer();   
104
105    public bool CanDecompose(Type type) {
106      return
107        (type.IsArray || type == typeof (Array)) &&
108        numberDecomposer.CanDecompose(type.GetElementType());
109    }
110
111    public IEnumerable<Tag> DeCompose(object obj) {
112      Array a = (Array) obj;
113      int[] lengths = new int[a.Rank];
114      int[] lowerBounds = new int[a.Rank];
115      StringBuilder sb = new StringBuilder();
116      sb.Append(a.Rank).Append(';');
117      for (int i = 0; i < a.Rank; i++) {
118        sb.Append(a.GetLength(i)).Append(';');
119        lengths[i] = a.GetLength(i);
120      }
121      for (int i = 0; i < a.Rank; i++) {
122        sb.Append(a.GetLowerBound(i)).Append(';');
123        lowerBounds[i] = a.GetLowerBound(i);
124      }
125      int[] positions = (int[])lowerBounds.Clone();
126      while (positions[a.Rank - 1] < lengths[a.Rank - 1] + lowerBounds[a.Rank - 1]) {
127        sb.Append(numberDecomposer.Format(a.GetValue(positions))).Append(';');
128        positions[0] += 1;
129        for (int i = 0; i < a.Rank - 1; i++) {
130          if (positions[i] >= lengths[i] + lowerBounds[i]) {
131            positions[i] = lowerBounds[i];
132            positions[i + 1] += 1;
133          } else {
134            break;
135          }
136        }
137      }
138      yield return new Tag("compact array", sb.ToString());
139    }
140
141    public object CreateInstance(Type type) {
142      return null;
143    }
144
145    public object Populate(object instance, IEnumerable<Tag> tags, Type type) {     
146      var tagIter = tags.GetEnumerator();
147      tagIter.MoveNext();
148      var valueIter = ((string) tagIter.Current.Value)
149        .Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)
150        .GetEnumerator();
151      valueIter.MoveNext();
152      int rank = int.Parse((string) valueIter.Current);     
153      int[] lengths = new int[rank];
154      int[] lowerBounds = new int[rank];     
155      for (int i = 0; i < rank; i++) {
156        valueIter.MoveNext();
157        lengths[i] = int.Parse((string) valueIter.Current);       
158      }     
159      for (int i = 0; i < rank; i++) {
160        valueIter.MoveNext();
161        lowerBounds[i] = int.Parse((string) valueIter.Current);       
162      }
163      Type elementType = type.GetElementType();
164      Array a = Array.CreateInstance(elementType, lengths, lowerBounds);
165      int[] positions = (int[]) lowerBounds.Clone();
166      while (valueIter.MoveNext()) {
167        a.SetValue(
168          numberDecomposer.Parse((string)valueIter.Current, elementType),         
169          positions);
170        positions[0] += 1;
171        for ( int i = 0; i<rank-1; i++ ) {
172          if (positions[i] >= lengths[i] + lowerBounds[i]) {
173            positions[i + 1] += 1;
174            positions[i] = lowerBounds[i];
175          } else {
176            break;
177          }
178        }
179      }
180      return a;
181    }
182  }
183
184  public class NumberEnumerable2StringDecomposer : IDecomposer {
185
186    private static readonly Number2StringDecomposer numberDecomposer =
187      new Number2StringDecomposer();
188   
189    private static readonly Dictionary<Type, Type> interfaceCache = new Dictionary<Type, Type>();
190
191    public Type GetGenericEnumerableInterface(Type type) {
192      if (interfaceCache.ContainsKey(type))
193        return interfaceCache[type];
194      foreach (Type iface in type.GetInterfaces()) {
195        if (iface.IsGenericType &&
196          iface.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
197          numberDecomposer.CanDecompose(iface.GetGenericArguments()[0])) {
198          interfaceCache.Add(type, iface);
199          return iface;
200        }
201      }
202      interfaceCache.Add(type, null);
203      return null;
204    }
205   
206    public bool ImplementsGenericEnumerable(Type type) {
207      return GetGenericEnumerableInterface(type) != null;
208    }
209
210    public bool HasAddMethod(Type type) {
211      return
212        type.GetMethod("Add") != null &&
213        type.GetMethod("Add").GetParameters().Length == 1 &&
214        type.GetConstructor(
215          BindingFlags.Public |
216          BindingFlags.NonPublic |
217          BindingFlags.Instance,
218          null, Type.EmptyTypes, null) != null;     
219    }
220
221    public bool CanDecompose(Type type) {
222      return
223        ImplementsGenericEnumerable(type) &&
224        HasAddMethod(type);
225    }
226
227    public IEnumerable<Tag> DeCompose(object obj) {
228      Type type = obj.GetType();
229      Type enumerable = GetGenericEnumerableInterface(type);     
230      InterfaceMapping iMap = obj.GetType().GetInterfaceMap(enumerable);     
231      MethodInfo getEnumeratorMethod =
232        iMap.TargetMethods[
233        Array.IndexOf(
234          iMap.InterfaceMethods,
235          enumerable.GetMethod("GetEnumerator"))];
236      object[] empty = new object[] {};
237      object genericEnumerator = getEnumeratorMethod.Invoke(obj, empty);
238      MethodInfo moveNextMethod = genericEnumerator.GetType().GetMethod("MoveNext");
239      PropertyInfo currentProperty = genericEnumerator.GetType().GetProperty("Current");
240      StringBuilder sb = new StringBuilder();
241      while ( (bool)moveNextMethod.Invoke(genericEnumerator, empty) )
242        sb.Append(
243          numberDecomposer.Format(
244            currentProperty.GetValue(genericEnumerator, null))).Append(';');
245      yield return new Tag("compact enumerable", sb.ToString());
246    }
247
248    public object CreateInstance(Type type) {
249      return Activator.CreateInstance(type, true);
250    }
251
252    public object Populate(object instance, IEnumerable<Tag> tags, Type type) {
253      Type enumerable = GetGenericEnumerableInterface(type);
254      Type elementType = enumerable.GetGenericArguments()[0];     
255      MethodInfo addMethod = type.GetMethod("Add");     
256      var tagEnumerator = tags.GetEnumerator();
257      tagEnumerator.MoveNext();
258      string[] stringValues = ((string) tagEnumerator.Current.Value)
259        .Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
260      foreach (var value in stringValues) {
261        addMethod.Invoke(instance, new[] {numberDecomposer.Parse(value, elementType)});
262      }     
263      return instance;     
264    }
265   
266  }
267
268}
Note: See TracBrowser for help on using the repository browser.