private static ICollection PipelineFilter(int pipelineType, ICollection members, object instance, IDictionary cache)
{
IComponent component = instance as IComponent;
ITypeDescriptorFilterService componentFilter = null;
if (component != null)
{
ISite site = component.Site;
if (site != null)
{
componentFilter = site.GetService(typeof(ITypeDescriptorFilterService)) as ITypeDescriptorFilterService;
}
}
// If we have no filter, there is nothing for us to do.
//
IList list = members as ArrayList;
if (componentFilter == null)
{
Debug.Assert(cache == null || list == null || !cache.Contains(s_pipelineFilterKeys[pipelineType]), "Earlier pipeline stage should have removed our cache");
return members;
}
// Now, check our cache. The cache state is only valid
// if the data coming into us is read-only. If it is read-write,
// that means something higher in the pipeline has already changed
// it so we must recompute anyway.
//
if (cache != null && (list == null || list.IsReadOnly))
{
FilterCacheItem cacheItem = cache[s_pipelineFilterKeys[pipelineType]] as FilterCacheItem;
if (cacheItem != null && cacheItem.IsValid(componentFilter))
{
return cacheItem.FilteredMembers;
}
}
// Cache either is dirty or doesn't exist. Re-filter the members.
// We need to build an IDictionary of key->value pairs and invoke
// Filter* on the filter service.
//
OrderedDictionary filterTable = new OrderedDictionary(members.Count);
bool cacheResults;
switch (pipelineType)
{
case PIPELINE_ATTRIBUTES:
foreach (Attribute attr in members)
{
filterTable[attr.TypeId] = attr;
}
cacheResults = componentFilter.FilterAttributes(component, filterTable);
break;
case PIPELINE_PROPERTIES:
case PIPELINE_EVENTS:
foreach (MemberDescriptor desc in members)
{
string descName = desc.Name;
// We must handle the case of duplicate property names
// because extender providers can provide any arbitrary
// name. Our rule for this is simple: If we find a
// duplicate name, resolve it back to the extender
// provider that offered it and append "_" + the
// provider name. If the provider has no name,
// then append the object hash code.
//
if (filterTable.Contains(descName))
{
// First, handle the new property. Because
// of the order in which we added extended
// properties earlier in the pipeline, we can be
// sure that the new property is an extender. We
// cannot be sure that the existing property
// in the table is an extender, so we will
// have to check.
//
string suffix = GetExtenderCollisionSuffix(desc);
Debug.Assert(suffix != null, "Name collision with non-extender property.");
if (suffix != null)
{
filterTable[descName + suffix] = desc;
}
// Now, handle the original property.
//
MemberDescriptor origDesc = (MemberDescriptor)filterTable[descName];
suffix = GetExtenderCollisionSuffix(origDesc);
if (suffix != null)
{
filterTable.Remove(descName);
filterTable[origDesc.Name + suffix] = origDesc;
}
}
else
{
filterTable[descName] = desc;
}
}
if (pipelineType == PIPELINE_PROPERTIES)
{
cacheResults = componentFilter.FilterProperties(component, filterTable);
}
else
{
cacheResults = componentFilter.FilterEvents(component, filterTable);
}
break;
default:
Debug.Fail("unknown pipeline type");
cacheResults = false;
break;
}
// See if we can re-use the IList were were passed. If we can,
// it is more efficient to re-use its slots than to generate new ones.
//
if (list == null || list.IsReadOnly)
{
list = new ArrayList(filterTable.Values);
}
else
{
list.Clear();
foreach (object obj in filterTable.Values)
{
list.Add(obj);
}
}
// Component filter has requested that we cache these
// new changes. We store them as a correctly typed collection
// so on successive invocations we can simply return. Note that
// we always return the IList so that successive stages in the
// pipeline can modify it.
//
if (cacheResults && cache != null)
{
ICollection cacheValue;
switch (pipelineType)
{
case PIPELINE_ATTRIBUTES:
Attribute[] attrArray = new Attribute[list.Count];
try
{
list.CopyTo(attrArray, 0);
}
catch (InvalidCastException)
{
throw new ArgumentException(SR.GetResourceString(SR.TypeDescriptorExpectedElementType, typeof(Attribute).FullName));
}
cacheValue = new AttributeCollection(attrArray);
break;
case PIPELINE_PROPERTIES:
PropertyDescriptor[] propArray = new PropertyDescriptor[list.Count];
try
{
list.CopyTo(propArray, 0);
}
catch (InvalidCastException)
{
throw new ArgumentException(SR.GetResourceString(SR.TypeDescriptorExpectedElementType, typeof(PropertyDescriptor).FullName));
}
cacheValue = new PropertyDescriptorCollection(propArray, true);
break;
case PIPELINE_EVENTS:
EventDescriptor[] eventArray = new EventDescriptor[list.Count];
try
{
list.CopyTo(eventArray, 0);
}
catch (InvalidCastException)
{
throw new ArgumentException(SR.GetResourceString(SR.TypeDescriptorExpectedElementType, typeof(EventDescriptor).FullName));
}
cacheValue = new EventDescriptorCollection(eventArray, true);
break;
default:
Debug.Fail("unknown pipeline type");
cacheValue = null;
break;
}
FilterCacheItem cacheItem = new FilterCacheItem(componentFilter, cacheValue);
cache[s_pipelineFilterKeys[pipelineType]] = cacheItem;
cache.Remove(s_pipelineAttributeFilterKeys[pipelineType]);
}
return list;
}