2008-02-16 Ivan N. Zlatev <contact@i-nz.net>
[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
44                 public ReflectionPropertyDescriptor (Type componentType, PropertyDescriptor oldPropertyDescriptor, Attribute [] attributes)
45                 : base (oldPropertyDescriptor, attributes)
46                 {
47                         _componentType = componentType;
48                 }
49
50                 public ReflectionPropertyDescriptor (Type componentType, string name, Type type, Attribute [] attributes)
51                 : base (name, attributes)
52                 {
53                         _componentType = componentType;
54                 }
55
56                 public ReflectionPropertyDescriptor (PropertyInfo info)
57                 : base (info.Name, (Attribute[])info.GetCustomAttributes (true))
58                 {
59                         _member = info;
60                         _componentType = _member.DeclaringType;
61                 }
62
63                 PropertyInfo GetPropertyInfo ()
64                 {
65                         if (_member == null) {
66                                 _member = _componentType.GetProperty (Name, BindingFlags.GetProperty |  BindingFlags.NonPublic |
67                                                                         BindingFlags.Public | BindingFlags.Instance);
68                                 if (_member == null)
69                                         throw new ArgumentException ("Accessor methods for the " + Name + " property are missing");
70                         }
71                         return _member;
72                 }
73
74                 public override Type ComponentType {
75                         get { return _componentType; }
76                 }
77
78                 public override bool IsReadOnly {
79                         get {
80                                 bool attr_ro = false;
81
82                                 ReadOnlyAttribute attrib = ((ReadOnlyAttribute) Attributes[typeof (ReadOnlyAttribute)]);
83                                 if (attrib != null)
84                                         attr_ro = attrib.IsReadOnly;
85
86                                 return !GetPropertyInfo ().CanWrite || attrib.IsReadOnly;
87                         }
88                 }
89
90                 public override Type PropertyType {
91                         get {
92                                 return GetPropertyInfo ().PropertyType;
93                         }
94                 }
95
96                 public override object GetValue (object component)
97                 {
98                         component = MemberDescriptor.GetInvokee (_componentType, component);
99                         return GetPropertyInfo ().GetValue (component, null);
100                 }
101
102                 DesignerTransaction CreateTransaction (object obj, string description)
103                 {
104                         IComponent com = obj as IComponent;
105                         if (com == null || com.Site == null)
106                                 return null;
107
108                         IDesignerHost dh = (IDesignerHost) com.Site.GetService (typeof(IDesignerHost));
109                         if (dh == null)
110                                 return null;
111
112                         DesignerTransaction tran = dh.CreateTransaction (description);
113                         IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
114                         if (ccs != null)
115                                 ccs.OnComponentChanging (com, this);
116                         return tran;
117                 }
118
119                 void EndTransaction (object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit)
120                 {
121                         if (tran == null) {
122                                 // FIXME: EventArgs might be differen type.
123                                 OnValueChanged (obj, new PropertyChangedEventArgs (Name));
124                                 return;
125                         }
126
127                         if (commit) {
128                                 IComponent com = obj as IComponent;
129                                 IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
130                                 if (ccs != null)
131                                         ccs.OnComponentChanged (com, this, oldValue, newValue);
132                                 tran.Commit ();
133                                 // FIXME: EventArgs might be differen type.
134                                 OnValueChanged (obj, new PropertyChangedEventArgs (Name));
135                         } else
136                                 tran.Cancel ();
137                 }
138
139                 public override void SetValue (object component, object value)
140                 {
141                         DesignerTransaction tran = CreateTransaction (component, "Set Property '" + Name + "'");
142                         
143                         object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
144                         object old = GetValue (propertyHolder);
145
146                         try {
147                                 GetPropertyInfo ().SetValue (propertyHolder, value, null);
148                                 EndTransaction (component, tran, old, value, true);
149                         } catch {
150                                 EndTransaction (component, tran, old, value, false);
151                                 throw;
152                         }
153                 }
154
155                 MethodInfo FindPropertyMethod (object o, string method_name)
156                 {
157                         MethodInfo mi = null;
158                         string name = method_name + Name;
159
160                         foreach (MethodInfo m in o.GetType().GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) {
161                                 // XXX should we really not check the return type of the method?
162                                 if (m.Name == name && m.GetParameters().Length == 0) {
163                                         mi = m;
164                                         break;
165                                 }
166                         }
167
168                         return mi;
169                 }
170
171                 public override void ResetValue (object component)
172                 {
173                         object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
174                         
175                         DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
176                         if (attrib != null)
177                                 SetValue (propertyHolder, attrib.Value);
178
179                         DesignerTransaction tran = CreateTransaction (component, "Reset Property '" + Name + "'");
180                         object old = GetValue (propertyHolder);
181
182                         try {
183                                 MethodInfo mi = FindPropertyMethod (propertyHolder, "Reset");
184                                 if (mi != null)
185                                         mi.Invoke (propertyHolder, null);
186                                 EndTransaction (component, tran, old, GetValue (propertyHolder), true);
187                         } catch {
188                                 EndTransaction (component, tran, old, GetValue (propertyHolder), false);
189                                 throw;
190                         }
191                 }
192
193                 public override bool CanResetValue (object component)
194                 {
195                         component = MemberDescriptor.GetInvokee (_componentType, component);
196                         
197                         DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
198                         if (attrib != null) {
199                                 object current = GetValue (component);
200                                 if (attrib.Value == null || current == null){
201                                         if (attrib.Value != current)
202                                                 return true;
203                                         if (attrib.Value == null && current == null)
204                                                 return false;
205                                 }
206
207                                 return !attrib.Value.Equals (current);
208                         } else {
209 #if NET_2_0
210                                 if (!_member.CanWrite)
211                                         return false;
212 #endif
213                                 MethodInfo mi = FindPropertyMethod (component, "ShouldPersist");
214                                 if (mi != null)
215                                         return (bool) mi.Invoke (component, null);
216
217                                 mi = FindPropertyMethod (component, "ShouldSerialize");
218                                 if (mi != null && !((bool) mi.Invoke (component, null)))
219                                         return false;
220
221                                 mi = FindPropertyMethod (component, "Reset");
222                                 return mi != null;
223                         }
224                 }
225
226                 public override bool ShouldSerializeValue (object component)
227                 {
228                         component = MemberDescriptor.GetInvokee (_componentType, component);
229
230                         if (IsReadOnly) {
231                                 MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
232                                 if (mi != null)
233                                         return (bool) mi.Invoke (component, null);
234                                 return Attributes.Contains (DesignerSerializationVisibilityAttribute.Content);
235                         }
236
237                         DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
238                         if (attrib != null) {
239                                 object current = GetValue (component);
240                                 if (attrib.Value == null || current == null)
241                                         return attrib.Value != current;
242                                 return !attrib.Value.Equals (current);
243                         }
244                         else {
245                                 MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
246                                 if (mi != null)
247                                         return (bool) mi.Invoke (component, null);
248                                 // MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method, 
249                                 // it cannot create optimizations and it returns true. 
250                                 return true;
251                         }
252                 }
253         }
254 }