e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
442 lines
16 KiB
C#
442 lines
16 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="AttributeCollection.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
/*
|
|
This class has the HostProtectionAttribute. The purpose of this attribute is to enforce host-specific programming model guidelines, not security behavior.
|
|
Suppress FxCop message - BUT REVISIT IF ADDING NEW SECURITY ATTRIBUTES.
|
|
*/
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields", Scope="type", Target="System.ComponentModel.AttributeCollection")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields", Scope="type", Target="System.ComponentModel.AttributeCollection")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Scope="member", Target="System.ComponentModel.AttributeCollection.CopyTo(System.Array,System.Int32):System.Void")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Scope="member", Target="System.ComponentModel.AttributeCollection.System.Collections.IEnumerable.GetEnumerator():System.Collections.IEnumerator")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Scope="member", Target="System.ComponentModel.AttributeCollection.System.Collections.ICollection.get_IsSynchronized():System.Boolean")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Scope="member", Target="System.ComponentModel.AttributeCollection.System.Collections.ICollection.get_Count():System.Int32")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Scope="member", Target="System.ComponentModel.AttributeCollection.System.Collections.ICollection.get_SyncRoot():System.Object")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection.Contains(System.Attribute):System.Boolean")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection.CopyTo(System.Array,System.Int32):System.Void")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection..ctor(System.Attribute[])")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection.GetEnumerator():System.Collections.IEnumerator")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection.get_Item(System.Type):System.Attribute")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection.get_Item(System.Int32):System.Attribute")]
|
|
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope="member", Target="System.ComponentModel.AttributeCollection.get_Count():System.Int32")]
|
|
|
|
namespace System.ComponentModel
|
|
{
|
|
|
|
using System.Reflection;
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
|
|
|
|
/// <devdoc>
|
|
/// Represents a collection of attributes.
|
|
/// </devdoc>
|
|
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
|
|
[System.Security.Permissions.HostProtection(Synchronization=true)]
|
|
public class AttributeCollection : ICollection
|
|
{
|
|
|
|
/// <devdoc>
|
|
/// An empty AttributeCollection that can used instead of creating a new one.
|
|
/// </devdoc>
|
|
public static readonly AttributeCollection Empty = new AttributeCollection((Attribute[])null);
|
|
private static Hashtable _defaultAttributes;
|
|
|
|
private Attribute[] _attributes;
|
|
|
|
private static object internalSyncObject = new object();
|
|
|
|
private struct AttributeEntry
|
|
{
|
|
public Type type;
|
|
public int index;
|
|
}
|
|
|
|
private const int FOUND_TYPES_LIMIT = 5;
|
|
|
|
private AttributeEntry[] _foundAttributeTypes;
|
|
|
|
private int _index = 0;
|
|
|
|
/// <devdoc>
|
|
/// Creates a new AttributeCollection.
|
|
/// </devdoc>
|
|
public AttributeCollection(params Attribute[] attributes)
|
|
{
|
|
if (attributes == null)
|
|
{
|
|
attributes = new Attribute[0];
|
|
}
|
|
_attributes = attributes;
|
|
|
|
for (int idx = 0; idx < attributes.Length; idx++)
|
|
{
|
|
if (attributes[idx] == null)
|
|
{
|
|
throw new ArgumentNullException("attributes");
|
|
}
|
|
}
|
|
}
|
|
|
|
protected AttributeCollection()
|
|
{
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Creates a new AttributeCollection from an existing AttributeCollection
|
|
/// </devdoc>
|
|
public static AttributeCollection FromExisting(AttributeCollection existing, params Attribute[] newAttributes)
|
|
{
|
|
// VSWhidbey #75418
|
|
// This method should be a constructor, but making it one introduces a breaking change.
|
|
//
|
|
if (existing == null)
|
|
{
|
|
throw new ArgumentNullException("existing");
|
|
}
|
|
|
|
if (newAttributes == null)
|
|
{
|
|
newAttributes = new Attribute[0];
|
|
}
|
|
|
|
Attribute[] newArray = new Attribute[existing.Count + newAttributes.Length];
|
|
int actualCount = existing.Count;
|
|
existing.CopyTo(newArray, 0);
|
|
|
|
for (int idx = 0; idx < newAttributes.Length; idx++)
|
|
{
|
|
if (newAttributes[idx] == null)
|
|
{
|
|
throw new ArgumentNullException("newAttributes");
|
|
}
|
|
|
|
// We must see if this attribute is already in the existing
|
|
// array. If it is, we replace it.
|
|
bool match = false;
|
|
for (int existingIdx = 0; existingIdx < existing.Count; existingIdx++)
|
|
{
|
|
if (newArray[existingIdx].TypeId.Equals(newAttributes[idx].TypeId))
|
|
{
|
|
match = true;
|
|
newArray[existingIdx] = newAttributes[idx];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match)
|
|
{
|
|
newArray[actualCount++] = newAttributes[idx];
|
|
}
|
|
}
|
|
|
|
// Now, if we collapsed some attributes, create a new array.
|
|
//
|
|
|
|
Attribute[] attributes = null;
|
|
if (actualCount < newArray.Length)
|
|
{
|
|
attributes = new Attribute[actualCount];
|
|
Array.Copy(newArray, 0, attributes, 0, actualCount);
|
|
}
|
|
else
|
|
{
|
|
attributes = newArray;
|
|
}
|
|
|
|
return new AttributeCollection(attributes);
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Gets the attributes collection.
|
|
/// </devdoc>
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays",
|
|
Justification = "Matches constructor input type")]
|
|
protected virtual Attribute[] Attributes
|
|
{
|
|
get
|
|
{
|
|
return _attributes;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Gets the number of attributes.
|
|
/// </devdoc>
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return Attributes.Length;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Gets the attribute with the specified index number.
|
|
/// </devdoc>
|
|
public virtual Attribute this[int index]
|
|
{
|
|
get
|
|
{
|
|
return Attributes[index];
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Gets the attribute with the specified type.
|
|
/// </devdoc>
|
|
public virtual Attribute this[Type attributeType]
|
|
{
|
|
get
|
|
{
|
|
lock (internalSyncObject) {
|
|
// 2 passes here for perf. Really! first pass, we just
|
|
// check equality, and if we don't find it, then we
|
|
// do the IsAssignableFrom dance. Turns out that's
|
|
// a relatively expensive call and we try to avoid it
|
|
// since we rarely encounter derived attribute types
|
|
// and this list is usually short.
|
|
//
|
|
if (_foundAttributeTypes == null)
|
|
{
|
|
_foundAttributeTypes = new AttributeEntry[FOUND_TYPES_LIMIT];
|
|
}
|
|
|
|
int ind = 0;
|
|
|
|
for (; ind < FOUND_TYPES_LIMIT; ind++)
|
|
{
|
|
if (_foundAttributeTypes[ind].type == attributeType)
|
|
{
|
|
int index = _foundAttributeTypes[ind].index;
|
|
if (index != -1)
|
|
{
|
|
return Attributes[index];
|
|
}
|
|
else{
|
|
return GetDefaultAttribute(attributeType);
|
|
}
|
|
}
|
|
if (_foundAttributeTypes[ind].type == null)
|
|
break;
|
|
}
|
|
|
|
ind = _index++;
|
|
|
|
if (_index >= FOUND_TYPES_LIMIT)
|
|
{
|
|
_index = 0;
|
|
}
|
|
|
|
_foundAttributeTypes[ind].type = attributeType;
|
|
|
|
int count = Attributes.Length;
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Attribute attribute = Attributes[i];
|
|
Type aType = attribute.GetType();
|
|
if (aType == attributeType)
|
|
{
|
|
_foundAttributeTypes[ind].index = i;
|
|
return attribute;
|
|
}
|
|
}
|
|
|
|
// now check the hierarchies.
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Attribute attribute = Attributes[i];
|
|
Type aType = attribute.GetType();
|
|
if (attributeType.IsAssignableFrom(aType))
|
|
{
|
|
_foundAttributeTypes[ind].index = i;
|
|
return attribute;
|
|
}
|
|
}
|
|
|
|
_foundAttributeTypes[ind].index = -1;
|
|
return GetDefaultAttribute(attributeType);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Determines if this collection of attributes has the specified attribute.
|
|
/// </devdoc>
|
|
public bool Contains(Attribute attribute)
|
|
{
|
|
Attribute attr = this[attribute.GetType()];
|
|
if (attr != null && attr.Equals(attribute))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Determines if this attribute collection contains the all
|
|
/// the specified attributes in the attribute array.
|
|
/// </devdoc>
|
|
public bool Contains(Attribute[] attributes)
|
|
{
|
|
|
|
if (attributes == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (int i = 0; i < attributes.Length; i++)
|
|
{
|
|
if (!Contains(attributes[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Returns the default value for an attribute. This uses the following hurestic:
|
|
/// 1. It looks for a public static field named "Default".
|
|
/// </devdoc>
|
|
protected Attribute GetDefaultAttribute(Type attributeType)
|
|
{
|
|
lock (internalSyncObject)
|
|
{
|
|
if (_defaultAttributes == null)
|
|
{
|
|
_defaultAttributes = new Hashtable();
|
|
}
|
|
|
|
// If we have already encountered this, use what's in the
|
|
// table.
|
|
if (_defaultAttributes.ContainsKey(attributeType))
|
|
{
|
|
return(Attribute)_defaultAttributes[attributeType];
|
|
}
|
|
|
|
Attribute attr = null;
|
|
|
|
// Nope, not in the table, so do the legwork to discover the default value.
|
|
Type reflect = TypeDescriptor.GetReflectionType(attributeType);
|
|
System.Reflection.FieldInfo field = reflect.GetField("Default", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField);
|
|
if (field != null && field.IsStatic)
|
|
{
|
|
attr = (Attribute)field.GetValue(null);
|
|
}
|
|
else
|
|
{
|
|
ConstructorInfo ci = reflect.UnderlyingSystemType.GetConstructor(new Type[0]);
|
|
if (ci != null)
|
|
{
|
|
attr = (Attribute)ci.Invoke(new object[0]);
|
|
|
|
// If we successfully created, verify that it is the
|
|
// default. Attributes don't have to abide by this rule.
|
|
if (!attr.IsDefaultAttribute())
|
|
{
|
|
attr = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
_defaultAttributes[attributeType] = attr;
|
|
return attr;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Gets an enumerator for this collection.
|
|
/// </devdoc>
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
return Attributes.GetEnumerator();
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Determines if a specified attribute is the same as an attribute
|
|
/// in the collection.
|
|
/// </devdoc>
|
|
public bool Matches(Attribute attribute)
|
|
{
|
|
for (int i = 0; i < Attributes.Length; i++)
|
|
{
|
|
if (Attributes[i].Match(attribute))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Determines if the attributes in the specified array are
|
|
/// the same as the attributes in the collection.
|
|
/// </devdoc>
|
|
public bool Matches(Attribute[] attributes)
|
|
{
|
|
for (int i = 0; i < attributes.Length; i++)
|
|
{
|
|
if (!Matches(attributes[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <internalonly/>
|
|
int ICollection.Count
|
|
{
|
|
get
|
|
{
|
|
return Count;
|
|
}
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
bool ICollection.IsSynchronized
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <internalonly/>
|
|
object ICollection.SyncRoot
|
|
{
|
|
get
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Copies this collection to an array.
|
|
/// </devdoc>
|
|
public void CopyTo(Array array, int index)
|
|
{
|
|
Array.Copy(Attributes, 0, array, index, Attributes.Length);
|
|
}
|
|
|
|
/// <internalonly/>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
}
|
|
}
|
|
|