|
//------------------------------------------------------------------------------
// <copyright file="MemberDescriptor.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.ComponentModel {
using Microsoft.Win32;
using System;
using System.Collections;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Reflection;
using System.Security.Permissions;
/// <devdoc>
/// <para>
/// Declares an array of attributes for a member and defines
/// the properties and methods that give you access to the attributes in the array.
/// All attributes must derive from <see cref='System.Attribute'/>.
/// </para>
/// </devdoc>
[HostProtection(SharedState = true)]
[System.Runtime.InteropServices.ComVisible(true)]
public abstract class MemberDescriptor {
private string name;
private string displayName;
private int nameHash;
private AttributeCollection attributeCollection;
private Attribute[] attributes;
private Attribute[] originalAttributes;
private bool attributesFiltered = false;
private bool attributesFilled = false;
private int metadataVersion;
private string category;
private string description;
private object lockCookie = new object();
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.ComponentModel.MemberDescriptor'/> class with the specified <paramref name="name
/// "/> and no
/// attributes.
/// </para>
/// </devdoc>
protected MemberDescriptor(string name) : this(name, null) {
}
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.ComponentModel.MemberDescriptor'/> class with the specified <paramref name="name"/>
/// and <paramref name="attributes "/>
/// array.
/// </para>
/// </devdoc>
protected MemberDescriptor(string name, Attribute[] attributes) {
try {
if (name == null || name.Length == 0) {
throw new ArgumentException(SR.GetString(SR.InvalidMemberName));
}
this.name = name;
this.displayName = name;
this.nameHash = name.GetHashCode();
if (attributes != null) {
this.attributes = attributes;
attributesFiltered = false;
}
this.originalAttributes = this.attributes;
}
catch (Exception t) {
Debug.Fail(t.ToString());
throw t;
}
}
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.ComponentModel.MemberDescriptor'/> class with the specified <see cref='System.ComponentModel.MemberDescriptor'/>.
/// </para>
/// </devdoc>
protected MemberDescriptor(MemberDescriptor descr) {
this.name = descr.Name;
this.displayName = this.name;
this.nameHash = name.GetHashCode();
this.attributes = new Attribute[descr.Attributes.Count];
descr.Attributes.CopyTo(this.attributes, 0);
attributesFiltered = true;
this.originalAttributes = this.attributes;
}
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.ComponentModel.MemberDescriptor'/> class with the name in the specified
/// <see cref='System.ComponentModel.MemberDescriptor'/> and the attributes
/// in both the old <see cref='System.ComponentModel.MemberDescriptor'/> and the <see cref='System.Attribute'/> array.
/// </para>
/// </devdoc>
protected MemberDescriptor(MemberDescriptor oldMemberDescriptor, Attribute[] newAttributes) {
this.name = oldMemberDescriptor.Name;
this.displayName = oldMemberDescriptor.DisplayName;
this.nameHash = name.GetHashCode();
ArrayList newArray = new ArrayList();
if (oldMemberDescriptor.Attributes.Count != 0) {
foreach (object o in oldMemberDescriptor.Attributes) {
newArray.Add(o);
}
}
if (newAttributes != null) {
foreach (object o in newAttributes) {
newArray.Add(o);
}
}
this.attributes = new Attribute[ newArray.Count ];
newArray.CopyTo( this.attributes, 0);
attributesFiltered = false;
this.originalAttributes = this.attributes;
}
/// <devdoc>
/// <para>
/// Gets or sets an array of
/// attributes.
/// </para>
/// </devdoc>
protected virtual Attribute[] AttributeArray {
get {
CheckAttributesValid();
FilterAttributesIfNeeded();
return attributes;
}
set {
lock(lockCookie) {
attributes = value;
originalAttributes = value;
attributesFiltered = false;
attributeCollection = null;
}
}
}
/// <devdoc>
/// <para>
/// Gets the collection of attributes for this member.
/// </para>
/// </devdoc>
public virtual AttributeCollection Attributes {
get {
CheckAttributesValid();
AttributeCollection attrs = attributeCollection;
if (attrs == null) {
lock(lockCookie) {
attrs = CreateAttributeCollection();
attributeCollection = attrs;
}
}
return attrs;
}
}
/// <devdoc>
/// <para>
/// Gets the name of the category that the
/// member
/// belongs to, as specified in the <see cref='System.ComponentModel.CategoryAttribute'/>.
/// </para>
/// </devdoc>
public virtual string Category {
get {
if (category == null) {
category = ((CategoryAttribute)Attributes[typeof(CategoryAttribute)]).Category;
}
return category;
}
}
/// <devdoc>
/// <para>
/// Gets the description of
/// the member as specified in the <see cref='System.ComponentModel.DescriptionAttribute'/>.
/// </para>
/// </devdoc>
public virtual string Description {
get {
if (description == null) {
description = ((DescriptionAttribute) Attributes[typeof(DescriptionAttribute)]).Description;
}
return description;
}
}
/// <devdoc>
/// <para>
/// Gets a value indicating whether the member is browsable as specified in the
/// <see cref='System.ComponentModel.BrowsableAttribute'/>.
/// </para>
/// </devdoc>
public virtual bool IsBrowsable {
get {
return((BrowsableAttribute)Attributes[typeof(BrowsableAttribute)]).Browsable;
}
}
/// <devdoc>
/// <para>
/// Gets the
/// name of the member.
/// </para>
/// </devdoc>
public virtual string Name {
get {
if (name == null) {
return "";
}
return name;
}
}
/// <devdoc>
/// <para>
/// Gets the hash
/// code for the name of the member as specified in <see cref='System.String.GetHashCode'/>.
/// </para>
/// </devdoc>
protected virtual int NameHashCode {
get {
return nameHash;
}
}
/// <devdoc>
/// <para>
/// Determines whether this member should be set only at
/// design time as specified in the <see cref='System.ComponentModel.DesignOnlyAttribute'/>.
/// </para>
/// </devdoc>
public virtual bool DesignTimeOnly {
get {
return(DesignOnlyAttribute.Yes.Equals(Attributes[typeof(DesignOnlyAttribute)]));
}
}
/// <devdoc>
/// <para>
/// Gets the name that can be displayed in a window like a
/// properties window.
/// </para>
/// </devdoc>
public virtual string DisplayName {
get {
DisplayNameAttribute displayNameAttr = Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
if (displayNameAttr == null || displayNameAttr.IsDefaultAttribute()) {
return displayName;
}
return displayNameAttr.DisplayName;
}
}
/// <devdoc>
/// Called each time we access the attribtes on
/// this member descriptor to give deriving classes
/// a chance to change them on the fly.
/// </devdoc>
private void CheckAttributesValid() {
if (attributesFiltered) {
if (metadataVersion != TypeDescriptor.MetadataVersion) {
attributesFilled = false;
attributesFiltered = false;
attributeCollection = null;
}
}
}
/// <include file='doc\MemberDescriptor.uex' path='docs/doc[@for="MemberDescriptor.CreateAttributeCollection"]/*' />
/// <devdoc>
/// <para>
/// Creates a collection of attributes using the
/// array of attributes that you passed to the constructor.
/// </para>
/// </devdoc>
protected virtual AttributeCollection CreateAttributeCollection() {
return new AttributeCollection(AttributeArray);
}
/// <devdoc>
/// <para>
/// Compares this instance to the specified <see cref='System.ComponentModel.MemberDescriptor'/> 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) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj.GetType() != GetType()) {
return false;
}
MemberDescriptor mdObj = (MemberDescriptor)obj;
FilterAttributesIfNeeded();
mdObj.FilterAttributesIfNeeded();
if (mdObj.nameHash != nameHash) {
return false;
}
if ((mdObj.category == null) != (category == null) ||
(category != null && !mdObj.category.Equals(category))) {
return false;
}
// VSO 149471 - Technically fixing this could cause a behavior change, so we are
// adding a quirk in case anyone is bit by this and needs the old, buggy behavior.
if (!LocalAppContextSwitches.MemberDescriptorEqualsReturnsFalseIfEquivalent) {
if ((mdObj.description == null) != (description == null) ||
(description != null && !mdObj.description.Equals(description))) {
return false;
}
}
else {
if ((mdObj.description == null) != (description == null) ||
(description != null && !mdObj.category.Equals(description))) {
return false;
}
}
if ((mdObj.attributes == null) != (attributes == null)) {
return false;
}
bool sameAttrs = true;
if (attributes != null) {
if (attributes.Length != mdObj.attributes.Length) {
return false;
}
for (int i = 0; i < attributes.Length; i++) {
if (!attributes[i].Equals(mdObj.attributes[i])) {
sameAttrs = false;
break;
}
}
}
return sameAttrs;
}
/// <devdoc>
/// <para>
/// 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.
/// </para>
/// </devdoc>
protected virtual void FillAttributes(IList attributeList) {
if (originalAttributes != null) {
foreach (Attribute attr in originalAttributes) {
attributeList.Add(attr);
}
}
}
private void FilterAttributesIfNeeded() {
if (!attributesFiltered) {
IList list;
if (!attributesFilled) {
list = new ArrayList();
try {
FillAttributes(list);
}
catch (System.Threading.ThreadAbortException) {
throw;
}
catch (Exception e) {
Debug.Fail(name + ">>" + e.ToString());
}
}
else {
list = new ArrayList(attributes);
}
Hashtable hash = new Hashtable(list.Count);
foreach (Attribute attr in list) {
hash[attr.TypeId] = attr;
}
Attribute[] newAttributes = new Attribute[hash.Values.Count];
hash.Values.CopyTo(newAttributes, 0);
lock(lockCookie) {
attributes = newAttributes;
attributesFiltered = true;
attributesFilled = true;
metadataVersion = TypeDescriptor.MetadataVersion;
}
}
}
/// <devdoc>
/// <para>
/// Finds the given method through reflection. This method only looks for public methods.
/// </para>
/// </devdoc>
protected static MethodInfo FindMethod(Type componentClass, string name, Type[] args, Type returnType) {
return FindMethod(componentClass, name, args, returnType, true);
}
/// <devdoc>
/// <para>
/// Finds the given method through reflection.
/// </para>
/// </devdoc>
protected static MethodInfo FindMethod(Type componentClass, string name, Type[] args, Type returnType, bool publicOnly) {
MethodInfo result = null;
if (publicOnly) {
result = componentClass.GetMethod(name, args);
}
else {
result = componentClass.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
}
if (result != null && !result.ReturnType.IsEquivalentTo(returnType)) {
result = null;
}
return result;
}
/// <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 nameHash;
}
/// <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 virtual object GetInvocationTarget(Type type, object instance) {
if (type == null)
{
throw new ArgumentNullException("type");
}
if (instance == null)
{
throw new ArgumentNullException("instance");
}
return TypeDescriptor.GetAssociation(type, instance);
}
/// <devdoc>
/// <para>
/// Gets a component site
/// for the given component.
/// </para>
/// </devdoc>
protected static ISite GetSite(object component) {
if (!(component is IComponent)) {
return null;
}
return((IComponent)component).Site;
}
/// <devdoc>
/// <para>
/// Gets
/// the component
/// that a method should be invoked on.
/// </para>
/// </devdoc>
[Obsolete("This method has been deprecated. Use GetInvocationTarget instead. http://go.microsoft.com/fwlink/?linkid=14202")]
protected static object GetInvokee(Type componentClass, object component) {
if (componentClass == null)
{
throw new ArgumentNullException("componentClass");
}
if (component == null)
{
throw new ArgumentNullException("component");
}
return TypeDescriptor.GetAssociation(componentClass, component);
}
}
}
|