f00b88296fd03f4c05696e0a1c48faf8b1dec550
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / DataRecordObjectView.cs
1 //---------------------------------------------------------------------
2 // <copyright file="DataRecordObjectView.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.ComponentModel;
13 using System.Data.Common;
14 using System.Data.Metadata;
15 using System.Data.Metadata.Edm;
16 using System.Reflection;
17
18 namespace System.Data.Objects
19 {
20     /// <summary>
21     /// ObjectView that provides binding to a list of data records.
22     /// </summary>
23     /// <remarks>
24     /// This class provides an implementation of ITypedList that returns property descriptors
25     /// for each column of results in a data record.  
26     /// </remarks>
27     internal sealed class DataRecordObjectView : ObjectView<DbDataRecord>, ITypedList
28     {
29         /// <summary>
30         /// Cache of the property descriptors for the element type of the root list wrapped by ObjectView.
31         /// </summary>
32         private PropertyDescriptorCollection _propertyDescriptorsCache;
33
34         /// <summary>
35         /// EDM RowType that describes the shape of record elements.
36         /// </summary>
37         private RowType _rowType;
38
39         internal DataRecordObjectView(IObjectViewData<DbDataRecord> viewData, object eventDataSource, RowType rowType, Type propertyComponentType)
40             : base(viewData, eventDataSource)
41         {
42             if (!typeof(IDataRecord).IsAssignableFrom(propertyComponentType))
43             {
44                 propertyComponentType = typeof(IDataRecord);
45             }
46
47             _rowType = rowType;
48             _propertyDescriptorsCache = MaterializedDataRecord.CreatePropertyDescriptorCollection(_rowType, propertyComponentType, true);
49         }
50
51         /// <summary>
52         /// Return a <see cref="PropertyInfo"/> instance that represents
53         /// a strongly-typed indexer property on the specified type.
54         /// </summary>
55         /// <param name="typedIndexer">
56         /// <see cref="Type"/> that may define the appropriate indexer.
57         /// </param>
58         /// <returns>
59         /// <see cref="PropertyInfo"/> instance of indexer defined on supplied type
60         /// that returns an object of any type but <see cref="Object"/>;
61         /// or null if no such indexer is defined on the supplied type.
62         /// </returns>
63         /// <remarks>
64         /// The algorithm here is lifted from System.Windows.Forms.ListBindingHelper,
65         /// from the GetTypedIndexer method.
66         /// The Entity Framework could not take a dependency on Microsoft,
67         /// so we lifted the appropriate parts from the Microsoft code here.
68         /// Not the best, but much better than guessing as to what algorithm is proper for data binding.
69         /// </remarks>
70         private static PropertyInfo GetTypedIndexer(Type type)
71         {
72             PropertyInfo indexer = null;
73
74             if (typeof(IList).IsAssignableFrom(type) ||
75                 typeof(ITypedList).IsAssignableFrom(type) ||
76                 typeof(IListSource).IsAssignableFrom(type))
77             {
78                 System.Reflection.PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
79
80                 for (int idx = 0; idx < props.Length; idx++)
81                 {
82                     if (props[idx].GetIndexParameters().Length > 0 && props[idx].PropertyType != typeof(object))
83                     {
84                         indexer = props[idx];
85                         //Prefer the standard indexer, if there is one
86                         if (indexer.Name == "Item")
87                         {
88                             break;
89                         }
90                     }
91                 }
92             }
93
94             return indexer;
95         }
96
97         /// <summary>
98         /// Return the element type for the supplied type.
99         /// </summary>
100         /// <param name="type"></param>
101         /// <returns>
102         /// If <paramref name="type"/> represents a list type that doesn't also implement ITypedList or IListSource,
103         /// return the element type for items in that list.
104         /// Otherwise, return the type supplied by <paramref name="type"/>.
105         /// </returns>
106         /// <remarks>
107         /// The algorithm here is lifted from System.Windows.Forms.ListBindingHelper,
108         /// from the GetListItemType(object) method.
109         /// The Entity Framework could not take a dependency on Microsoft,
110         /// so we lifted the appropriate parts from the Microsoft code here.
111         /// Not the best, but much better than guessing as to what algorithm is proper for data binding.
112         /// </remarks>
113         private static Type GetListItemType(Type type)
114         {
115             Type itemType;
116
117             if (typeof(Array).IsAssignableFrom(type))
118             {
119                 itemType = type.GetElementType();
120             }
121             else
122             {
123                 PropertyInfo typedIndexer = GetTypedIndexer(type);
124
125                 if (typedIndexer != null)
126                 {
127                     itemType = typedIndexer.PropertyType;
128                 }
129                 else
130                 {
131                     itemType = type;
132                 }
133             }
134
135             return itemType;
136         }
137
138         #region ITypedList Members
139
140         PropertyDescriptorCollection System.ComponentModel.ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
141         {
142             PropertyDescriptorCollection propertyDescriptors;
143
144             if (listAccessors == null || listAccessors.Length == 0)
145             {
146                 // Caller is requesting property descriptors for the root element type.
147                 propertyDescriptors = _propertyDescriptorsCache;
148             }
149             else
150             {
151                 // Use the last PropertyDescriptor in the array to build the collection of returned property descriptors.
152                 PropertyDescriptor propertyDescriptor = listAccessors[listAccessors.Length - 1];
153                 FieldDescriptor fieldDescriptor = propertyDescriptor as FieldDescriptor;
154
155                 // If the property descriptor describes a data record with the EDM type of RowType,
156                 // construct the collection of property descriptors from the property's EDM metadata.
157                 // Otherwise use the CLR type of the property.
158                 if (fieldDescriptor != null && fieldDescriptor.EdmProperty != null && fieldDescriptor.EdmProperty.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.RowType)
159                 {
160                     // Retrieve property descriptors from EDM metadata.
161                     propertyDescriptors = MaterializedDataRecord.CreatePropertyDescriptorCollection((RowType)fieldDescriptor.EdmProperty.TypeUsage.EdmType, typeof(IDataRecord), true);
162                 }
163                 else
164                 {
165                     // Use the CLR type.
166                     propertyDescriptors = TypeDescriptor.GetProperties(GetListItemType(propertyDescriptor.PropertyType));
167                 }
168             }
169
170             return propertyDescriptors;
171         }
172
173         string System.ComponentModel.ITypedList.GetListName(PropertyDescriptor[] listAccessors)
174         {
175             return _rowType.Name;
176         }
177
178         #endregion
179     }
180 }