File: compmod\system\componentmodel\PropertyDescriptor.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="PropertyDescriptor.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 */
 
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2113:SecureLateBindingMethods", Scope="member", Target="System.ComponentModel.PropertyDescriptor.GetTypeFromName(System.String):System.Type")]
 
namespace System.ComponentModel {
 
    using Microsoft.Win32;
    using System;
    using System.Collections;
    using System.ComponentModel.Design;
    using System.Diagnostics;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting.Activation;
    using System.Runtime.Serialization.Formatters;
    using System.Security;
    using System.Security.Permissions;
 
    /// <devdoc>
    ///    <para>Provides a description of a property.</para>
    /// </devdoc>
    [HostProtection(SharedState = true)]
    [System.Runtime.InteropServices.ComVisible(true)]
    public abstract class PropertyDescriptor : MemberDescriptor {
        private TypeConverter converter = null;
        private Hashtable     valueChangedHandlers;
        private object[]      editors;
        private Type[]        editorTypes;
        private int           editorCount;
 
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.ComponentModel.PropertyDescriptor'/> class with the specified name and
        ///       attributes.
        ///    </para>
        /// </devdoc>
        protected PropertyDescriptor(string name, Attribute[] attrs)
        : base(name, attrs) {
        }
        
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.ComponentModel.PropertyDescriptor'/> class with
        ///       the name and attributes in the specified <see cref='System.ComponentModel.MemberDescriptor'/>.
        ///    </para>
        /// </devdoc>
        protected PropertyDescriptor(MemberDescriptor descr)
        : base(descr) {
        }
        
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.ComponentModel.PropertyDescriptor'/> class with
        ///       the name in the specified <see cref='System.ComponentModel.MemberDescriptor'/> and the
        ///       attributes in both the <see cref='System.ComponentModel.MemberDescriptor'/> and the
        ///    <see cref='System.Attribute'/> array. 
        ///    </para>
        /// </devdoc>
        protected PropertyDescriptor(MemberDescriptor descr, Attribute[] attrs)
        : base(descr, attrs) {
        }
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, gets the type of the
        ///       component this property
        ///       is bound to.
        ///    </para>
        /// </devdoc>
        public abstract Type ComponentType {get;}
 
        /// <devdoc>
        ///    <para>
        ///       Gets the type converter for this property.
        ///    </para>
        /// </devdoc>
        public virtual TypeConverter Converter {
            get {
                // Always grab the attribute collection first here, because if the metadata version
                // changes it will invalidate our type converter cache.
                AttributeCollection attrs = Attributes;
 
                if (converter == null) {
                    TypeConverterAttribute attr = (TypeConverterAttribute)attrs[typeof(TypeConverterAttribute)];
                    if (attr.ConverterTypeName != null && attr.ConverterTypeName.Length > 0) {
                        Type converterType = GetTypeFromName(attr.ConverterTypeName);
                        if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) 
                        {
                            converter = (TypeConverter)CreateInstance(converterType);
                        }
                    }
 
                    if (converter == null) {
                        converter = TypeDescriptor.GetConverter(PropertyType);
                    }
                }
                return converter;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Gets a value
        ///       indicating whether this property should be localized, as
        ///       specified in the <see cref='System.ComponentModel.LocalizableAttribute'/>.
        ///    </para>
        /// </devdoc>
        public virtual bool IsLocalizable {
            get {
                return(LocalizableAttribute.Yes.Equals(Attributes[typeof(LocalizableAttribute)]));
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in
        ///       a derived class, gets a value
        ///       indicating whether this property is read-only.
        ///    </para>
        /// </devdoc>
        public abstract bool IsReadOnly { get;}
 
        /// <devdoc>
        ///    <para>
        ///       Gets a value
        ///       indicating whether this property should be serialized as specified in the <see cref='System.ComponentModel.DesignerSerializationVisibilityAttribute'/>.
        ///    </para>
        /// </devdoc>
        public DesignerSerializationVisibility SerializationVisibility {
            get {
                DesignerSerializationVisibilityAttribute attr = (DesignerSerializationVisibilityAttribute)Attributes[typeof(DesignerSerializationVisibilityAttribute)];
                return attr.Visibility;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class,
        ///       gets the type of the property.
        ///    </para>
        /// </devdoc>
        public abstract Type PropertyType { get;}
 
        /// <devdoc>
        ///     Allows interested objects to be notified when this property changes.
        /// </devdoc>
        public virtual void AddValueChanged(object component, EventHandler handler) {
            if (component == null) throw new ArgumentNullException("component");
            if (handler == null) throw new ArgumentNullException("handler");
            
            if (valueChangedHandlers == null) {
                valueChangedHandlers = new Hashtable();
            }
            
            EventHandler h = (EventHandler)valueChangedHandlers[component];
            valueChangedHandlers[component] = Delegate.Combine(h, handler);
        }
        
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, indicates whether
        ///       resetting the <paramref name="component "/>will change the value of the
        ///    <paramref name="component"/>.
        /// </para>
        /// </devdoc>
        public abstract bool CanResetValue(object component);
 
        /// <devdoc>
        ///    <para>
        ///       Compares this to another <see cref='System.ComponentModel.PropertyDescriptor'/>
        ///       to see if they are equivalent.
        ///       NOTE: If you make a change here, you likely need to change GetHashCode() as well.
        ///    </para>
        /// </devdoc>
        public override bool Equals(object obj) {
            try {
                if (obj == this) {
                    return true;
                }
                
                if (obj == null) {
                    return false;
                }
                
                // Assume that 90% of the time we will only do a .Equals(...) for
                // propertydescriptor vs. propertydescriptor... avoid the overhead
                // of an instanceof call.
                PropertyDescriptor pd = obj as PropertyDescriptor;
 
                if (pd != null && pd.NameHashCode == this.NameHashCode
                    && pd.PropertyType == this.PropertyType
                    && pd.Name.Equals(this.Name)) {
    
                    return true;
                }
            }
            catch {}
 
            return false;
        }
 
        /// <devdoc>
        ///    <para>
        ///       Creates an instance of the
        ///       specified type.
        ///    </para>
        /// </devdoc>
        protected object CreateInstance(Type type) {
            Type[] typeArgs = new Type[] {typeof(Type)};
            ConstructorInfo ctor = type.GetConstructor(typeArgs);
            if (ctor != null) {
                return TypeDescriptor.CreateInstance(null, type, typeArgs, new object[] {PropertyType});
            }
            
            return TypeDescriptor.CreateInstance(null, type, null, null);
        }
 
        /// <devdoc>
        ///       In an inheriting class, adds the attributes of the inheriting class to the
        ///       specified list of attributes in the parent class.  For duplicate attributes,
        ///       the last one added to the list will be kept.
        /// </devdoc>
        protected override void FillAttributes(IList attributeList) {
 
            // Each time we fill our attributes, we should clear our cached
            // stuff.
            converter = null;
            editors = null;
            editorTypes = null;
            editorCount = 0;
 
            base.FillAttributes(attributeList);
        }
 
        /// <include file='doc\PropertyDescriptor.uex' path='docs/doc[@for="PropertyDescriptor.GetChildProperties"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public PropertyDescriptorCollection GetChildProperties() {
            return GetChildProperties(null, null);
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public PropertyDescriptorCollection GetChildProperties(Attribute[] filter) {
            return GetChildProperties(null, filter);
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public PropertyDescriptorCollection GetChildProperties(object instance) {
            return GetChildProperties(instance, null);
        }
        
        /// <devdoc>
        ///    Retrieves the properties 
        /// </devdoc>
        public virtual PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) {
            if (instance == null) {
                return TypeDescriptor.GetProperties(PropertyType, filter);
            }
            else {
                return TypeDescriptor.GetProperties(instance, filter);
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Gets an editor of the specified type.
        ///    </para>
        /// </devdoc>
        public virtual object GetEditor(Type editorBaseType) {
            object editor = null;
 
            // Always grab the attribute collection first here, because if the metadata version
            // changes it will invalidate our editor cache.
            AttributeCollection attrs = Attributes;
 
            // Check the editors we've already created for this type.
            //
            if (editorTypes != null) {
                for (int i = 0; i < editorCount; i++) {
                    if (editorTypes[i] == editorBaseType) {
                        return editors[i];
                    }
                }
            }
 
            // If one wasn't found, then we must go through the attributes.
            //
            if (editor == null) {
                for (int i = 0; i < attrs.Count; i++) {
                    EditorAttribute attr = attrs[i] as EditorAttribute;
                    if (attr == null) {
                        continue;
                    }
 
                    Type editorType = GetTypeFromName(attr.EditorBaseTypeName);
 
                    if (editorBaseType == editorType) {
                        Type type = GetTypeFromName(attr.EditorTypeName);
                        if (type != null) {
                            editor = CreateInstance(type);
                            break;
                        }
                    }
                }
                
                // Now, if we failed to find it in our own attributes, go to the
                // component descriptor.
                //
                if (editor == null) {
                    editor = TypeDescriptor.GetEditor(PropertyType, editorBaseType);
                }
                
                // Now, another slot in our editor cache for next time
                //
                if (editorTypes == null) {
                    editorTypes = new Type[5];
                    editors = new object[5];
                }
 
                if (editorCount >= editorTypes.Length) {
                    Type[] newTypes = new Type[editorTypes.Length * 2];
                    object[] newEditors = new object[editors.Length * 2];
                    Array.Copy(editorTypes, newTypes, editorTypes.Length);
                    Array.Copy(editors, newEditors, editors.Length);
                    editorTypes = newTypes;
                    editors = newEditors;
                }
 
                editorTypes[editorCount] = editorBaseType;
                editors[editorCount++] = editor;
            }
 
            return editor;
        }
 
        /// <devdoc>
        ///     Try to keep this reasonable in sync with Equals(). Specifically, 
        ///     if A.Equals(B) returns true, A & B should have the same hash code.
        /// </devdoc>
        public override int GetHashCode() {
            return this.NameHashCode ^ PropertyType.GetHashCode();
        }
 
        /// <devdoc>
        ///     This method returns the object that should be used during invocation of members.
        ///     Normally the return value will be the same as the instance passed in.  If
        ///     someone associated another object with this instance, or if the instance is a
        ///     custom type descriptor, GetInvocationTarget may return a different value.
        /// </devdoc>
        protected override object GetInvocationTarget(Type type, object instance) {
            object target = base.GetInvocationTarget(type, instance);
            ICustomTypeDescriptor td = target as ICustomTypeDescriptor;
            if (td != null) {
                target = td.GetPropertyOwner(this);
            }
 
            return target;
        }
       
        /// <devdoc>
        ///    <para>Gets a type using its name.</para>
        /// </devdoc>
        protected Type GetTypeFromName(string typeName) {
 
            if (typeName == null || typeName.Length == 0) {
                return null;
            }
 
            //  try the generic method.
            Type typeFromGetType = Type.GetType(typeName);
 
            // If we didn't get a type from the generic method, or if the assembly we found the type
            // in is the same as our Component's assembly, use or Component's assembly instead.  This is
            // because the CLR may have cached an older version if the assembly's version number didn't change
            // See VSWhidbey 560732
            Type typeFromComponent = null;
            if (ComponentType != null) {
                if ((typeFromGetType == null) ||
                    (ComponentType.Assembly.FullName.Equals(typeFromGetType.Assembly.FullName))) {
 
                    int comma = typeName.IndexOf(',');
 
                    if (comma != -1)
                        typeName = typeName.Substring(0, comma);
 
                    typeFromComponent = ComponentType.Assembly.GetType(typeName);
                }
            }
 
            return typeFromComponent ?? typeFromGetType;
        }
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, gets the current
        ///       value
        ///       of the
        ///       property on a component.
        ///    </para>
        /// </devdoc>
        public abstract object GetValue(object component);
 
        /// <devdoc>
        ///     This should be called by your property descriptor implementation
        ///     when the property value has changed.
        /// </devdoc>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
        protected virtual void OnValueChanged(object component, EventArgs e) {
            if (component != null && valueChangedHandlers != null) {
                EventHandler handler = (EventHandler)valueChangedHandlers[component];
                if (handler != null) {
                    handler(component, e);
                }
            }
        }
 
        /// <devdoc>
        ///     Allows interested objects to be notified when this property changes.
        /// </devdoc>
        public virtual void RemoveValueChanged(object component, EventHandler handler) {
            if (component == null) throw new ArgumentNullException("component");
            if (handler == null) throw new ArgumentNullException("handler");
            
            if (valueChangedHandlers != null) {
                EventHandler h = (EventHandler)valueChangedHandlers[component];
                h = (EventHandler)Delegate.Remove(h, handler);
                if (h != null) {
                    valueChangedHandlers[component] = h;
                }
                else {
                    valueChangedHandlers.Remove(component);
                }
            }
        }
        
        /// <devdoc>
        ///     Return current set of ValueChanged event handlers for a specific
        ///     component, in the form of a combined multicast event handler.
        ///     Returns null if no event handlers currently assigned to component.
        /// </devdoc>
        internal protected EventHandler GetValueChangedHandler(object component) {
            if (component != null && valueChangedHandlers != null) {
                return (EventHandler) valueChangedHandlers[component];
            }
            else {
                return null;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, resets the
        ///       value
        ///       for this property
        ///       of the component.
        ///    </para>
        /// </devdoc>
        public abstract void ResetValue(object component);
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, sets the value of
        ///       the component to a different value.
        ///    </para>
        /// </devdoc>
        public abstract void SetValue(object component, object value);
 
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class, indicates whether the
        ///       value of
        ///       this property needs to be persisted.
        ///    </para>
        /// </devdoc>
        public abstract bool ShouldSerializeValue(object component);
 
        /// <devdoc>
        ///     Indicates whether value change notifications for this property may originate from outside the property
        ///     descriptor, such as from the component itself (value=true), or whether notifications will only originate
        ///     from direct calls made to PropertyDescriptor.SetValue (value=false). For example, the component may
        ///     implement the INotifyPropertyChanged interface, or may have an explicit '{name}Changed' event for this property.
        /// </devdoc>
        public virtual bool SupportsChangeEvents {
            get {
                return false;
            }
        }
 
    }
}