File: System.Activities.Presentation\System\Activities\Presentation\Model\ModelItemCollectionImpl.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.Activities.Presentation.Model
{
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Markup;
    using System.Activities.Presentation.Services;
    using System.Linq;
    using System.Runtime;
 
    // This class provides the implementation for the ModelItemCollection. This provides
    // a container for the child modelItems for the entries in a collection.
 
    class ModelItemCollectionImpl : ModelItemCollection, IModelTreeItem
    {
        ModelProperty contentProperty;
        object instance;
        Type itemType;
        List<ModelItem> modelItems;
        Dictionary<string, ModelItem> modelPropertyStore;
        ModelTreeManager modelTreeManager;
        ModelProperty nameProperty;
        List<ModelItem> parents;
        ModelPropertyCollectionImpl properties;
        List<ModelProperty> sources;
        ModelTreeItemHelper helper;
        List<ModelItem> subTreeNodesThatNeedBackLinkPatching;
        DependencyObject view;
        ModelItem manuallySetParent;
 
        public ModelItemCollectionImpl(ModelTreeManager modelTreeManager, Type itemType, Object instance, ModelItem parent)
        {
            Fx.Assert(modelTreeManager != null, "modelTreeManager cannot be null");
            Fx.Assert(itemType != null, "item type cannot be null");
            Fx.Assert(instance != null, "instance cannot be null");
            this.itemType = itemType;
            this.instance = instance;
            this.modelTreeManager = modelTreeManager;
            this.parents = new List<ModelItem>(1);
            this.sources = new List<ModelProperty>(1);
            this.helper = new ModelTreeItemHelper();
            if (parent != null)
            {
                this.manuallySetParent = parent;
            }
            this.modelPropertyStore = new Dictionary<string, ModelItem>();
            this.subTreeNodesThatNeedBackLinkPatching = new List<ModelItem>();
            this.modelItems = new List<ModelItem>();
            UpdateInstance(instance);
        }
 
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
 
        public override event PropertyChangedEventHandler PropertyChanged;
 
        public override AttributeCollection Attributes
        {
            get
            {
                Fx.Assert(this.itemType != null, "item type cannot be null");
                return TypeDescriptor.GetAttributes(this.itemType);
            }
        }
 
        public override ModelProperty Content
        {
            get
            {
                if (this.contentProperty == null)
                {
                    Fx.Assert(this.instance != null, "instance cannot be null");
                    ContentPropertyAttribute contentAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(ContentPropertyAttribute)] as ContentPropertyAttribute;
                    if (contentAttribute != null && !String.IsNullOrEmpty(contentAttribute.Name))
                    {
                        this.contentProperty = this.Properties.Find(contentAttribute.Name);
                    }
                }
                return contentProperty;
            }
        }
 
        public override int Count
        {
            get
            {
                Fx.Assert(instance != null, "instance cannot be null");
                if (instance != null)
                {
                    return ((ICollection)instance).Count;
                }
                return 0;
            }
        }
 
        public override bool IsReadOnly
        {
            get
            {
                IList instanceList = instance as IList;
                Fx.Assert(instanceList != null, "instance should be IList");
                return instanceList.IsReadOnly;
            }
        }
 
        public override Type ItemType
        {
            get
            {
                return this.itemType;
            }
        }
 
        public override string Name
        {
            get
            {
                string name = null;
                if ((this.NameProperty != null) && (this.NameProperty.Value != null))
                {
                    name = (string)this.NameProperty.Value.GetCurrentValue();
                }
                return name;
            }
            set
            {
                this.NameProperty.SetValue(value);
            }
        }
 
        public override ModelItem Parent
        {
            get
            {
                return (this.Parents.Count() > 0) ? this.Parents.First() : null;
            }
 
        }
 
        public override ModelPropertyCollection Properties
        {
            get
            {
                if (this.properties == null)
                {
                    properties = new ModelPropertyCollectionImpl(this);
                }
                return properties;
            }
        }
 
        public override ModelItem Root
        {
            get
            {
                return this.modelTreeManager.Root;
            }
        }
 
        public override ModelProperty Source
        {
            get
            {
                return (this.sources.Count > 0) ? this.sources.First() : null;
            }
        }
 
        public override DependencyObject View
        {
            get
            {
                return this.view;
            }
        }
 
        public override IEnumerable<ModelItem> Parents
        {
            get
            {
                if (this.manuallySetParent != null)
                {
                    List<ModelItem> list = new List<ModelItem>();
                    list.Add(this.manuallySetParent);
                    return list.Concat(this.parents).Concat(
                        from source in this.sources
                        select source.Parent);
                }
 
                return this.parents.Concat(
                    from source in this.sources
                    select source.Parent);
            }
        }
 
        public override IEnumerable<ModelProperty> Sources
        {
            get
            {
                return this.sources;
            }
        }
 
        internal List<ModelItem> Items
        {
            get
            {
                return modelItems;
            }
        }
 
        internal Dictionary<string, ModelItem> ModelPropertyStore
        {
            get
            {
                return modelPropertyStore;
            }
        }
 
        protected ModelProperty NameProperty
        {
            get
            {
                if (this.nameProperty == null)
                {
                    Fx.Assert(this.instance != null, "instance cannot be null");
                    RuntimeNamePropertyAttribute runtimeNamePropertyAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(RuntimeNamePropertyAttribute)] as RuntimeNamePropertyAttribute;
                    if (runtimeNamePropertyAttribute != null && !String.IsNullOrEmpty(runtimeNamePropertyAttribute.Name))
                    {
                        this.nameProperty = this.Properties.Find(runtimeNamePropertyAttribute.Name);
                    }
                }
                return nameProperty;
            }
        }
 
        ModelItem IModelTreeItem.ModelItem
        {
            get
            {
                return this;
            }
        }
 
        Dictionary<string, ModelItem> IModelTreeItem.ModelPropertyStore
        {
            get
            {
                return modelPropertyStore;
            }
        }
 
 
        ModelTreeManager IModelTreeItem.ModelTreeManager
        {
            get
            {
                return modelTreeManager;
            }
        }
 
        public override ModelItem this[int index]
        {
            get
            {
                return this.modelItems[index];
            }
            set
            {
                this.Insert(index, value);
            }
        }
 
        public void SetCurrentView(DependencyObject view)
        {
            this.view = view;
        }
 
        public override ModelItem Add(object value)
        {
            ModelItem item = value as ModelItem;
            if (item == null)
            {
                item = this.modelTreeManager.WrapAsModelItem(value);
            }
            if (item == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
            }
            Add(item);
            return item;
        }
 
        public override void Add(ModelItem item)
        {
            if (item == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
            }
            this.modelTreeManager.CollectionAdd(this, item);
        }
 
 
        public override ModelEditingScope BeginEdit(string description, bool shouldApplyChangesImmediately)
        {
            return ModelItemHelper.ModelItemBeginEdit(this.modelTreeManager, description, shouldApplyChangesImmediately);
        }
 
        public override ModelEditingScope BeginEdit(bool shouldApplyChangesImmediately)
        {
            return this.BeginEdit(null, shouldApplyChangesImmediately);
        }
 
        public override ModelEditingScope BeginEdit(string description)
        {
            return this.BeginEdit(description, false);
        }
 
        public override ModelEditingScope BeginEdit()
        {
            return this.BeginEdit(null);
        }
 
        public override void Clear()
        {
            if (!this.IsReadOnly && (this.modelItems.Count > 0))
            {
                this.modelTreeManager.CollectionClear(this);
            }
        }
 
        public override bool Contains(object value)
        {
            ModelItem item = value as ModelItem;
            if (item == null)
            {
                return ((IList)instance).Contains(value);
            }
            return Contains(item);
        }
 
        public override bool Contains(ModelItem item)
        {
            return this.Items.Contains(item);
        }
 
        public override void CopyTo(ModelItem[] array, int arrayIndex)
        {
            if (array == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("array"));
            }
            if (this.Items != null)
            {
                this.Items.CopyTo(array, arrayIndex);
            }
        }
 
        public override object GetCurrentValue()
        {
            return this.instance;
        }
 
        public override IEnumerator<ModelItem> GetEnumerator()
        {
            foreach (ModelItem modelItem in modelItems)
            {
                yield return modelItem;
            }
        }
 
        void IModelTreeItem.OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
 
        IEnumerable<ModelItem> IModelTreeItem.ItemBackPointers
        {
            get { return this.parents; }
        }
 
        List<BackPointer> IModelTreeItem.ExtraPropertyBackPointers
        {
            get { return this.helper.ExtraPropertyBackPointers; }
        }
 
        void IModelTreeItem.SetParent(ModelItem dataModelItem)
        {
            if (this.manuallySetParent == dataModelItem)
            {
                this.manuallySetParent = null;
            }
 
            if (!this.parents.Contains(dataModelItem))
            {
                this.parents.Add(dataModelItem);
            }
        }
 
        void IModelTreeItem.SetSource(ModelProperty property)
        {
            if (!this.sources.Contains(property))
            {
                // also check if the same parent.property is in the list as a different instance of oldModelProperty
                ModelProperty foundProperty = this.sources.Find((modelProperty) => modelProperty.Name.Equals(property.Name) && property.Parent == modelProperty.Parent);
                if (foundProperty == null)
                {
                    this.sources.Add(property);
                }
            }
        }
 
        public override int IndexOf(ModelItem item)
        {
            if (item == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
            }
            return this.modelItems.IndexOf(item);
        }
 
        public override ModelItem Insert(int index, object value)
        {
            ModelItem item = value as ModelItem;
            if (item == null)
            {
                item = this.modelTreeManager.WrapAsModelItem(value);
            }
            if (item == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
            }
            Insert(index, item);
            return item;
        }
 
        public override void Insert(int index, ModelItem item)
        {
            if (item == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
            }
            this.modelTreeManager.CollectionInsert(this, index, item);
        }
 
        public override void Move(int fromIndex, int toIndex)
        {
            if (fromIndex > this.Items.Count)
            {
                throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("fromIndex"));
            }
            if (toIndex > this.Items.Count)
            {
                throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("toIndex"));
            }
            ModelItem movingItem = this.Items[fromIndex];
            this.RemoveAt(fromIndex);
            this.Insert(toIndex, movingItem);
        }
 
        public override bool Remove(object value)
        {
            ModelItem item = value as ModelItem;
            if (item == null)
            {
                Fx.Assert(this.Items != null, "Items collection is null when trying to iterate over it");
                foreach (ModelItem childItem in this.Items)
                {
                    if (childItem.GetCurrentValue() == value)
                    {
                        item = childItem;
                        break;
                    }
                }
                if (item == null)
                {
                    return false;
                }
            }
            return Remove(item);
        }
 
        public override bool Remove(ModelItem item)
        {
            this.modelTreeManager.CollectionRemove(this, item);
            return true;
        }
 
        public override void RemoveAt(int index)
        {
            if (index >= this.Count)
            {
                throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("index"));
            }
            this.modelTreeManager.CollectionRemoveAt(this, index);
        }
 
 
 
        internal void AddCore(ModelItem item)
        {
            Fx.Assert(instance is IList, "instance should be IList");
            Fx.Assert(instance != null, "instance should not be null");
 
            IList instanceList = (IList)instance;
            instanceList.Add(item.GetCurrentValue());
            bool wasInCollection = this.modelItems.Contains(item);
            this.modelItems.Add(item);
            if (!wasInCollection)
            {
                this.modelTreeManager.OnItemEdgeAdded(this, item);
            }
 
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
            }
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, this.Count - 1));
            }
        }
 
 
 
        internal void ClearCore()
        {
            Fx.Assert(instance is IList, " Instance needs to be Ilist for clear to work");
            Fx.Assert(instance != null, "Instance should not be null");
 
            IList instanceList = (IList)instance;
            instanceList.Clear();
 
            List<ModelItem> modelItemsRemoved = new List<ModelItem>(this.modelItems);
            this.modelItems.Clear();
 
            foreach (ModelItem item in modelItemsRemoved.Distinct())
            {
                if (item != null)
                {
                    this.modelTreeManager.OnItemEdgeRemoved(this, item);
                }
            }
 
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
            }
        }
 
 
        internal void InsertCore(int index, ModelItem item)
        {
            Fx.Assert(instance is IList, "instance needs to be IList");
            Fx.Assert(instance != null, "instance should not be null");
            IList instanceList = (IList)instance;
            instanceList.Insert(index, item.GetCurrentValue());
 
            bool wasInCollection = this.modelItems.Contains(item);
            
            this.modelItems.Insert(index, item);
 
            if (!wasInCollection)
            {
                this.modelTreeManager.OnItemEdgeAdded(this, item);
            }
 
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
            }
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, index));
            }
        }
 
        internal void RemoveCore(ModelItem item)
        {
 
            Fx.Assert(instance is IList, "Instance needs to be IList for remove to work");
            Fx.Assert(instance != null, "instance should not be null");
 
            IList instanceList = (IList)instance;
            int index = instanceList.IndexOf(item.GetCurrentValue());
            instanceList.Remove(item.GetCurrentValue());
            this.modelItems.Remove(item);
 
            if (!this.modelItems.Contains(item))
            {
                this.modelTreeManager.OnItemEdgeRemoved(this, item);
            }
 
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
            }
 
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
            }
        }
 
        internal void RemoveAtCore(int index)
        {
            Fx.Assert(instance is IList, "Instance needs to be IList for remove to work");
            Fx.Assert(instance != null, "instance should not be null");
 
            IList instanceList = (IList)instance;
            ModelItem item = this.modelItems[index];
            instanceList.RemoveAt(index);
            this.modelItems.RemoveAt(index);
            if (!this.modelItems.Contains(item))
            {
                this.modelTreeManager.OnItemEdgeRemoved(this, item);
            }
 
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
            }
 
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
            }
        }
 
        void UpdateInstance(object instance)
        {
            this.instance = instance;
            IEnumerable instanceCollection = this.instance as IEnumerable;
            if (instanceCollection != null)
            {
                foreach (object item in instanceCollection)
                {
                    ModelItem modelItem = this.modelTreeManager.WrapAsModelItem(item);
                    bool wasInCollection = item != null && this.modelItems.Contains(item);
                    this.modelItems.Add(modelItem);
                    if (item != null && !wasInCollection)
                    {
                        this.modelTreeManager.OnItemEdgeAdded(this, modelItem);
                    }
                }
            }
        }
 
        void IModelTreeItem.RemoveParent(ModelItem oldParent)
        {
            if (this.manuallySetParent == oldParent)
            {
                this.manuallySetParent = null;
            }
 
            if (this.parents.Contains(oldParent))
            {
                this.parents.Remove(oldParent);
            }
        }
 
        void IModelTreeItem.RemoveSource(ModelProperty oldModelProperty)
        {
            if (this.sources.Contains(oldModelProperty))
            {
                this.sources.Remove(oldModelProperty);
            }
            else
            {
                ((IModelTreeItem)this).RemoveSource(oldModelProperty.Parent, oldModelProperty.Name);
            }
        }
 
        void IModelTreeItem.RemoveSource(ModelItem parent, string propertyName)
        {
            // also check if the same parent.property is in the list as a different instance of oldModelProperty
            ModelProperty foundProperty = this.sources.FirstOrDefault<ModelProperty>((modelProperty) => modelProperty.Name.Equals(propertyName) && modelProperty.Parent == parent);
            if (foundProperty != null)
            {
                this.sources.Remove(foundProperty);
            }
            else
            {
                this.helper.RemoveExtraPropertyBackPointer(parent, propertyName);
            }
        }
    }
}