2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.ComponentModel.DataAnnotations / System.ComponentModel.DataAnnotations / AssociatedMetadataTypeTypeDescriptor.cs
1 //
2 // AssociatedMetadataTypeTypeDescriptionProvider.cs
3 //
4 // Author:
5 //      Marek Habersack <mhabersack@novell.com>
6 //
7 // Copyright (C) 2009 Novell Inc. http://novell.com
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Collections.Generic;
32 using System.ComponentModel;
33 using System.Reflection;
34
35 namespace System.ComponentModel.DataAnnotations
36 {
37         class AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor
38         {
39                 Type type;
40                 Type associatedMetadataType;
41                 bool associatedMetadataTypeChecked;
42                 PropertyDescriptorCollection properties;
43                 
44                 Type AssociatedMetadataType {
45                         get {
46                                 if (!associatedMetadataTypeChecked && associatedMetadataType == null)
47                                         associatedMetadataType = FindMetadataType ();
48
49                                 return associatedMetadataType;
50                         }
51                 }
52                 
53                 public AssociatedMetadataTypeTypeDescriptor (ICustomTypeDescriptor parent, Type type)
54                         : this (parent, type, null)
55                 {
56                 }
57
58                 public AssociatedMetadataTypeTypeDescriptor (ICustomTypeDescriptor parent, Type type, Type associatedMetadataType)
59                         : base (parent)
60                 {
61                         this.type = type;
62                         this.associatedMetadataType = associatedMetadataType;
63                 }
64
65                 void CopyAttributes (object[] from, List <Attribute> to)
66                 {
67                         foreach (object o in from) {
68                                 Attribute a = o as Attribute;
69                                 if (a == null)
70                                         continue;
71
72                                 to.Add (a);
73                         }
74                 }
75                 
76                 public override AttributeCollection GetAttributes ()
77                 {
78                         var attributes = new List <Attribute> ();
79                         CopyAttributes (type.GetCustomAttributes (true), attributes);
80                         
81                         Type metaType = AssociatedMetadataType;
82                         if (metaType != null) 
83                                 CopyAttributes (metaType.GetCustomAttributes (true), attributes);
84                         
85                         return new AttributeCollection (attributes.ToArray ());
86                 }
87
88                 public override PropertyDescriptorCollection GetProperties ()
89                 {
90                         // Code partially copied from TypeDescriptor.TypeInfo.GetProperties
91                         if (properties != null)
92                                 return properties;
93
94                         Dictionary <string, MemberInfo> metaMembers = null;
95                         var propertiesHash = new Dictionary <string, bool> (); // name - null
96                         var propertiesList = new List <AssociatedMetadataTypePropertyDescriptor> ();
97                         Type currentType = type;
98                         Type metaType = AssociatedMetadataType;
99
100                         if (metaType != null) {
101                                 metaMembers = new Dictionary <string, MemberInfo> ();
102                                 MemberInfo[] members = metaType.GetMembers (BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
103
104                                 foreach (MemberInfo member in members) {
105                                         switch (member.MemberType) {
106                                                 case MemberTypes.Field:
107                                                 case MemberTypes.Property:
108                                                         break;
109
110                                                 default:
111                                                         continue;
112                                         }
113
114                                         string name = member.Name;
115                                         if (metaMembers.ContainsKey (name))
116                                                 continue;
117
118                                         metaMembers.Add (name, member);
119                                 }
120                         }
121                         
122                         // Getting properties type by type, because in the case of a property in the child type, where
123                         // the "new" keyword is used and also the return type is changed Type.GetProperties returns 
124                         // also the parent property. 
125                         // 
126                         // Note that we also have to preserve the properties order here.
127                         // 
128                         while (currentType != null && currentType != typeof (object)) {
129                                 PropertyInfo[] props = currentType.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
130                                 foreach (PropertyInfo property in props) {
131                                         string propName = property.Name;
132                                         
133                                         if (property.GetIndexParameters ().Length == 0 && property.CanRead && !propertiesHash.ContainsKey (propName)) {
134                                                 MemberInfo metaMember;
135
136                                                 if (metaMembers != null)
137                                                         metaMembers.TryGetValue (propName, out metaMember);
138                                                 else
139                                                         metaMember = null;
140                                                 propertiesList.Add (new AssociatedMetadataTypePropertyDescriptor (property, metaMember));
141                                                 propertiesHash.Add (propName, true);
142                                         }
143                                 }
144                                 currentType = currentType.BaseType;
145                         }
146
147                         properties = new PropertyDescriptorCollection ((PropertyDescriptor[]) propertiesList.ToArray (), true);
148                         return properties;
149                 }
150                 
151                 Type FindMetadataType ()
152                 {
153                         associatedMetadataTypeChecked = true;
154                         if (type == null)
155                                 return null;
156                         
157                         object[] attrs = type.GetCustomAttributes (typeof (MetadataTypeAttribute), true);
158                         if (attrs == null || attrs.Length == 0)
159                                 return null;
160
161                         var attr = attrs [0] as MetadataTypeAttribute;
162                         if (attr == null)
163                                 return null;
164
165                         return attr.MetadataClassType;
166                 }
167         }
168 }