Free cookie consent management tool by TermsFeed Policy Generator

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

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

Generic decomposers for number arrays and number enumerables that "decompose" into a single string. (#563)

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