use MOONLIGHT symbol
[mono.git] / mcs / class / System / System.ComponentModel / ReflectionPropertyDescriptor.cs
index e897b83bfbde06ca7fda8355c6e278c6b64038c1..1b46293da620cc41ffb9674f01279a3e5e3318ab 100644 (file)
@@ -40,71 +40,116 @@ 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, BindingFlags.GetProperty |  BindingFlags.NonPublic |
-                                                                       BindingFlags.Public | BindingFlags.Instance);
+                               _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
-                       {
-                               bool attr_ro = false;
-
+               public override bool IsReadOnly {
+                       get {
                                ReadOnlyAttribute attrib = ((ReadOnlyAttribute) Attributes[typeof (ReadOnlyAttribute)]);
-                               if (attrib != null)
-                                       attr_ro = attrib.IsReadOnly;
-
                                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)
                {
-                       component = MemberDescriptor.GetInvokee (_componentType, 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)
@@ -114,7 +159,7 @@ namespace System.ComponentModel
                        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);
@@ -141,15 +186,90 @@ namespace System.ComponentModel
                                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);
+                       DesignerTransaction tran = CreateTransaction (component, "Set Property '" + Name + "'");
                        
                        object propertyHolder = MemberDescriptor.GetInvokee (_componentType, component);
                        object old = GetValue (propertyHolder);
 
                        try {
-                               GetPropertyInfo ().SetValue (propertyHolder, value, null);
+                               InitAccessors ();
+                               setter.SetValue (propertyHolder, value, null);
                                EndTransaction (component, tran, old, value, true);
                        } catch {
                                EndTransaction (component, tran, old, value, false);
@@ -181,7 +301,7 @@ namespace System.ComponentModel
                        if (attrib != null)
                                SetValue (propertyHolder, attrib.Value);
 
-                       DesignerTransaction tran = CreateTransaction (component);
+                       DesignerTransaction tran = CreateTransaction (component, "Reset Property '" + Name + "'");
                        object old = GetValue (propertyHolder);
 
                        try {
@@ -218,6 +338,11 @@ namespace System.ComponentModel
                                MethodInfo mi = FindPropertyMethod (component, "ShouldPersist");
                                if (mi != null)
                                        return (bool) mi.Invoke (component, null);
+
+                               mi = FindPropertyMethod (component, "ShouldSerialize");
+                               if (mi != null && !((bool) mi.Invoke (component, null)))
+                                       return false;
+
                                mi = FindPropertyMethod (component, "Reset");
                                return mi != null;
                        }
@@ -226,22 +351,29 @@ namespace System.ComponentModel
                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 = FindPropertyMethod (component, "ShouldSerialize");
                                if (mi != null)
                                        return (bool) mi.Invoke (component, null);
-
-                               return false;
+                               // MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method, 
+                               // it cannot create optimizations and it returns true. 
+                               return true;
                        }
                }
        }
 }
-