/// <devdoc>
        ///     Metdata filtering is the third stage of our pipeline.  
        ///     In this stage we check to see if the given object is a
        ///     sited component that provides the ITypeDescriptorFilterService
        ///     object.  If it does, we allow the TDS to filter the metadata.
        ///     This will use the cache, if available, to store filtered
        ///     metdata.
        /// </devdoc>
        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(_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[_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)
            {
                Trace("Pipeline : Filter needs to create member list for {0}", instance.GetType().Name);
                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.GetString(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.GetString(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.GetString(SR.TypeDescriptorExpectedElementType, typeof(EventDescriptor).FullName));
                        }
                        cacheValue = new EventDescriptorCollection(eventArray, true);
                        break;

                    default:
                        Debug.Fail("unknown pipeline type");
                        cacheValue = null;
                        break;
                }

                Trace("Pipeline : Filter results being cached for {0}", instance.GetType().Name);

                FilterCacheItem cacheItem = new FilterCacheItem(componentFilter, cacheValue);
                cache[_pipelineFilterKeys[pipelineType]] = cacheItem;
                cache.Remove(_pipelineAttributeFilterKeys[pipelineType]);
            }

            return list;
        }
        private static ICollection PipelineFilter(int pipelineType, ICollection members, object instance, IDictionary cache)
        {
            bool flag;
            IComponent component = instance as IComponent;
            ITypeDescriptorFilterService filterService = null;
            if (component != null)
            {
                ISite site = component.Site;
                if (site != null)
                {
                    filterService = site.GetService(typeof(ITypeDescriptorFilterService)) as ITypeDescriptorFilterService;
                }
            }
            IList list = members as ArrayList;
            if (filterService == null)
            {
                return members;
            }
            if ((cache != null) && ((list == null) || list.IsReadOnly))
            {
                FilterCacheItem item = cache[_pipelineFilterKeys[pipelineType]] as FilterCacheItem;
                if ((item != null) && item.IsValid(filterService))
                {
                    return item.FilteredMembers;
                }
            }
            OrderedDictionary attributes = new OrderedDictionary(members.Count);
            switch (pipelineType)
            {
                case 0:
                    foreach (Attribute attribute in members)
                    {
                        attributes[attribute.TypeId] = attribute;
                    }
                    flag = filterService.FilterAttributes(component, attributes);
                    break;

                case 1:
                case 2:
                    foreach (MemberDescriptor descriptor in members)
                    {
                        string name = descriptor.Name;
                        if (attributes.Contains(name))
                        {
                            string extenderCollisionSuffix = GetExtenderCollisionSuffix(descriptor);
                            if (extenderCollisionSuffix != null)
                            {
                                attributes[name + extenderCollisionSuffix] = descriptor;
                            }
                            MemberDescriptor member = (MemberDescriptor) attributes[name];
                            extenderCollisionSuffix = GetExtenderCollisionSuffix(member);
                            if (extenderCollisionSuffix != null)
                            {
                                attributes.Remove(name);
                                attributes[member.Name + extenderCollisionSuffix] = member;
                            }
                        }
                        else
                        {
                            attributes[name] = descriptor;
                        }
                    }
                    if (pipelineType == 1)
                    {
                        flag = filterService.FilterProperties(component, attributes);
                    }
                    else
                    {
                        flag = filterService.FilterEvents(component, attributes);
                    }
                    break;

                default:
                    flag = false;
                    break;
            }
            if ((list == null) || list.IsReadOnly)
            {
                list = new ArrayList(attributes.Values);
            }
            else
            {
                list.Clear();
                foreach (object obj2 in attributes.Values)
                {
                    list.Add(obj2);
                }
            }
            if (flag && (cache != null))
            {
                ICollection is2;
                switch (pipelineType)
                {
                    case 0:
                    {
                        Attribute[] array = new Attribute[list.Count];
                        try
                        {
                            list.CopyTo(array, 0);
                        }
                        catch (InvalidCastException)
                        {
                            throw new ArgumentException(SR.GetString("TypeDescriptorExpectedElementType", new object[] { typeof(Attribute).FullName }));
                        }
                        is2 = new AttributeCollection(array);
                        break;
                    }
                    case 1:
                    {
                        PropertyDescriptor[] descriptorArray = new PropertyDescriptor[list.Count];
                        try
                        {
                            list.CopyTo(descriptorArray, 0);
                        }
                        catch (InvalidCastException)
                        {
                            throw new ArgumentException(SR.GetString("TypeDescriptorExpectedElementType", new object[] { typeof(PropertyDescriptor).FullName }));
                        }
                        is2 = new PropertyDescriptorCollection(descriptorArray, true);
                        break;
                    }
                    case 2:
                    {
                        EventDescriptor[] descriptorArray2 = new EventDescriptor[list.Count];
                        try
                        {
                            list.CopyTo(descriptorArray2, 0);
                        }
                        catch (InvalidCastException)
                        {
                            throw new ArgumentException(SR.GetString("TypeDescriptorExpectedElementType", new object[] { typeof(EventDescriptor).FullName }));
                        }
                        is2 = new EventDescriptorCollection(descriptorArray2, true);
                        break;
                    }
                    default:
                        is2 = null;
                        break;
                }
                FilterCacheItem item2 = new FilterCacheItem(filterService, is2);
                cache[_pipelineFilterKeys[pipelineType]] = item2;
                cache.Remove(_pipelineAttributeFilterKeys[pipelineType]);
            }
            return list;
        }