4c1578a39f672cb0c6513916d65a016fc6d43c58
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / ModelItemCollectionImpl.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Model
6 {
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.Collections.Specialized;
10     using System.ComponentModel;
11     using System.Diagnostics;
12     using System.Windows;
13     using System.Windows.Markup;
14     using System.Activities.Presentation.Services;
15     using System.Linq;
16     using System.Runtime;
17
18     // This class provides the implementation for the ModelItemCollection. This provides
19     // a container for the child modelItems for the entries in a collection.
20
21     class ModelItemCollectionImpl : ModelItemCollection, IModelTreeItem
22     {
23         ModelProperty contentProperty;
24         object instance;
25         Type itemType;
26         List<ModelItem> modelItems;
27         Dictionary<string, ModelItem> modelPropertyStore;
28         ModelTreeManager modelTreeManager;
29         ModelProperty nameProperty;
30         List<ModelItem> parents;
31         ModelPropertyCollectionImpl properties;
32         List<ModelProperty> sources;
33         ModelTreeItemHelper helper;
34         List<ModelItem> subTreeNodesThatNeedBackLinkPatching;
35         DependencyObject view;
36         ModelItem manuallySetParent;
37
38         public ModelItemCollectionImpl(ModelTreeManager modelTreeManager, Type itemType, Object instance, ModelItem parent)
39         {
40             Fx.Assert(modelTreeManager != null, "modelTreeManager cannot be null");
41             Fx.Assert(itemType != null, "item type cannot be null");
42             Fx.Assert(instance != null, "instance cannot be null");
43             this.itemType = itemType;
44             this.instance = instance;
45             this.modelTreeManager = modelTreeManager;
46             this.parents = new List<ModelItem>(1);
47             this.sources = new List<ModelProperty>(1);
48             this.helper = new ModelTreeItemHelper();
49             if (parent != null)
50             {
51                 this.manuallySetParent = parent;
52             }
53             this.modelPropertyStore = new Dictionary<string, ModelItem>();
54             this.subTreeNodesThatNeedBackLinkPatching = new List<ModelItem>();
55             this.modelItems = new List<ModelItem>();
56             UpdateInstance(instance);
57         }
58
59         public override event NotifyCollectionChangedEventHandler CollectionChanged;
60
61         public override event PropertyChangedEventHandler PropertyChanged;
62
63         public override AttributeCollection Attributes
64         {
65             get
66             {
67                 Fx.Assert(this.itemType != null, "item type cannot be null");
68                 return TypeDescriptor.GetAttributes(this.itemType);
69             }
70         }
71
72         public override ModelProperty Content
73         {
74             get
75             {
76                 if (this.contentProperty == null)
77                 {
78                     Fx.Assert(this.instance != null, "instance cannot be null");
79                     ContentPropertyAttribute contentAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(ContentPropertyAttribute)] as ContentPropertyAttribute;
80                     if (contentAttribute != null && !String.IsNullOrEmpty(contentAttribute.Name))
81                     {
82                         this.contentProperty = this.Properties.Find(contentAttribute.Name);
83                     }
84                 }
85                 return contentProperty;
86             }
87         }
88
89         public override int Count
90         {
91             get
92             {
93                 Fx.Assert(instance != null, "instance cannot be null");
94                 if (instance != null)
95                 {
96                     return ((ICollection)instance).Count;
97                 }
98                 return 0;
99             }
100         }
101
102         public override bool IsReadOnly
103         {
104             get
105             {
106                 IList instanceList = instance as IList;
107                 Fx.Assert(instanceList != null, "instance should be IList");
108                 return instanceList.IsReadOnly;
109             }
110         }
111
112         public override Type ItemType
113         {
114             get
115             {
116                 return this.itemType;
117             }
118         }
119
120         public override string Name
121         {
122             get
123             {
124                 string name = null;
125                 if ((this.NameProperty != null) && (this.NameProperty.Value != null))
126                 {
127                     name = (string)this.NameProperty.Value.GetCurrentValue();
128                 }
129                 return name;
130             }
131             set
132             {
133                 this.NameProperty.SetValue(value);
134             }
135         }
136
137         public override ModelItem Parent
138         {
139             get
140             {
141                 return (this.Parents.Count() > 0) ? this.Parents.First() : null;
142             }
143
144         }
145
146         public override ModelPropertyCollection Properties
147         {
148             get
149             {
150                 if (this.properties == null)
151                 {
152                     properties = new ModelPropertyCollectionImpl(this);
153                 }
154                 return properties;
155             }
156         }
157
158         public override ModelItem Root
159         {
160             get
161             {
162                 return this.modelTreeManager.Root;
163             }
164         }
165
166         public override ModelProperty Source
167         {
168             get
169             {
170                 return (this.sources.Count > 0) ? this.sources.First() : null;
171             }
172         }
173
174         public override DependencyObject View
175         {
176             get
177             {
178                 return this.view;
179             }
180         }
181
182         public override IEnumerable<ModelItem> Parents
183         {
184             get
185             {
186                 if (this.manuallySetParent != null)
187                 {
188                     List<ModelItem> list = new List<ModelItem>();
189                     list.Add(this.manuallySetParent);
190                     return list.Concat(this.parents).Concat(
191                         from source in this.sources
192                         select source.Parent);
193                 }
194
195                 return this.parents.Concat(
196                     from source in this.sources
197                     select source.Parent);
198             }
199         }
200
201         public override IEnumerable<ModelProperty> Sources
202         {
203             get
204             {
205                 return this.sources;
206             }
207         }
208
209         internal List<ModelItem> Items
210         {
211             get
212             {
213                 return modelItems;
214             }
215         }
216
217         internal Dictionary<string, ModelItem> ModelPropertyStore
218         {
219             get
220             {
221                 return modelPropertyStore;
222             }
223         }
224
225         protected ModelProperty NameProperty
226         {
227             get
228             {
229                 if (this.nameProperty == null)
230                 {
231                     Fx.Assert(this.instance != null, "instance cannot be null");
232                     RuntimeNamePropertyAttribute runtimeNamePropertyAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(RuntimeNamePropertyAttribute)] as RuntimeNamePropertyAttribute;
233                     if (runtimeNamePropertyAttribute != null && !String.IsNullOrEmpty(runtimeNamePropertyAttribute.Name))
234                     {
235                         this.nameProperty = this.Properties.Find(runtimeNamePropertyAttribute.Name);
236                     }
237                 }
238                 return nameProperty;
239             }
240         }
241
242         ModelItem IModelTreeItem.ModelItem
243         {
244             get
245             {
246                 return this;
247             }
248         }
249
250         Dictionary<string, ModelItem> IModelTreeItem.ModelPropertyStore
251         {
252             get
253             {
254                 return modelPropertyStore;
255             }
256         }
257
258
259         ModelTreeManager IModelTreeItem.ModelTreeManager
260         {
261             get
262             {
263                 return modelTreeManager;
264             }
265         }
266
267         public override ModelItem this[int index]
268         {
269             get
270             {
271                 return this.modelItems[index];
272             }
273             set
274             {
275                 this.Insert(index, value);
276             }
277         }
278
279         public void SetCurrentView(DependencyObject view)
280         {
281             this.view = view;
282         }
283
284         public override ModelItem Add(object value)
285         {
286             ModelItem item = value as ModelItem;
287             if (item == null)
288             {
289                 item = this.modelTreeManager.WrapAsModelItem(value);
290             }
291             if (item == null)
292             {
293                 throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
294             }
295             Add(item);
296             return item;
297         }
298
299         public override void Add(ModelItem item)
300         {
301             if (item == null)
302             {
303                 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
304             }
305             this.modelTreeManager.CollectionAdd(this, item);
306         }
307
308
309         public override ModelEditingScope BeginEdit(string description, bool shouldApplyChangesImmediately)
310         {
311             return ModelItemHelper.ModelItemBeginEdit(this.modelTreeManager, description, shouldApplyChangesImmediately);
312         }
313
314         public override ModelEditingScope BeginEdit(bool shouldApplyChangesImmediately)
315         {
316             return this.BeginEdit(null, shouldApplyChangesImmediately);
317         }
318
319         public override ModelEditingScope BeginEdit(string description)
320         {
321             return this.BeginEdit(description, false);
322         }
323
324         public override ModelEditingScope BeginEdit()
325         {
326             return this.BeginEdit(null);
327         }
328
329         public override void Clear()
330         {
331             if (!this.IsReadOnly && (this.modelItems.Count > 0))
332             {
333                 this.modelTreeManager.CollectionClear(this);
334             }
335         }
336
337         public override bool Contains(object value)
338         {
339             ModelItem item = value as ModelItem;
340             if (item == null)
341             {
342                 return ((IList)instance).Contains(value);
343             }
344             return Contains(item);
345         }
346
347         public override bool Contains(ModelItem item)
348         {
349             return this.Items.Contains(item);
350         }
351
352         public override void CopyTo(ModelItem[] array, int arrayIndex)
353         {
354             if (array == null)
355             {
356                 throw FxTrace.Exception.AsError(new ArgumentNullException("array"));
357             }
358             if (this.Items != null)
359             {
360                 this.Items.CopyTo(array, arrayIndex);
361             }
362         }
363
364         public override object GetCurrentValue()
365         {
366             return this.instance;
367         }
368
369         public override IEnumerator<ModelItem> GetEnumerator()
370         {
371             foreach (ModelItem modelItem in modelItems)
372             {
373                 yield return modelItem;
374             }
375         }
376
377         void IModelTreeItem.OnPropertyChanged(string propertyName)
378         {
379             if (PropertyChanged != null)
380             {
381                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
382             }
383         }
384
385         IEnumerable<ModelItem> IModelTreeItem.ItemBackPointers
386         {
387             get { return this.parents; }
388         }
389
390         List<BackPointer> IModelTreeItem.ExtraPropertyBackPointers
391         {
392             get { return this.helper.ExtraPropertyBackPointers; }
393         }
394
395         void IModelTreeItem.SetParent(ModelItem dataModelItem)
396         {
397             if (this.manuallySetParent == dataModelItem)
398             {
399                 this.manuallySetParent = null;
400             }
401
402             if (!this.parents.Contains(dataModelItem))
403             {
404                 this.parents.Add(dataModelItem);
405             }
406         }
407
408         void IModelTreeItem.SetSource(ModelProperty property)
409         {
410             if (!this.sources.Contains(property))
411             {
412                 // also check if the same parent.property is in the list as a different instance of oldModelProperty
413                 ModelProperty foundProperty = this.sources.Find((modelProperty) => modelProperty.Name.Equals(property.Name) && property.Parent == modelProperty.Parent);
414                 if (foundProperty == null)
415                 {
416                     this.sources.Add(property);
417                 }
418             }
419         }
420
421         public override int IndexOf(ModelItem item)
422         {
423             if (item == null)
424             {
425                 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
426             }
427             return this.modelItems.IndexOf(item);
428         }
429
430         public override ModelItem Insert(int index, object value)
431         {
432             ModelItem item = value as ModelItem;
433             if (item == null)
434             {
435                 item = this.modelTreeManager.WrapAsModelItem(value);
436             }
437             if (item == null)
438             {
439                 throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
440             }
441             Insert(index, item);
442             return item;
443         }
444
445         public override void Insert(int index, ModelItem item)
446         {
447             if (item == null)
448             {
449                 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
450             }
451             this.modelTreeManager.CollectionInsert(this, index, item);
452         }
453
454         public override void Move(int fromIndex, int toIndex)
455         {
456             if (fromIndex > this.Items.Count)
457             {
458                 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("fromIndex"));
459             }
460             if (toIndex > this.Items.Count)
461             {
462                 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("toIndex"));
463             }
464             ModelItem movingItem = this.Items[fromIndex];
465             this.RemoveAt(fromIndex);
466             this.Insert(toIndex, movingItem);
467         }
468
469         public override bool Remove(object value)
470         {
471             ModelItem item = value as ModelItem;
472             if (item == null)
473             {
474                 Fx.Assert(this.Items != null, "Items collection is null when trying to iterate over it");
475                 foreach (ModelItem childItem in this.Items)
476                 {
477                     if (childItem.GetCurrentValue() == value)
478                     {
479                         item = childItem;
480                         break;
481                     }
482                 }
483                 if (item == null)
484                 {
485                     return false;
486                 }
487             }
488             return Remove(item);
489         }
490
491         public override bool Remove(ModelItem item)
492         {
493             this.modelTreeManager.CollectionRemove(this, item);
494             return true;
495         }
496
497         public override void RemoveAt(int index)
498         {
499             if (index >= this.Count)
500             {
501                 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("index"));
502             }
503             this.modelTreeManager.CollectionRemoveAt(this, index);
504         }
505
506
507
508         internal void AddCore(ModelItem item)
509         {
510             Fx.Assert(instance is IList, "instance should be IList");
511             Fx.Assert(instance != null, "instance should not be null");
512
513             IList instanceList = (IList)instance;
514             instanceList.Add(item.GetCurrentValue());
515             bool wasInCollection = this.modelItems.Contains(item);
516             this.modelItems.Add(item);
517             if (!wasInCollection)
518             {
519                 this.modelTreeManager.OnItemEdgeAdded(this, item);
520             }
521
522             if (this.PropertyChanged != null)
523             {
524                 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
525             }
526             if (this.CollectionChanged != null)
527             {
528                 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, this.Count - 1));
529             }
530         }
531
532
533
534         internal void ClearCore()
535         {
536             Fx.Assert(instance is IList, " Instance needs to be Ilist for clear to work");
537             Fx.Assert(instance != null, "Instance should not be null");
538
539             IList instanceList = (IList)instance;
540             instanceList.Clear();
541
542             List<ModelItem> modelItemsRemoved = new List<ModelItem>(this.modelItems);
543             this.modelItems.Clear();
544
545             foreach (ModelItem item in modelItemsRemoved.Distinct())
546             {
547                 if (item != null)
548                 {
549                     this.modelTreeManager.OnItemEdgeRemoved(this, item);
550                 }
551             }
552
553             if (this.PropertyChanged != null)
554             {
555                 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
556             }
557         }
558
559
560         internal void InsertCore(int index, ModelItem item)
561         {
562             Fx.Assert(instance is IList, "instance needs to be IList");
563             Fx.Assert(instance != null, "instance should not be null");
564             IList instanceList = (IList)instance;
565             instanceList.Insert(index, item.GetCurrentValue());
566
567             bool wasInCollection = this.modelItems.Contains(item);
568             
569             this.modelItems.Insert(index, item);
570
571             if (!wasInCollection)
572             {
573                 this.modelTreeManager.OnItemEdgeAdded(this, item);
574             }
575
576             if (this.PropertyChanged != null)
577             {
578                 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
579             }
580             if (this.CollectionChanged != null)
581             {
582                 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, index));
583             }
584         }
585
586         internal void RemoveCore(ModelItem item)
587         {
588
589             Fx.Assert(instance is IList, "Instance needs to be IList for remove to work");
590             Fx.Assert(instance != null, "instance should not be null");
591
592             IList instanceList = (IList)instance;
593             int index = instanceList.IndexOf(item.GetCurrentValue());
594             instanceList.Remove(item.GetCurrentValue());
595             this.modelItems.Remove(item);
596
597             if (!this.modelItems.Contains(item))
598             {
599                 this.modelTreeManager.OnItemEdgeRemoved(this, item);
600             }
601
602             if (this.PropertyChanged != null)
603             {
604                 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
605             }
606
607             if (this.CollectionChanged != null)
608             {
609                 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
610             }
611         }
612
613         internal void RemoveAtCore(int index)
614         {
615             Fx.Assert(instance is IList, "Instance needs to be IList for remove to work");
616             Fx.Assert(instance != null, "instance should not be null");
617
618             IList instanceList = (IList)instance;
619             ModelItem item = this.modelItems[index];
620             instanceList.RemoveAt(index);
621             this.modelItems.RemoveAt(index);
622             if (!this.modelItems.Contains(item))
623             {
624                 this.modelTreeManager.OnItemEdgeRemoved(this, item);
625             }
626
627             if (this.PropertyChanged != null)
628             {
629                 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
630             }
631
632             if (this.CollectionChanged != null)
633             {
634                 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
635             }
636         }
637
638         void UpdateInstance(object instance)
639         {
640             this.instance = instance;
641             IEnumerable instanceCollection = this.instance as IEnumerable;
642             if (instanceCollection != null)
643             {
644                 foreach (object item in instanceCollection)
645                 {
646                     ModelItem modelItem = this.modelTreeManager.WrapAsModelItem(item);
647                     bool wasInCollection = item != null && this.modelItems.Contains(item);
648                     this.modelItems.Add(modelItem);
649                     if (item != null && !wasInCollection)
650                     {
651                         this.modelTreeManager.OnItemEdgeAdded(this, modelItem);
652                     }
653                 }
654             }
655         }
656
657         void IModelTreeItem.RemoveParent(ModelItem oldParent)
658         {
659             if (this.manuallySetParent == oldParent)
660             {
661                 this.manuallySetParent = null;
662             }
663
664             if (this.parents.Contains(oldParent))
665             {
666                 this.parents.Remove(oldParent);
667             }
668         }
669
670         void IModelTreeItem.RemoveSource(ModelProperty oldModelProperty)
671         {
672             if (this.sources.Contains(oldModelProperty))
673             {
674                 this.sources.Remove(oldModelProperty);
675             }
676             else
677             {
678                 ((IModelTreeItem)this).RemoveSource(oldModelProperty.Parent, oldModelProperty.Name);
679             }
680         }
681
682         void IModelTreeItem.RemoveSource(ModelItem parent, string propertyName)
683         {
684             // also check if the same parent.property is in the list as a different instance of oldModelProperty
685             ModelProperty foundProperty = this.sources.FirstOrDefault<ModelProperty>((modelProperty) => modelProperty.Name.Equals(propertyName) && modelProperty.Parent == parent);
686             if (foundProperty != null)
687             {
688                 this.sources.Remove(foundProperty);
689             }
690             else
691             {
692                 this.helper.RemoveExtraPropertyBackPointer(parent, propertyName);
693             }
694         }
695     }
696 }