use MOONLIGHT symbol
[mono.git] / mcs / class / System / System.ComponentModel / ReflectionPropertyDescriptor.cs
index 30d15550532e89c2ac13a4ffeae5ffba7a1cad94..1b46293da620cc41ffb9674f01279a3e5e3318ab 100644 (file)
@@ -3,8 +3,8 @@
 //
 // Author:
 //  Lluis Sanchez Gual (lluis@ximian.com)
-//
-// (C) Novell, Inc.  
+//  Ivan N. Zlatev (contact i-nZ.net)
+// (C) Novell, Inc.
 //
 
 //
 // distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so, subject to
 // the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be
 // included in all copies or substantial portions of the Software.
-// 
+//
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -40,79 +40,132 @@ namespace System.ComponentModel
        {
                PropertyInfo _member;
                Type _componentType;
-               
+               Type _propertyType;
+               PropertyInfo getter, setter;
+               bool accessors_inited;
+
                public ReflectionPropertyDescriptor (Type componentType, PropertyDescriptor oldPropertyDescriptor, Attribute [] attributes)
                : base (oldPropertyDescriptor, attributes)
                {
                        _componentType = componentType;
+                       _propertyType = oldPropertyDescriptor.PropertyType;
                }
-                                                        
+
                public ReflectionPropertyDescriptor (Type componentType, string name, Type type, Attribute [] attributes)
                : base (name, attributes)
                {
                        _componentType = componentType;
+                       _propertyType = type;
                }
-                                                        
+
                public ReflectionPropertyDescriptor (PropertyInfo info)
-               : base (info.Name, (Attribute[])info.GetCustomAttributes (true))
+               : base (info.Name, null)
                {
                        _member = info;
                        _componentType = _member.DeclaringType;
+                       _propertyType = info.PropertyType;
                }
-               
+
                PropertyInfo GetPropertyInfo ()
                {
                        if (_member == null) {
-                               _member = _componentType.GetProperty (Name);
+                               _member = _componentType.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic | 
+                                                                     BindingFlags.Public | BindingFlags.Instance,
+                                                                     null, this.PropertyType,
+                                                                     new Type[0], new ParameterModifier[0]);
                                if (_member == null)
                                        throw new ArgumentException ("Accessor methods for the " + Name + " property are missing");
                        }
                        return _member;
-               }               
+               }
 
-               public override Type ComponentType 
-               { 
+               public override Type ComponentType {
                        get { return _componentType; }
                }
 
-               public override bool IsReadOnly 
-               {
-                       get
-                       {
-                               return !GetPropertyInfo ().CanWrite;
+               public override bool IsReadOnly {
+                       get {
+                               ReadOnlyAttribute attrib = ((ReadOnlyAttribute) Attributes[typeof (ReadOnlyAttribute)]);
+                               return !GetPropertyInfo ().CanWrite || attrib.IsReadOnly;
                        }
                }
 
-               public override Type PropertyType 
+               public override Type PropertyType {
+                       get { return _propertyType; }
+               }
+
+               // The last added to the list attributes have higher precedence
+               //
+               protected override void FillAttributes (IList attributeList)
                {
-                       get
-                       {
-                               return GetPropertyInfo ().PropertyType;
+                       base.FillAttributes (attributeList);
+
+                       if (!GetPropertyInfo ().CanWrite)
+                               attributeList.Add (ReadOnlyAttribute.Yes);
+                       
+                       // PropertyDescriptor merges the attributes of both virtual and also "new" properties 
+                       // in the the component type hierarchy.
+                       // 
+                       int numberOfBaseTypes = 0;
+                       Type baseType = this.ComponentType;
+                       while (baseType != null && baseType != typeof (object)) {
+                               numberOfBaseTypes++;
+                               baseType = baseType.BaseType;
+                       }
+
+                       Attribute[][] hierarchyAttributes = new Attribute[numberOfBaseTypes][];
+                       baseType = this.ComponentType;
+                       while (baseType != null && baseType != typeof (object)) {
+                               PropertyInfo property = baseType.GetProperty (Name, BindingFlags.NonPublic |
+                                                                             BindingFlags.Public | BindingFlags.Instance | 
+                                                                             BindingFlags.DeclaredOnly, 
+                                                                             null, this.PropertyType,
+                                                                             new Type[0], new ParameterModifier[0]);
+                               if (property != null) {
+                                       object[] attrObjects = property.GetCustomAttributes (false);
+                                       Attribute[] attrsArray = new Attribute[attrObjects.Length];
+                                       attrObjects.CopyTo (attrsArray, 0);
+                                       // add in reverse order so that the base types have lower precedence
+                                       hierarchyAttributes[--numberOfBaseTypes] = attrsArray;
+                               }
+                               baseType = baseType.BaseType;
+                       }
+
+                       foreach (Attribute[] attrArray in hierarchyAttributes) {
+                               if (attrArray != null) {
+                                       foreach (Attribute attr in attrArray)
+                                               attributeList.Add (attr);
+                               }
                        }
+
+                       foreach (Attribute attribute in TypeDescriptor.GetAttributes (PropertyType))
+                               attributeList.Add (attribute);
                }
-               
+
                public override object GetValue (object component)
                {
-                       return GetPropertyInfo ().GetValue (component, null);
+                       component = MemberDescriptor.GetInvokee (_componentType, component);
+                       InitAccessors ();
+                       return getter.GetValue (component,  null);
                }
-               
-               DesignerTransaction CreateTransaction (object obj)
+
+               DesignerTransaction CreateTransaction (object obj, string description)
                {
                        IComponent com = obj as IComponent;
                        if (com == null || com.Site == null)
                                return null;
-                       
+
                        IDesignerHost dh = (IDesignerHost) com.Site.GetService (typeof(IDesignerHost));
                        if (dh == null)
                                return null;
-                       
-                       DesignerTransaction tran = dh.CreateTransaction ();
+
+                       DesignerTransaction tran = dh.CreateTransaction (description);
                        IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
                        if (ccs != null)
                                ccs.OnComponentChanging (com, this);
                        return tran;
                }
-               
+
                void EndTransaction (object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit)
                {
                        if (tran == null) {
@@ -120,7 +173,7 @@ namespace System.ComponentModel
                                OnValueChanged (obj, new PropertyChangedEventArgs (Name));
                                return;
                        }
-                       
+
                        if (commit) {
                                IComponent com = obj as IComponent;
                                IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
@@ -132,14 +185,91 @@ namespace System.ComponentModel
                        } else
                                tran.Cancel ();
                }
-               
+
+               /*
+               This method exists because reflection is way too low level for what we need.
+               A given virtual property that is partially overriden by a child won't show the
+               non-overriden accessor in PropertyInfo. IOW:
+               class Parent {
+                       public virtual string Prop { get; set; }
+               }
+               class Child : Parent {
+                       public override string Prop {
+                               get { return "child"; }
+                       }
+               }
+               PropertyInfo pi = typeof (Child).GetProperty ("Prop");
+               pi.GetGetMethod (); //returns the MethodInfo for the overridden getter
+               pi.GetSetMethod (); //returns null as no override exists
+               */
+               void InitAccessors () {
+                       if (accessors_inited)
+                               return;
+                       PropertyInfo prop = GetPropertyInfo ();
+                       MethodInfo setterMethod, getterMethod;
+                       setterMethod = prop.GetSetMethod (true);
+                       getterMethod = prop.GetGetMethod (true);
+
+                       if (getterMethod != null)
+                               getter = prop;
+
+                       if (setterMethod != null)
+                               setter = prop;
+
+
+                       if (setterMethod != null && getterMethod != null) {//both exist
+                               accessors_inited = true;
+                               return;
+                       }
+                       if (setterMethod == null && getterMethod == null) {//neither exist, this is a broken property
+                               accessors_inited = true;
+                               return;
+                       }
+
+                       //In order to detect that this is a virtual property with override, we check the non null accessor
+                       MethodInfo mi = getterMethod != null ? getterMethod : setterMethod;
+
+                       if (mi == null || !mi.IsVirtual || (mi.Attributes & MethodAttributes.NewSlot) == MethodAttributes.NewSlot) {
+                               accessors_inited = true;
+                               return;
+                       }
+
+                       Type type = _componentType.BaseType;
+                       while (type != null && type != typeof (object)) {
+                               prop = type.GetProperty (Name, BindingFlags.GetProperty | BindingFlags.NonPublic | 
+                                                                                     BindingFlags.Public | BindingFlags.Instance,
+                                                                                     null, this.PropertyType,
+                                                                                     new Type[0], new ParameterModifier[0]);
+                               if (prop == null) //nothing left to search
+                                       break;
+                               if (setterMethod == null)
+                                       setterMethod = mi = prop.GetSetMethod ();
+                               else
+                                       getterMethod = mi = prop.GetGetMethod ();
+
+                               if (getterMethod != null && getter == null)
+                                       getter = prop;
+       
+                               if (setterMethod != null && setter == null)
+                                       setter = prop;
+                               
+                               if (mi != null)
+                                       break;
+                               type = type.BaseType;
+                       }
+                       accessors_inited = true;
+               }
+
                public override void SetValue (object component, object value)
                {
-                       DesignerTransaction tran = CreateTransaction (component);
-                       object old = GetValue (component);
+                       DesignerTransaction tran = CreateTransaction (component, "Set Property '" + Name + "'");
                        
+                       object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
+                       object old = GetValue (propertyHolder);
+
                        try {
-                               GetPropertyInfo ().SetValue (component, value, null);
+                               InitAccessors ();
+                               setter.SetValue (propertyHolder, value, null);
                                EndTransaction (component, tran, old, value, true);
                        } catch {
                                EndTransaction (component, tran, old, value, false);
@@ -147,28 +277,48 @@ namespace System.ComponentModel
                        }
                }
 
+               MethodInfo FindPropertyMethod (object o, string method_name)
+               {
+                       MethodInfo mi = null;
+                       string name = method_name + Name;
+
+                       foreach (MethodInfo m in o.GetType().GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) {
+                               // XXX should we really not check the return type of the method?
+                               if (m.Name == name && m.GetParameters().Length == 0) {
+                                       mi = m;
+                                       break;
+                               }
+                       }
+
+                       return mi;
+               }
+
                public override void ResetValue (object component)
                {
-                       DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
-                       if (attrib != null) 
-                               SetValue (component, attrib.Value); 
-                       
-                       DesignerTransaction tran = CreateTransaction (component);
-                       object old = GetValue (component);
+                       object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
                        
+                       DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
+                       if (attrib != null)
+                               SetValue (propertyHolder, attrib.Value);
+
+                       DesignerTransaction tran = CreateTransaction (component, "Reset Property '" + Name + "'");
+                       object old = GetValue (propertyHolder);
+
                        try {
-                               MethodInfo mi = component.GetType().GetMethod ("Reset" + Name, Type.EmptyTypes);
+                               MethodInfo mi = FindPropertyMethod (propertyHolder, "Reset");
                                if (mi != null)
-                                       mi.Invoke (component, null);
-                               EndTransaction (component, tran, old, GetValue (component), true);
+                                       mi.Invoke (propertyHolder, null);
+                               EndTransaction (component, tran, old, GetValue (propertyHolder), true);
                        } catch {
-                               EndTransaction (component, tran, old, GetValue (component), false);
+                               EndTransaction (component, tran, old, GetValue (propertyHolder), false);
                                throw;
                        }
                }
 
                public override bool CanResetValue (object component)
                {
+                       component = MemberDescriptor.GetInvokee (_componentType, component);
+                       
                        DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
                        if (attrib != null) {
                                object current = GetValue (component);
@@ -181,30 +331,49 @@ namespace System.ComponentModel
 
                                return !attrib.Value.Equals (current);
                        } else {
-                               MethodInfo mi = component.GetType().GetMethod ("ShouldPersist" + Name, Type.EmptyTypes);
+#if NET_2_0
+                               if (!_member.CanWrite)
+                                       return false;
+#endif
+                               MethodInfo mi = FindPropertyMethod (component, "ShouldPersist");
                                if (mi != null)
                                        return (bool) mi.Invoke (component, null);
-                               mi = component.GetType().GetMethod ("Reset" + Name, Type.EmptyTypes);
+
+                               mi = FindPropertyMethod (component, "ShouldSerialize");
+                               if (mi != null && !((bool) mi.Invoke (component, null)))
+                                       return false;
+
+                               mi = FindPropertyMethod (component, "Reset");
                                return mi != null;
                        }
                }
 
                public override bool ShouldSerializeValue (object component)
                {
+                       component = MemberDescriptor.GetInvokee (_componentType, component);
+
+                       if (IsReadOnly) {
+                               MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
+                               if (mi != null)
+                                       return (bool) mi.Invoke (component, null);
+                               return Attributes.Contains (DesignerSerializationVisibilityAttribute.Content);
+                       }
+
                        DefaultValueAttribute attrib = ((DefaultValueAttribute) Attributes[typeof (DefaultValueAttribute)]);
                        if (attrib != null) {
                                object current = GetValue (component);
-                               if ((attrib.Value == null || current == null) && attrib.Value != current)
-                                       return true;
+                               if (attrib.Value == null || current == null)
+                                       return attrib.Value != current;
                                return !attrib.Value.Equals (current);
                        }
                        else {
-                               MethodInfo mi = component.GetType().GetMethod ("ShouldSerialize" + Name, Type.EmptyTypes);
+                               MethodInfo mi = FindPropertyMethod (component, "ShouldSerialize");
                                if (mi != null)
                                        return (bool) mi.Invoke (component, null);
+                               // MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method, 
+                               // it cannot create optimizations and it returns true. 
                                return true;
                        }
                }
        }
 }
-