Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / System / System.ComponentModel / ReflectionPropertyDescriptor.cs
1 //
2 // System.ComponentModel.PropertyDescriptor.cs
3 //
4 // Author:
5 //  Lluis Sanchez Gual (lluis@ximian.com)
6 //  Ivan N. Zlatev (contact i-nZ.net)
7 // (C) Novell, Inc.
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
31 using System;
32 using System.Collections;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
35 using System.ComponentModel.Design;
36
37 namespace System.ComponentModel
38 {
39         internal class ReflectionPropertyDescriptor : PropertyDescriptor
40         {
41                 PropertyInfo _member;
42                 Type _componentType;
43                 Type _propertyType;
44                 PropertyInfo getter, setter;
45                 bool accessors_inited;
46
47                 public ReflectionPropertyDescriptor (Type componentType, PropertyDescriptor oldPropertyDescriptor, Attribute [] attributes)
48                 : base (oldPropertyDescriptor, attributes)
49                 {
50                         _componentType = componentType;
51                         _propertyType = oldPropertyDescriptor.PropertyType;
52                 }
53
54                 public ReflectionPropertyDescriptor (Type componentType, string name, Type type, Attribute [] attributes)
55                 : base (name, attributes)
56                 {
57                         _componentType = componentType;
58                         _propertyType = type;
59                 }
60
61                 public ReflectionPropertyDescriptor (PropertyInfo info)
62                 : base (info.Name, null)
63                 {
64                         _member = info;
65                         _componentType = _member.DeclaringType;
66                         _propertyType = info.PropertyType;
67                 }
68
69                 PropertyInfo GetPropertyInfo ()
70                 {
71                         if (_member == null) {
72                                 _member = _componentType.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic | 
73                                                                       BindingFlags.Public | BindingFlags.Instance,
74                                                                       null, this.PropertyType,
75                                                                       new Type[0], new ParameterModifier[0]);
76                                 if (_member == null)
77                                         throw new ArgumentException ("Accessor methods for the " + Name + " property are missing");
78                         }
79                         return _member;
80                 }
81
82                 public override Type ComponentType {
83                         get { return _componentType; }
84                 }
85
86                 public override bool IsReadOnly {
87                         get {
88                                 ReadOnlyAttribute attrib = ((ReadOnlyAttribute) Attributes[typeof (ReadOnlyAttribute)]);
89                                 return !GetPropertyInfo ().CanWrite || attrib.IsReadOnly;
90                         }
91                 }
92
93                 public override Type PropertyType {
94                         get { return _propertyType; }
95                 }
96
97                 // The last added to the list attributes have higher precedence
98                 //
99                 protected override void FillAttributes (IList attributeList)
100                 {
101                         base.FillAttributes (attributeList);
102
103                         if (!GetPropertyInfo ().CanWrite)
104                                 attributeList.Add (ReadOnlyAttribute.Yes);
105                         
106                         // PropertyDescriptor merges the attributes of both virtual and also "new" properties 
107                         // in the the component type hierarchy.
108                         // 
109                         int numberOfBaseTypes = 0;
110                         Type baseType = this.ComponentType;
111                         while (baseType != null && baseType != typeof (object)) {
112                                 numberOfBaseTypes++;
113                                 baseType = baseType.BaseType;
114                         }
115
116                         Attribute[][] hierarchyAttributes = new Attribute[numberOfBaseTypes][];
117                         baseType = this.ComponentType;
118                         while (baseType != null && baseType != typeof (object)) {
119                                 PropertyInfo property = baseType.GetProperty (Name, BindingFlags.NonPublic |
120                                                                               BindingFlags.Public | BindingFlags.Instance | 
121                                                                               BindingFlags.DeclaredOnly, 
122                                                                               null, this.PropertyType,
123                                                                               new Type[0], new ParameterModifier[0]);
124                                 if (property != null) {
125                                         object[] attrObjects = property.GetCustomAttributes (false);
126                                         Attribute[] attrsArray = new Attribute[attrObjects.Length];
127                                         attrObjects.CopyTo (attrsArray, 0);
128                                         // add in reverse order so that the base types have lower precedence
129                                         hierarchyAttributes[--numberOfBaseTypes] = attrsArray;
130                                 }
131                                 baseType = baseType.BaseType;
132                         }
133
134                         foreach (Attribute[] attrArray in hierarchyAttributes) {
135                                 if (attrArray != null) {
136                                         foreach (Attribute attr in attrArray)
137                                                 attributeList.Add (attr);
138                                 }
139                         }
140
141                         foreach (Attribute attribute in TypeDescriptor.GetAttributes (PropertyType))
142                                 attributeList.Add (attribute);
143                 }
144
145                 public override object GetValue (object component)
146                 {
147                         component = MemberDescriptor.GetInvokee (_componentType, component);
148                         InitAccessors ();
149                         return getter.GetValue (component,  null);
150                 }
151
152                 DesignerTransaction CreateTransaction (object obj, string description)
153                 {
154                         IComponent com = obj as IComponent;
155                         if (com == null || com.Site == null)
156                                 return null;
157
158                         IDesignerHost dh = (IDesignerHost) com.Site.GetService (typeof(IDesignerHost));
159                         if (dh == null)
160                                 return null;
161
162                         DesignerTransaction tran = dh.CreateTransaction (description);
163                         IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
164                         if (ccs != null)
165                                 ccs.OnComponentChanging (com, this);
166                         return tran;
167                 }
168
169                 void EndTransaction (object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit)
170                 {
171                         if (tran == null) {
172                                 // FIXME: EventArgs might be differen type.
173                                 OnValueChanged (obj, new PropertyChangedEventArgs (Name));
174                                 return;
175                         }
176
177                         if (commit) {
178                                 IComponent com = obj as IComponent;
179                                 IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
180                                 if (ccs != null)
181                                         ccs.OnComponentChanged (com, this, oldValue, newValue);
182                                 tran.Commit ();
183                                 // FIXME: EventArgs might be differen type.
184                                 OnValueChanged (obj, new PropertyChangedEventArgs (Name));
185                         } else
186                                 tran.Cancel ();
187                 }
188
189                 /*
190                 This method exists because reflection is way too low level for what we need.
191                 A given virtual property that is partially overriden by a child won't show the
192                 non-overriden accessor in PropertyInfo. IOW:
193                 class Parent {
194                         public virtual string Prop { get; set; }
195                 }
196                 class Child : Parent {
197                         public override string Prop {
198                                 get { return "child"; }
199                         }
200                 }
201                 PropertyInfo pi = typeof (Child).GetProperty ("Prop");
202                 pi.GetGetMethod (); //returns the MethodInfo for the overridden getter
203                 pi.GetSetMethod (); //returns null as no override exists
204                 */
205                 void InitAccessors () {
206                         if (accessors_inited)
207                                 return;
208                         PropertyInfo prop = GetPropertyInfo ();
209                         MethodInfo setterMethod, getterMethod;
210                         setterMethod = prop.GetSetMethod (true);
211                         getterMethod = prop.GetGetMethod (true);
212
213                         if (getterMethod != null)
214                                 getter = prop;
215
216                         if (setterMethod != null)
217                                 setter = prop;
218
219
220                         if (setterMethod != null && getterMethod != null) {//both exist
221                                 accessors_inited = true;
222                                 return;
223                         }
224                         if (setterMethod == null && getterMethod == null) {//neither exist, this is a broken property
225                                 accessors_inited = true;
226                                 return;
227                         }
228
229                         //In order to detect that this is a virtual property with override, we check the non null accessor
230                         MethodInfo mi = getterMethod != null ? getterMethod : setterMethod;
231
232                         if (mi == null || !mi.IsVirtual || (mi.Attributes & MethodAttributes.NewSlot) == MethodAttributes.NewSlot) {
233                                 accessors_inited = true;
234                                 return;
235                         }
236
237                         Type type = _componentType.BaseType;
238                         while (type != null && type != typeof (object)) {
239                                 prop = type.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic | 
240                                                                                       BindingFlags.Public | BindingFlags.Instance,
241                                                                                       null, this.PropertyType,
242                                                                                       new Type[0], new ParameterModifier[0]);
243                                 if (prop == null) //nothing left to search
244                                         break;
245                                 if (setterMethod == null)
246                                         setterMethod = mi = prop.GetSetMethod ();
247                                 else
248                                         getterMethod = mi = prop.GetGetMethod ();
249
250                                 if (getterMethod != null && getter == null)
251                                         getter = prop;
252         
253                                 if (setterMethod != null && setter == null)
254                                         setter = prop;
255                                 
256                                 if (mi != null)
257                                         break;
258                                 type = type.BaseType;
259                         }
260                         accessors_inited = true;
261                 }
262
263                 public override void SetValue (object component, object value)
264                 {
265                         DesignerTransaction tran = CreateTransaction (component, "Set Property '" + Name + "'");
266                         
267                         object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
268                         object old = GetValue (propertyHolder);
269
270                         try {
271                                 InitAccessors ();
272                                 setter.SetValue (propertyHolder, value, null);
273                                 EndTransaction (component, tran, old, value, true);
274                         } catch {
275                                 EndTransaction (component, tran, old, value, false);
276                                 throw;
277                         }
278                 }
279
280                 MethodInfo FindPropertyMethod (object o, string method_name)
281                 {
282                         MethodInfo mi = null;
283                         string name = method_name + Name;
284
285                         foreach (MethodInfo m in o.GetType().GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) {
286                                 // XXX should we really not check the return type of the method?
287                                 if (m.Name == name && m.GetParameters().Length == 0) {
288                                         mi = m;
289                                         break;
290                                 }
291                         }
292
293                         return mi;
294                 }
295
296                 public override void ResetValue (object component)
297                 {
298                         object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
299                         
300                         DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
301                         if (attrib != null)
302                                 SetValue (propertyHolder, attrib.Value);
303
304                         DesignerTransaction tran = CreateTransaction (component, "Reset Property '" + Name + "'");
305                         object old = GetValue (propertyHolder);
306
307                         try {
308                                 MethodInfo mi = FindPropertyMethod (propertyHolder, "Reset");
309                                 if (mi != null)
310                                         mi.Invoke (propertyHolder, null);
311                                 EndTransaction (component, tran, old, GetValue (propertyHolder), true);
312                         } catch {
313                                 EndTransaction (component, tran, old, GetValue (propertyHolder), false);
314                                 throw;
315                         }
316                 }
317
318                 public override bool CanResetValue (object component)
319                 {
320                         component = MemberDescriptor.GetInvokee (_componentType, component);
321                         
322                         DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
323                         if (attrib != null) {
324                                 object current = GetValue (component);
325                                 if (attrib.Value == null || current == null){
326                                         if (attrib.Value != current)
327                                                 return true;
328                                         if (attrib.Value == null && current == null)
329                                                 return false;
330                                 }
331
332                                 return !attrib.Value.Equals (current);
333                         } else {
334                                 if (!_member.CanWrite)
335                                         return false;
336
337                                 MethodInfo mi = FindPropertyMethod (component, "ShouldPersist");
338                                 if (mi != null)
339                                         return (bool) mi.Invoke (component, null);
340
341                                 mi = FindPropertyMethod (component, "ShouldSerialize");
342                                 if (mi != null && !((bool) mi.Invoke (component, null)))
343                                         return false;
344
345                                 mi = FindPropertyMethod (component, "Reset");
346                                 return mi != null;
347                         }
348                 }
349
350                 public override bool ShouldSerializeValue (object component)
351                 {
352                         component = MemberDescriptor.GetInvokee (_componentType, component);
353
354                         if (IsReadOnly) {
355                                 MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
356                                 if (mi != null)
357                                         return (bool) mi.Invoke (component, null);
358                                 return Attributes.Contains (DesignerSerializationVisibilityAttribute.Content);
359                         }
360
361                         DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
362                         if (attrib != null) {
363                                 object current = GetValue (component);
364                                 if (attrib.Value == null || current == null)
365                                         return attrib.Value != current;
366                                 return !attrib.Value.Equals (current);
367                         }
368                         else {
369                                 MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
370                                 if (mi != null)
371                                         return (bool) mi.Invoke (component, null);
372                                 // MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method, 
373                                 // it cannot create optimizations and it returns true. 
374                                 return true;
375                         }
376                 }
377         }
378 }