File: AuthoringOM\Design\ReferenceService.cs
Project: ndp\cdf\src\WF\Common\System.Workflow.ComponentModel.csproj (System.Workflow.ComponentModel)
namespace System.Workflow.ComponentModel.Design
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Diagnostics;
    using System.Reflection;
    using System.Globalization;
 
    internal sealed class ReferenceService : IReferenceService, IDisposable
    {
        private static readonly Attribute[] Attributes = new Attribute[] { BrowsableAttribute.Yes };
        private IServiceProvider provider;   // service provider we use to get to other services
        private ArrayList addedComponents;   // list of newly added components
        private ArrayList removedComponents; // list of newly removed components
        private ArrayList changedComponents; // list of changed components, we will re-cylcle their references too
        private ArrayList references;        // our current list of references
 
        internal ReferenceService(IServiceProvider provider)
        {
            this.provider = provider;
        }
 
        ~ReferenceService()
        {
            Dispose(false);
        }
 
        private void CreateReferences(IComponent component)
        {
            CreateReferences(string.Empty, component, component);
        }
 
        private void CreateReferences(string trailingName, object reference, IComponent sitedComponent)
        {
            if (object.ReferenceEquals(reference, null))
                return;
 
            this.references.Add(new ReferenceHolder(trailingName, reference, sitedComponent));
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(reference, Attributes))
            {
                object value = null;
                try
                {
                    value = property.GetValue(reference);
                }
                catch
                {
                    // Work around!!! if an property getter throws exception then we ignore it.
                }
                if (value != null)
                {
                    BrowsableAttribute[] browsableAttrs = (BrowsableAttribute[])(value.GetType().GetCustomAttributes(typeof(BrowsableAttribute), true));
                    if (browsableAttrs.Length > 0 && browsableAttrs[0].Browsable)
                    {
                        CreateReferences(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { trailingName, property.Name }), property.GetValue(reference), sitedComponent);
                    }
                }
            }
        }
 
        private void EnsureReferences()
        {
            // If the references are null, create them for the first time and connect
            // up our events to listen to changes to the container.  Otherwise, check to
            // see if the added or removed lists contain anything for us to sync up.
            //
            if (this.references == null)
            {
                if (this.provider == null)
                {
                    throw new ObjectDisposedException("IReferenceService");
                }
 
                IComponentChangeService cs = this.provider.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
                Debug.Assert(cs != null, "Reference service relies on IComponentChangeService");
                if (cs != null)
                {
                    cs.ComponentAdded += new ComponentEventHandler(this.OnComponentAdded);
                    cs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved);
                    cs.ComponentRename += new ComponentRenameEventHandler(this.OnComponentRename);
                    cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
                }
 
                TypeDescriptor.Refreshed += new RefreshEventHandler(OnComponentRefreshed);
 
                IContainer container = this.provider.GetService(typeof(IContainer)) as IContainer;
                if (container == null)
                {
                    Debug.Fail("Reference service cannot operate without IContainer");
                    throw new InvalidOperationException();
                }
 
                this.references = new ArrayList(container.Components.Count);
 
                foreach (IComponent component in container.Components)
                {
                    CreateReferences(component);
                }
            }
            else
            {
                if (this.addedComponents != null && this.addedComponents.Count > 0)
                {
                    // There is a possibility that this component already exists.
                    // If it does, just remove it first and then re-add it.
                    //
                    ArrayList clonedAddedComponents = new ArrayList(this.addedComponents);
                    foreach (IComponent ic in clonedAddedComponents)
                    {
                        RemoveReferences(ic);
                        CreateReferences(ic);
                    }
                    this.addedComponents.Clear();
                }
 
                if (this.removedComponents != null && this.removedComponents.Count > 0)
                {
                    ArrayList clonedRemovedComponents = new ArrayList(this.removedComponents);
                    foreach (IComponent ic in clonedRemovedComponents)
                        RemoveReferences(ic);
 
                    this.removedComponents.Clear();
                }
                if (this.changedComponents != null && this.changedComponents.Count > 0)
                {
                    ArrayList clonedChangedComponents = new ArrayList(this.changedComponents);
                    foreach (IComponent ic in clonedChangedComponents)
                    {
                        RemoveReferences(ic);
                        CreateReferences(ic);
                    }
                    this.changedComponents.Clear();
                }
            }
        }
 
        private void OnComponentChanged(object sender, ComponentChangedEventArgs cevent)
        {
            IComponent comp = ((IReferenceService)this).GetComponent(cevent.Component);
 
            if (comp != null)
            {
                if ((this.addedComponents == null || !this.addedComponents.Contains(comp)) &&
                    (this.removedComponents == null || !this.removedComponents.Contains(comp)))
                {
                    if (this.changedComponents == null)
                    {
                        this.changedComponents = new ArrayList();
                        this.changedComponents.Add(comp);
                    }
                    else if (!this.changedComponents.Contains(comp))
                    {
                        this.changedComponents.Add(comp);
                    }
                }
            }
        }
 
        private void OnComponentAdded(object sender, ComponentEventArgs cevent)
        {
            if (this.addedComponents == null)
                this.addedComponents = new ArrayList();
            this.addedComponents.Add(cevent.Component);
            if (this.removedComponents != null)
                this.removedComponents.Remove(cevent.Component);
            if (this.changedComponents != null)
                this.changedComponents.Remove(cevent.Component);
        }
 
        private void OnComponentRemoved(object sender, ComponentEventArgs cevent)
        {
            if (this.removedComponents == null)
                this.removedComponents = new ArrayList();
 
            this.removedComponents.Add(cevent.Component);
 
            if (this.addedComponents != null)
                this.addedComponents.Remove(cevent.Component);
            if (this.changedComponents != null)
                this.changedComponents.Remove(cevent.Component);
        }
 
        private void OnComponentRename(object sender, ComponentRenameEventArgs cevent)
        {
            foreach (ReferenceHolder reference in this.references)
            {
                if (object.ReferenceEquals(reference.SitedComponent, cevent.Component))
                {
                    reference.ResetName();
                    return;
                }
            }
        }
        private void OnComponentRefreshed(RefreshEventArgs e)
        {
            if (e.ComponentChanged != null)
                OnComponentChanged(this, new ComponentChangedEventArgs(e.ComponentChanged, null, null, null));
        }
 
        private void RemoveReferences(IComponent component)
        {
            if (this.references != null)
            {
                int size = this.references.Count;
                for (int i = size - 1; i >= 0; i--)
                {
                    if (object.ReferenceEquals(((ReferenceHolder)this.references[i]).SitedComponent, component))
                        this.references.RemoveAt(i);
                }
            }
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        private void Dispose(bool disposing)
        {
            if (this.references != null && this.provider != null)
            {
                IComponentChangeService cs = this.provider.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
                if (cs != null)
                {
                    cs.ComponentAdded -= new ComponentEventHandler(this.OnComponentAdded);
                    cs.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemoved);
                    cs.ComponentRename -= new ComponentRenameEventHandler(this.OnComponentRename);
                    cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
                }
 
                TypeDescriptor.Refreshed -= new RefreshEventHandler(OnComponentRefreshed);
 
                this.references = null;
                this.provider = null;
            }
        }
 
        IComponent IReferenceService.GetComponent(object reference)
        {
            if (object.ReferenceEquals(reference, null))
                throw new ArgumentNullException("reference");
 
            EnsureReferences();
 
            foreach (ReferenceHolder holder in this.references)
            {
                if (object.ReferenceEquals(holder.Reference, reference))
                    return holder.SitedComponent;
            }
            return null;
        }
        string IReferenceService.GetName(object reference)
        {
            if (object.ReferenceEquals(reference, null))
                throw new ArgumentNullException("reference");
 
            EnsureReferences();
 
            foreach (ReferenceHolder holder in this.references)
            {
                if (object.ReferenceEquals(holder.Reference, reference))
                    return holder.Name;
            }
            return null;
        }
        object IReferenceService.GetReference(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");
 
            EnsureReferences();
 
            foreach (ReferenceHolder holder in this.references)
            {
                if (string.Equals(holder.Name, name, StringComparison.OrdinalIgnoreCase))
                    return holder.Reference;
            }
 
            return null;
        }
 
        object[] IReferenceService.GetReferences()
        {
            EnsureReferences();
 
            object[] references = new object[this.references.Count];
            for (int i = 0; i < references.Length; i++)
                references[i] = ((ReferenceHolder)this.references[i]).Reference;
 
            return references;
        }
 
        object[] IReferenceService.GetReferences(Type baseType)
        {
            if (baseType == null)
                throw new ArgumentNullException("baseType");
 
            EnsureReferences();
 
            ArrayList results = new ArrayList(this.references.Count);
            foreach (ReferenceHolder holder in this.references)
            {
                object reference = holder.Reference;
                if (baseType.IsAssignableFrom(reference.GetType()))
                    results.Add(reference);
            }
 
            object[] references = new object[results.Count];
            results.CopyTo(references, 0);
            return references;
        }
 
        private sealed class ReferenceHolder
        {
            private string trailingName;
            private object reference;
            private IComponent sitedComponent;
            private string fullName;
 
            internal ReferenceHolder(string trailingName, object reference, IComponent sitedComponent)
            {
                this.trailingName = trailingName;
                this.reference = reference;
                this.sitedComponent = sitedComponent;
 
                Debug.Assert(trailingName != null, "Expected a trailing name");
                Debug.Assert(reference != null, "Expected a reference");
 
#if DEBUG

                Debug.Assert(sitedComponent != null, "Expected a sited component");
                if (sitedComponent != null)
                    Debug.Assert(sitedComponent.Site != null, "Sited component is not really sited: " + sitedComponent.ToString());
                if (sitedComponent != null && sitedComponent.Site != null)
                    Debug.Assert(sitedComponent.Site.Name != null, "Sited component has no name: " + sitedComponent.ToString());
 
#endif // DEBUG
            }
 
            internal void ResetName()
            {
                this.fullName = null;
            }
 
            internal string Name
            {
                get
                {
                    if (this.fullName == null)
                    {
                        if (this.sitedComponent != null && this.sitedComponent.Site != null && this.sitedComponent.Site.Name != null)
                        {
                            this.fullName = string.Format(CultureInfo.InvariantCulture, "{0}{1}", new object[] { this.sitedComponent.Site.Name, this.trailingName });
                        }
                        else
                        {
#if DEBUG
                            Debug.Assert(this.sitedComponent != null, "Expected a sited component");
                            if (this.sitedComponent != null)
                                Debug.Assert(this.sitedComponent.Site != null, "Sited component is not really sited: " + this.sitedComponent.ToString());
                            if (this.sitedComponent != null && this.sitedComponent.Site != null)
                                Debug.Assert(this.sitedComponent.Site.Name != null, "Sited component has no name: " + this.sitedComponent.ToString());
#endif // DEBUG
                            this.fullName = string.Empty;
                        }
                    }
                    return this.fullName;
                }
            }
 
            internal object Reference
            {
                get
                {
                    return this.reference;
                }
            }
 
            internal IComponent SitedComponent
            {
                get
                {
                    return this.sitedComponent;
                }
            }
        }
    }
}