PageRenderTime 77ms CodeModel.GetById 63ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/Rhino.Etl.Core/Row.cs

http://github.com/ayende/rhino-etl
C# | 260 lines | 166 code | 30 blank | 64 comment | 26 complexity | a67382ffb75b27c107951a674a012764 MD5 | raw file
  1using System.Linq;
  2using System.Linq.Expressions;
  3using System.Runtime.CompilerServices;
  4
  5namespace Rhino.Etl.Core
  6{
  7    using System;
  8    using System.Collections;
  9    using System.Collections.Generic;
 10    using System.Data;
 11    using System.Diagnostics;
 12    using System.Reflection;
 13
 14    /// <summary>
 15    /// Represent a virtual row
 16    /// </summary>
 17    [DebuggerDisplay("Count = {items.Count}")]
 18    [DebuggerTypeProxy(typeof(QuackingDictionaryDebugView))]
 19    [Serializable]
 20    public class Row : QuackingDictionary, IEquatable<Row>
 21    {
 22        static readonly Dictionary<Type, List<PropertyInfo>> propertiesCache = new Dictionary<Type, List<PropertyInfo>>();
 23        static readonly Dictionary<Type, List<FieldInfo>> fieldsCache = new Dictionary<Type, List<FieldInfo>>();
 24
 25        /// <summary>
 26        /// Initializes a new instance of the <see cref="Row"/> class.
 27        /// </summary>
 28        /// <param name="comparer">Defines key equality</param>
 29        public Row(StringComparer comparer)
 30            : base(new Hashtable(), comparer)
 31        {
 32        }
 33
 34        /// <summary>
 35        /// Initializes a new instance of the <see cref="Row"/> class.
 36        /// </summary>
 37        public Row()
 38            : base(new Hashtable())
 39        {
 40        }
 41
 42        /// <summary>
 43        /// Initializes a new instance of the <see cref="Row"/> class.
 44        /// </summary>
 45        /// <param name="itemsToClone">The items to clone.</param>
 46        /// <param name="comparer">Defines key equality</param>
 47        protected Row(IDictionary itemsToClone, StringComparer comparer)
 48            : base(itemsToClone, comparer)
 49        {
 50        }
 51
 52
 53        /// <summary>
 54        /// Creates a copy of the given source, erasing whatever is in the row currently.
 55        /// </summary>
 56        /// <param name="source">The source row.</param>
 57        public void Copy(IDictionary source)
 58        {
 59            items = new Hashtable(source, Comparer);
 60        }
 61
 62        /// <summary>
 63        /// Gets the columns in this row.
 64        /// </summary>
 65        /// <value>The columns.</value>
 66        public IEnumerable<string> Columns
 67        {
 68            get
 69            {
 70                //We likely would want to change the row when iterating on the columns, so we
 71                //want to make sure that we send a copy, to avoid enumeration modified exception
 72                foreach (string column in new ArrayList(items.Keys))
 73                {
 74                    yield return column;
 75                }
 76            }
 77        }
 78
 79        /// <summary>
 80        /// Clones this instance.
 81        /// </summary>
 82        /// <returns></returns>
 83        public Row Clone()
 84        {
 85            Row row = new Row(this, Comparer);
 86            return row;
 87        }
 88
 89        /// <summary>
 90        /// Indicates whether the current <see cref="Row" /> is equal to another <see cref="Row" />.
 91        /// </summary>
 92        /// <returns>
 93        /// true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.
 94        /// </returns>
 95        /// <param name="other">An object to compare with this object.</param>
 96        public bool Equals(Row other)
 97        {
 98            if (!Comparer.Equals(other.Comparer))
 99                return false;
100
101            if(Columns.SequenceEqual(other.Columns, Comparer) == false)
102                return false;
103
104            foreach (var key in items.Keys)
105            {
106                var item = items[key];
107                var otherItem = other.items[key];
108
109                if (item == null | otherItem == null)
110                    return item == null & otherItem == null;
111
112                var equalityComparer = CreateComparer(item.GetType(), otherItem.GetType());
113
114                if(equalityComparer(item, otherItem) == false)
115                    return false;
116            }
117
118            return true;
119        }
120
121        private static Func<object, object, bool> CreateComparer(Type firstType, Type secondType)
122        {
123            if (firstType == secondType)
124                return Equals;
125
126            var firstParameter = Expression.Parameter(typeof (object), "first");
127            var secondParameter = Expression.Parameter(typeof (object), "second");
128
129            var equalExpression = Expression.Equal(Expression.Convert(firstParameter, firstType), 
130                Expression.Convert(Expression.Convert(secondParameter, secondType), firstType));
131
132            return Expression.Lambda<Func<object, object, bool>>(equalExpression, firstParameter, secondParameter).Compile();
133        }
134
135        /// <summary>
136        /// Creates a key from the current row, suitable for use in hashtables
137        /// </summary>
138        public ObjectArrayKeys CreateKey()
139        {
140            return CreateKey(Columns.ToArray());
141        }
142
143        /// <summary>
144        /// Creates a key that allow to do full or partial indexing on a row
145        /// </summary>
146        /// <param name="columns">The columns.</param>
147        /// <returns></returns>
148        public ObjectArrayKeys CreateKey(params string[] columns)
149        {
150            object[] array = new object[columns.Length];
151            for (int i = 0; i < columns.Length; i++)
152            {
153                array[i] = items[columns[i]];
154            }
155            return new ObjectArrayKeys(array);
156        }
157
158        /// <summary>
159        /// Copy all the public properties and fields of an object to the row
160        /// </summary>
161        /// <param name="obj">The obj.</param>
162        /// <returns></returns>
163        public static Row FromObject(object obj)
164        {
165            if (obj == null)
166                throw new ArgumentNullException("obj");
167            Row row = new Row();
168            foreach (PropertyInfo property in GetProperties(obj))
169            {
170                row[property.Name] = property.GetValue(obj, new object[0]);
171            }
172            foreach (FieldInfo field in GetFields(obj))
173            {
174                row[field.Name] = field.GetValue(obj);
175            }
176            return row;
177        }
178
179        private static List<PropertyInfo> GetProperties(object obj)
180        {
181            List<PropertyInfo> properties;
182            if (propertiesCache.TryGetValue(obj.GetType(), out properties))
183                return properties;
184
185            properties = new List<PropertyInfo>();
186            foreach (PropertyInfo property in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
187            {
188                if (property.CanRead == false || property.GetIndexParameters().Length > 0)
189                    continue;
190                properties.Add(property);
191            }
192            propertiesCache[obj.GetType()] = properties;
193            return properties;
194        }
195
196        private static List<FieldInfo> GetFields(object obj)
197        {
198            List<FieldInfo> fields;
199            if (fieldsCache.TryGetValue(obj.GetType(), out fields))
200                return fields;
201
202            fields = new List<FieldInfo>();
203            foreach (FieldInfo fieldInfo in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
204            {
205                if (Attribute.IsDefined(fieldInfo, typeof(CompilerGeneratedAttribute)) == false)
206                {
207                    fields.Add(fieldInfo);
208                }
209            }
210            fieldsCache[obj.GetType()] = fields;
211            return fields;
212        }
213
214        /// <summary>
215        /// Generate a row from the reader
216        /// </summary>
217        /// <param name="reader">The reader.</param>
218        /// <returns></returns>
219        public static Row FromReader(IDataReader reader)
220        {
221            Row row = new Row();
222            for (int i = 0; i < reader.FieldCount; i++)
223            {
224                row[reader.GetName(i)] = reader.GetValue(i);
225            }
226            return row;
227        }
228
229        /// <summary>
230        /// Create a new object of <typeparamref name="T"/> and set all
231        /// the matching fields/properties on it.
232        /// </summary>
233        /// <typeparam name="T"></typeparam>
234        /// <returns></returns>
235        public T ToObject<T>()
236        {
237            return (T)ToObject(typeof(T));
238        }
239
240        /// <summary>
241        /// Create a new object of <param name="type"/> and set all
242        /// the matching fields/properties on it.
243        /// </summary>
244        public object ToObject(Type type)
245        {
246            object instance = Activator.CreateInstance(type);
247            foreach (PropertyInfo info in GetProperties(instance))
248            {
249                if(items.Contains(info.Name) && info.CanWrite)
250                    info.SetValue(instance, items[info.Name],null);
251            }
252            foreach (FieldInfo info in GetFields(instance))
253            {
254                if(items.Contains(info.Name))
255                    info.SetValue(instance,items[info.Name]);
256            }
257            return instance;
258        }
259    }
260}