[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / ModelItemDictionaryImpl.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Model
5 {
6     using System.Activities.Presentation.Services;
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.Diagnostics.CodeAnalysis;
13     using System.Globalization;
14     using System.Linq;
15     using System.Reflection;
16     using System.Runtime;
17     using System.Runtime.Collections;
18     using System.Windows;
19     using System.Windows.Markup;
20
21     internal class ModelItemDictionaryImpl : ModelItemDictionary, IModelTreeItem, ICustomTypeDescriptor
22     {
23         ModelProperty contentProperty;
24         DictionaryWrapper instance;
25         Type itemType;
26         private NullableKeyDictionary<ModelItem, ModelItem> modelItems;
27         internal ModelItem updateKeySavedValue;
28         Dictionary<string, ModelItem> modelPropertyStore;
29         ModelTreeManager modelTreeManager;
30         ModelProperty nameProperty;
31         List<ModelItem> parents;
32         ModelPropertyCollectionImpl properties;
33         List<ModelProperty> sources;
34         ModelTreeItemHelper helper;
35         List<ModelItem> subTreeNodesThatNeedBackLinkPatching;
36         DependencyObject view;
37         ModelItem manuallySetParent;
38
39         [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "This is internal code with no derived class")]
40         public ModelItemDictionaryImpl(ModelTreeManager modelTreeManager, Type itemType, Object instance, ModelItem parent)
41         {
42             Fx.Assert(modelTreeManager != null, "modelTreeManager cannot be null");
43             Fx.Assert(itemType != null, "item type cannot be null");
44             Fx.Assert(instance != null, "instance cannot be null");
45             this.itemType = itemType;
46             this.instance = new DictionaryWrapper(instance);
47             this.modelTreeManager = modelTreeManager;
48             this.parents = new List<ModelItem>(1);
49             this.sources = new List<ModelProperty>(1);
50             this.helper = new ModelTreeItemHelper();
51             if (parent != null)
52             {
53                 this.manuallySetParent = parent;
54             }
55             this.modelPropertyStore = new Dictionary<string, ModelItem>();
56             this.subTreeNodesThatNeedBackLinkPatching = new List<ModelItem>();
57             this.modelItems = new NullableKeyDictionary<ModelItem, ModelItem>();
58             UpdateInstance();
59
60
61             if (ItemsCollectionObject != null)
62             {
63                 ItemsCollectionModelItemCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(itemsCollection_CollectionChanged);
64                 this.ItemsCollectionObject.ModelDictionary = this;
65             }
66         }
67
68         Type itemsCollectionKVPType = null;
69         Type ItemsCollectionKVPType
70         {
71             get
72             {
73                 if (itemsCollectionKVPType == null)
74                 {
75                     if (ItemsCollectionModelItemCollection != null)
76                     {
77                         Type itemsCollectionType = ItemsCollectionModelItemCollection.ItemType;
78                         Type[] genericArguments = itemsCollectionType.GetGenericArguments();
79                         this.itemsCollectionKVPType = typeof(ModelItemKeyValuePair<,>).MakeGenericType(genericArguments);
80                     }
81                 }
82                 return itemsCollectionKVPType;
83             }
84         }
85
86         ModelItemCollection ItemsCollectionModelItemCollection
87         {
88             get
89             {
90                 return this.Properties["ItemsCollection"].Collection;
91             }
92         }
93
94         IItemsCollection ItemsCollectionObject
95         {
96             get
97             {
98                 IItemsCollection itemsCollectionObject = null;
99                 if (this.ItemsCollectionModelItemCollection != null)
100                 {
101                     itemsCollectionObject = ItemsCollectionModelItemCollection.GetCurrentValue() as IItemsCollection;
102                 }
103                 return itemsCollectionObject;
104             }
105         }
106
107         private bool EditInProgress { get; set; }
108
109         internal void UpdateValue(object keyObj, object valueObj)
110         {
111             ModelItem key = null;
112             bool keyFound = this.KeyAsModelItem(keyObj, false, out key);
113             Fx.Assert(keyFound, "The key should already exist in the current dictionary");
114             ModelItem value = this.WrapObject(valueObj);
115             this.EditCore(key, value, false);
116         }
117
118         internal void UpdateKey(object oldKeyObj, object newKeyObj)
119         {
120             if (oldKeyObj != newKeyObj)
121             {
122                 ModelItem newKey = null;
123                 this.KeyAsModelItem(newKeyObj, true, out newKey);
124                 ModelItem oldKey = null;
125                 bool oldKeyFound = this.KeyAsModelItem(oldKeyObj, false, out oldKey);
126                 Fx.Assert(oldKeyFound, "The old key should already exist in the current dictionary");
127                 
128                 try
129                 {
130                     this.EditInProgress = true;
131                     Fx.Assert(this.instance != null, "instance should not be null");
132
133                     bool wasNewKeyInKeysOrValuesCollection = newKey != null && this.IsInKeysOrValuesCollection(newKey);
134
135                     ModelItem value = this.modelItems[oldKey];
136                     this.modelItems.Remove(oldKey);
137
138                     this.updateKeySavedValue = value;
139
140                     if (oldKey != null && !this.IsInKeysOrValuesCollection(oldKey))
141                     {
142                         this.modelTreeManager.OnItemEdgeRemoved(this, oldKey);
143                     }
144
145                     this.updateKeySavedValue = null;
146                     this.modelItems[newKey] = value;
147
148                     if (newKey != null && !wasNewKeyInKeysOrValuesCollection)
149                     {
150                         this.modelTreeManager.OnItemEdgeAdded(this, newKey);
151                     }
152
153                     if (null != this.CollectionChanged)
154                     {
155                         this.CollectionChanged(this,
156                             new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
157                             new KeyValuePair<ModelItem, ModelItem>(newKey, value),
158                             new KeyValuePair<ModelItem, ModelItem>(oldKey, value)));
159                     }
160                 }
161                 finally
162                 {
163                     this.EditInProgress = false;
164                 }
165             }
166         }
167
168         void itemsCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
169         {
170             //If we're in editing, then we don't trigger an update
171             if (EditInProgress)
172             {
173                 return;
174             }
175
176             if (e.Action == NotifyCollectionChangedAction.Add)
177             {
178                 foreach (ModelItem item in e.NewItems)
179                 {
180                     ModelItem key = item.Properties["Key"] == null ? null : item.Properties["Key"].Value;
181                     ModelItem value = item.Properties["Value"] == null ? null : item.Properties["Value"].Value;
182                     this.AddCore(key, value, false);
183                 }
184             }
185             else if (e.Action == NotifyCollectionChangedAction.Remove)
186             {
187                 foreach (ModelItem item in e.OldItems)
188                 {
189                     object keyObject = item.Properties["Key"].Value == null ? null : item.Properties["Key"].Value.GetCurrentValue();
190                     ModelItem key = null;
191                     bool keyFound = KeyAsModelItem(keyObject, false, out key);
192                     Fx.Assert(keyFound, "Key should exist in the current dictionary");
193                     this.RemoveCore(key, false);
194                 }
195             }
196             else if (e.Action == NotifyCollectionChangedAction.Replace)
197             {
198                 Fx.Assert(e.NewItems != null && e.OldItems != null && e.NewItems.Count == e.OldItems.Count,
199                     "there must be equal number of old and new items");
200
201                 foreach (ModelItem item in e.NewItems)
202                 {
203                     object keyObject = item.Properties["Key"].Value == null ? null : item.Properties["Key"].Value.GetCurrentValue();
204                     ModelItem key = null;
205                     bool keyFound = KeyAsModelItem(keyObject, false, out key);
206                     Fx.Assert(keyFound, "Key should exist in the current dictionary");
207                     ModelItem value = item.Properties["Value"] == null ? null : item.Properties["Value"].Value;
208                     this.EditCore(key, value, false);
209                 }
210             }
211             else if (e.Action == NotifyCollectionChangedAction.Reset)
212             {
213                 UpdateInstance();
214                 if (this.CollectionChanged != null)
215                 {
216                     this.CollectionChanged(this,
217                         new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
218                 }
219             }
220             //note we do not handle NotifyCollectionChangedAction.Move as we don't expect it nor can Dictionary do a move operation
221         }
222
223         public override event NotifyCollectionChangedEventHandler CollectionChanged;
224
225         public override event PropertyChangedEventHandler PropertyChanged;
226
227         public override int Count
228         {
229             get { return this.instance.Count; }
230         }
231
232         public override bool IsReadOnly
233         {
234             get { return this.instance.IsReadOnly; }
235         }
236
237         public override ICollection<ModelItem> Keys
238         {
239             get { return this.modelItems.Keys; }
240         }
241
242         public override ICollection<ModelItem> Values
243         {
244             get { return this.modelItems.Values; }
245         }
246
247         public override AttributeCollection Attributes
248         {
249             get
250             {
251                 Fx.Assert(null != this.itemType, "ItemType cannot be null!");
252                 return TypeDescriptor.GetAttributes(this.itemType);
253             }
254         }
255
256         public override ModelProperty Content
257         {
258             get
259             {
260                 if (this.contentProperty == null)
261                 {
262                     Fx.Assert(this.instance != null, "instance cannot be null");
263                     ContentPropertyAttribute contentAttribute = TypeDescriptor.GetAttributes(this.instance.Value)[typeof(ContentPropertyAttribute)] as ContentPropertyAttribute;
264                     if (contentAttribute != null && !String.IsNullOrEmpty(contentAttribute.Name))
265                     {
266                         this.contentProperty = this.Properties.Find(contentAttribute.Name);
267                     }
268                 }
269                 return contentProperty;
270             }
271         }
272
273         public override Type ItemType
274         {
275             get { return this.itemType; }
276         }
277
278         public override string Name
279         {
280             get
281             {
282                 string name = null;
283                 if ((this.NameProperty != null) && (this.NameProperty.Value != null))
284                 {
285                     name = (string)this.NameProperty.Value.GetCurrentValue();
286                 }
287                 return name;
288             }
289             set
290             {
291                 if (null != this.NameProperty)
292                 {
293                     this.NameProperty.SetValue(value);
294                 }
295             }
296         }
297
298         public override ModelItem Parent
299         {
300             get
301             {
302                 return (this.Parents.Count() > 0) ? this.Parents.First() : null;
303             }
304
305         }
306
307         public override ModelItem Root
308         {
309             get { return this.modelTreeManager.Root; }
310         }
311
312         public override ModelPropertyCollection Properties
313         {
314             get
315             {
316                 if (this.properties == null)
317                 {
318                     this.properties = new ModelPropertyCollectionImpl(this);
319                 }
320                 return this.properties;
321             }
322         }
323
324         public override ModelProperty Source
325         {
326             get
327             {
328                 return (this.sources.Count > 0) ? this.sources.First() : null;
329             }
330         }
331
332         public override DependencyObject View
333         {
334             get { return this.view; }
335         }
336
337         public ModelItem ModelItem
338         {
339             get { return this; }
340         }
341
342         public Dictionary<string, ModelItem> ModelPropertyStore
343         {
344             get { return this.modelPropertyStore; }
345         }
346
347         public ModelTreeManager ModelTreeManager
348         {
349             get { return this.modelTreeManager; }
350         }
351
352
353         public override IEnumerable<ModelItem> Parents
354         {
355             get
356             {
357                 if (this.manuallySetParent != null)
358                 {
359                     List<ModelItem> list = new List<ModelItem>();
360                     list.Add(this.manuallySetParent);
361                     return list.Concat(this.parents).Concat(
362                         from source in this.sources
363                         select source.Parent);
364                 }
365
366                 return this.parents.Concat(
367                     from source in this.sources
368                     select source.Parent);
369             }
370         }
371
372         public override IEnumerable<ModelProperty> Sources
373         {
374             get
375             {
376                 return this.sources;
377             }
378         }
379
380         protected ModelProperty NameProperty
381         {
382             get
383             {
384                 if (this.nameProperty == null)
385                 {
386                     Fx.Assert(this.instance != null, "instance cannot be null");
387                     RuntimeNamePropertyAttribute runtimeNamePropertyAttribute = TypeDescriptor.GetAttributes(this.instance.Value)[typeof(RuntimeNamePropertyAttribute)] as RuntimeNamePropertyAttribute;
388                     if (runtimeNamePropertyAttribute != null && !String.IsNullOrEmpty(runtimeNamePropertyAttribute.Name))
389                     {
390                         this.nameProperty = this.Properties.Find(runtimeNamePropertyAttribute.Name);
391                     }
392                     else
393                     {
394                         this.nameProperty = this.Properties.FirstOrDefault<ModelProperty>(p => (0 == string.Compare(p.Name, "Name", StringComparison.OrdinalIgnoreCase)));
395                     }
396                 }
397                 return nameProperty;
398             }
399         }
400
401         public override ModelItem this[ModelItem key]
402         {
403             get
404             {
405                 return this.modelItems[key];
406             }
407             set
408             {
409                 if (this.instance.IsReadOnly)
410                 {
411                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
412                 }
413
414                 ModelItem oldValue = null;
415                 if (this.modelItems.TryGetValue(key, out oldValue))
416                 {
417                     this.modelTreeManager.DictionaryEdit(this, key, value, oldValue);
418                 }
419                 else
420                 {
421                     this.modelTreeManager.DictionaryAdd(this, key, value);
422                 }
423             }
424         }
425
426         public override ModelItem this[object key]
427         {
428             get
429             {
430                 ModelItem keyItem = null;
431                 bool keyFound = this.KeyAsModelItem(key, false, out keyItem);
432                 if (!keyFound)
433                 {
434                     throw FxTrace.Exception.AsError(new KeyNotFoundException(key.ToString()));
435                 }
436
437                 return this[keyItem];
438             }
439             set
440             {
441                 if (this.instance.IsReadOnly)
442                 {
443                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
444                 }
445
446                 ModelItem keyItem = null;
447                 this.KeyAsModelItem(key, true, out keyItem);
448                 this[keyItem] = value;
449             }
450         }
451
452         public override void Add(ModelItem key, ModelItem value)
453         {
454             if (this.instance.IsReadOnly)
455             {
456                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
457             }
458             this.modelTreeManager.DictionaryAdd(this, key, value);
459         }
460
461         public override ModelItem Add(object key, object value)
462         {
463             if (this.instance.IsReadOnly)
464             {
465                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
466             }
467             ModelItem keyModelItem = key as ModelItem ?? this.WrapObject(key);
468             ModelItem valueModelItem = value as ModelItem ?? this.WrapObject(value);
469             this.Add(keyModelItem, valueModelItem);
470             return valueModelItem;
471         }
472
473         public override void Clear()
474         {
475             this.modelTreeManager.DictionaryClear(this);
476         }
477
478         public override bool ContainsKey(ModelItem key)
479         {
480             return this.modelItems.Keys.Contains<ModelItem>(key);
481         }
482
483         public override bool ContainsKey(object key)
484         {
485             ModelItem keyItem = key as ModelItem;
486
487             if (keyItem != null)
488             {
489                 return this.ContainsKey(keyItem);
490             }
491
492             return this.KeyAsModelItem(key, false, out keyItem);
493         }
494
495         public override IEnumerator<KeyValuePair<ModelItem, ModelItem>> GetEnumerator()
496         {
497             return this.modelItems.GetEnumerator();
498         }
499
500         public override bool Remove(ModelItem key)
501         {
502             this.modelTreeManager.DictionaryRemove(this, key);
503             return true;
504         }
505
506         public override bool Remove(object key)
507         {
508             ModelItem keyItem = null;
509             if (!this.KeyAsModelItem(key, false, out keyItem))
510             {
511                 return false;
512             }
513
514             return this.Remove(keyItem);
515         }
516
517         public override bool TryGetValue(ModelItem key, out ModelItem value)
518         {
519             return this.modelItems.TryGetValue(key, out value);
520         }
521
522         public override bool TryGetValue(object key, out ModelItem value)
523         {
524             ModelItem keyItem = null;
525             if (!this.KeyAsModelItem(key, false, out keyItem))
526             {
527                 value = null;
528                 return false;
529             }
530
531             return this.TryGetValue(keyItem, out value);
532         }
533
534         public override ModelEditingScope BeginEdit(string description, bool shouldApplyChangesImmediately)
535         {
536             return ModelItemHelper.ModelItemBeginEdit(this.modelTreeManager, description, shouldApplyChangesImmediately);
537         }
538
539         public override ModelEditingScope BeginEdit(bool shouldApplyChangesImmediately)
540         {
541             return this.BeginEdit(null, shouldApplyChangesImmediately);
542         }
543
544         public override ModelEditingScope BeginEdit(string description)
545         {
546             return this.BeginEdit(description, false);
547         }
548
549         public override ModelEditingScope BeginEdit()
550         {
551             return this.BeginEdit(null);
552         }
553
554         public override object GetCurrentValue()
555         {
556             return this.instance.Value;
557         }
558
559         #region IModelTreeItem Members
560
561         public void OnPropertyChanged(string propertyName)
562         {
563             if (null != this.PropertyChanged)
564             {
565                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
566             }
567         }
568
569         void IModelTreeItem.SetParent(ModelItem dataModelItem)
570         {
571             if (this.manuallySetParent == dataModelItem)
572             {
573                 this.manuallySetParent = null;
574             }
575
576             if (!this.parents.Contains(dataModelItem))
577             {
578                 this.parents.Add(dataModelItem);
579             }
580         }
581
582         void IModelTreeItem.SetSource(ModelProperty property)
583         {
584             if (!this.sources.Contains(property))
585             {
586                 // also check if the same parent.property is in the list as a different instance of oldModelProperty
587                 ModelProperty foundProperty = this.sources.Find((modelProperty) => modelProperty.Name.Equals(property.Name) && property.Parent == modelProperty.Parent);
588                 if (foundProperty == null)
589                 {
590                     this.sources.Add(property);
591                 }
592             }
593         }
594
595         public void SetCurrentView(DependencyObject view)
596         {
597             this.view = view;
598         }
599
600         #endregion
601
602         void IModelTreeItem.RemoveParent(ModelItem oldParent)
603         {
604             if (this.manuallySetParent == oldParent)
605             {
606                 this.manuallySetParent = null;
607             }
608
609             if (this.parents.Contains(oldParent))
610             {
611                 this.parents.Remove(oldParent);
612             }
613         }
614
615         IEnumerable<ModelItem> IModelTreeItem.ItemBackPointers
616         {
617             get { return this.parents; }
618         }
619
620         List<BackPointer> IModelTreeItem.ExtraPropertyBackPointers
621         {
622             get { return this.helper.ExtraPropertyBackPointers; }
623         }
624
625         void IModelTreeItem.RemoveSource(ModelProperty oldModelProperty)
626         {
627             if (this.sources.Contains(oldModelProperty))
628             {
629                 this.sources.Remove(oldModelProperty);
630             }
631             else
632             {
633                 ((IModelTreeItem)this).RemoveSource(oldModelProperty.Parent, oldModelProperty.Name);
634             }
635         }
636
637         void IModelTreeItem.RemoveSource(ModelItem parent, string propertyName)
638         {
639             // also check if the same parent.property is in the list as a different instance of oldModelProperty
640             ModelProperty foundProperty = this.sources.FirstOrDefault<ModelProperty>((modelProperty) => modelProperty.Name.Equals(propertyName) && modelProperty.Parent == parent);
641             if (foundProperty != null)
642             {
643                 this.sources.Remove(foundProperty);
644             }
645             else
646             {
647                 this.helper.RemoveExtraPropertyBackPointer(parent, propertyName);
648             }
649         }
650
651         internal void EditCore(ModelItem key, ModelItem value)
652         {
653             this.EditCore(key, value, true);
654         }
655
656         private void EditCore(ModelItem key, ModelItem value, bool updateInstance)
657         {
658             try
659             {
660                 ModelItem oldValue = this.modelItems[key];
661                 this.EditInProgress = true;
662                 Fx.Assert(this.instance != null, "instance should not be null");
663
664                 bool wasValueInKeysOrValuesCollection = this.IsInKeysOrValuesCollection(value);
665
666                 if (updateInstance)
667                 {
668                     this.instance[(key == null) ? null : key.GetCurrentValue()] = null != value ? value.GetCurrentValue() : null;
669                     //this also makes sure ItemsCollectionModelItemCollection is not null 
670                     if (ItemsCollectionObject != null)
671                     {
672                         try
673                         {
674                             ItemsCollectionObject.ShouldUpdateDictionary = false;
675
676                             foreach (ModelItem item in ItemsCollectionModelItemCollection)
677                             {
678                                 ModelItem keyInCollection = item.Properties["Key"].Value;
679                                 bool found = (key == keyInCollection);
680
681                                 if (!found && key != null && keyInCollection != null)
682                                 {
683                                     object keyValue = key.GetCurrentValue();
684
685                                     // ValueType do not share ModelItem, a ModelItem is always created for a ValueType
686                                     // ModelTreeManager always create a ModelItem even for the same string
687                                     // So, we compare object instance instead of ModelItem for above cases.
688                                     if (keyValue is ValueType || keyValue is string)
689                                     {
690                                         found = keyValue.Equals(keyInCollection.GetCurrentValue());
691                                     }
692                                 }
693
694                                 if (found)
695                                 {
696                                     ModelPropertyImpl valueImpl = item.Properties["Value"] as ModelPropertyImpl;
697                                     if (valueImpl != null)
698                                     {
699                                         valueImpl.SetValueCore(value);
700                                     }
701                                 }
702                             }
703                         }
704                         finally
705                         {
706                             ItemsCollectionObject.ShouldUpdateDictionary = true;
707                         }
708                     }
709                 }
710
711                 this.modelItems[key] = null;
712                 if (oldValue != null && !this.IsInKeysOrValuesCollection(oldValue))
713                 {
714                     this.modelTreeManager.OnItemEdgeRemoved(this, oldValue);
715                 }
716
717                 this.modelItems[key] = value;
718                 if (value != null && !wasValueInKeysOrValuesCollection)
719                 {
720                     this.modelTreeManager.OnItemEdgeAdded(this, value);
721                 }
722
723                 if (null != this.CollectionChanged)
724                 {
725                     this.CollectionChanged(this,
726                         new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
727                         new KeyValuePair<ModelItem, ModelItem>(key, value),
728                         new KeyValuePair<ModelItem, ModelItem>(key, oldValue)));
729                 }
730             }
731             finally
732             {
733                 this.EditInProgress = false;
734             }
735         }
736
737         internal void AddCore(ModelItem key, ModelItem value)
738         {
739             this.AddCore(key, value, true);
740         }
741
742         private void AddCore(ModelItem key, ModelItem value, bool updateInstance)
743         {
744             try
745             {
746                 this.EditInProgress = true;
747                 Fx.Assert(this.instance != null, "instance should not be null");
748                 
749                 bool wasKeyInKeysOrValuesCollection = key != null && this.IsInKeysOrValuesCollection(key);
750                 bool wasValueInKeysOrValuesCollection = value != null && this.IsInKeysOrValuesCollection(value);
751
752                 if (updateInstance)
753                 {
754                     //no need to [....] if the ItemsCollection is not DictionaryItemsCollection wrapped by ModelItemCollectionImpl
755                     ModelItemCollectionImpl itemsCollectionImpl = this.ItemsCollectionModelItemCollection as ModelItemCollectionImpl;
756                     if (ItemsCollectionObject != null && itemsCollectionImpl != null)
757                     {
758                         try
759                         {
760                             ItemsCollectionObject.ShouldUpdateDictionary = false;
761                             object mutableKVPair = Activator.CreateInstance(this.ItemsCollectionKVPType, new object[] { key == null ? null : key.GetCurrentValue(), value != null ? value.GetCurrentValue() : null });
762                             ModelItem mutableKVPairItem = this.modelTreeManager.WrapAsModelItem(mutableKVPair);
763
764                             itemsCollectionImpl.AddCore(mutableKVPairItem);
765                         }
766                         finally
767                         {
768                             ItemsCollectionObject.ShouldUpdateDictionary = true;
769                         }
770                     }
771                     this.instance.Add(key == null ? null : key.GetCurrentValue(), null != value ? value.GetCurrentValue() : null);
772                 }
773
774                 this.modelItems.Add(key, value);
775
776                 if (key != null && !wasKeyInKeysOrValuesCollection)
777                 {
778                     this.modelTreeManager.OnItemEdgeAdded(this, key);
779                 }
780
781                 if (value != null && !wasValueInKeysOrValuesCollection && value != key)
782                 {
783                     this.modelTreeManager.OnItemEdgeAdded(this, value);
784                 }
785
786                 if (null != this.CollectionChanged)
787                 {
788                     this.CollectionChanged(this,
789                         new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,
790                         new KeyValuePair<ModelItem, ModelItem>(key, value)));
791                 }
792             }
793             finally
794             {
795                 this.EditInProgress = false;
796             }
797         }
798
799         internal void ClearCore()
800         {
801             this.ClearCore(true);
802         }
803
804         private void ClearCore(bool updateInstance)
805         {
806             try
807             {
808                 this.EditInProgress = true;
809                 Fx.Assert(this.instance != null, "instance should not be null");
810                 IList removed = this.modelItems.ToList<KeyValuePair<ModelItem, ModelItem>>();
811                 if (updateInstance)
812                 {
813                     //no need to [....] if the ItemsCollection is not DictionaryItemsCollection wrapped by ModelItemCollectionImpl
814                     ModelItemCollectionImpl itemsCollectionImpl = this.ItemsCollectionModelItemCollection as ModelItemCollectionImpl;
815                     if (ItemsCollectionObject != null && itemsCollectionImpl != null)
816                     {
817                         try
818                         {
819                             ItemsCollectionObject.ShouldUpdateDictionary = false;
820                             itemsCollectionImpl.ClearCore();
821                         }
822                         finally
823                         {
824                             ItemsCollectionObject.ShouldUpdateDictionary = true;
825                         }
826                     }
827                     this.instance.Clear();
828                 }
829                 List<ModelItem> removedItems = new List<ModelItem>(this.modelItems.Keys.Concat(this.modelItems.Values).Distinct());
830                 this.modelItems.Clear();
831
832                 foreach (ModelItem item in removedItems.Distinct())
833                 {
834                     if (item != null)
835                     {
836                         this.modelTreeManager.OnItemEdgeRemoved(this, item);
837                     }
838                 }
839
840                 if (null != this.CollectionChanged)
841                 {
842                     this.CollectionChanged(this,
843                         new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
844                 }
845             }
846             finally
847             {
848                 this.EditInProgress = false;
849             }
850         }
851
852         internal void RemoveCore(ModelItem key)
853         {
854             this.RemoveCore(key, true);
855         }
856
857         private bool IsInKeysOrValuesCollection(ModelItem modelItem)
858         {
859             foreach (ModelItem item in this.modelItems.Keys)
860             {
861                 if (item == modelItem)
862                 {
863                     return true;
864                 }
865             }
866
867             foreach (ModelItem item in this.modelItems.Values)
868             {
869                 if (item == modelItem)
870                 {
871                     return true;
872                 }
873             }
874
875             return false;
876         }
877
878         private void RemoveCore(ModelItem key, bool updateInstance)
879         {
880             try
881             {
882                 this.EditInProgress = true;
883                 Fx.Assert(this.instance != null, "instance should not be null");
884                 ModelItem value = this.modelItems[key];
885                 this.modelItems.Remove(key);
886
887                 if (key != null && !this.IsInKeysOrValuesCollection(key))
888                 {
889                     this.modelTreeManager.OnItemEdgeRemoved(this, key);
890                 }
891
892                 if (value != null && !this.IsInKeysOrValuesCollection(value) && value != key)
893                 {
894                     this.modelTreeManager.OnItemEdgeRemoved(this, value);
895                 }
896
897                 if (updateInstance)
898                 {
899                     ModelItemCollectionImpl itemsCollectionImpl = ItemsCollectionModelItemCollection as ModelItemCollectionImpl;
900                     if (ItemsCollectionObject != null && itemsCollectionImpl != null)
901                     {
902                         try
903                         {
904                             ItemsCollectionObject.ShouldUpdateDictionary = false;
905
906                             ModelItem itemToBeRemoved = null;
907                             foreach (ModelItem item in itemsCollectionImpl)
908                             {
909                                 ModelItem keyInCollection = item.Properties["Key"].Value;
910
911                                 if (key == keyInCollection)
912                                 {
913                                     itemToBeRemoved = item;
914                                     break;
915                                 }
916
917                                 if (key != null && keyInCollection != null)
918                                 {
919                                     object keyValue = key.GetCurrentValue();
920
921                                     // ValueType do not share ModelItem, a ModelItem is always created for a ValueType
922                                     // ModelTreeManager always create a ModelItem even for the same string
923                                     // So, we compare object instance instead of ModelItem for above cases.
924                                     if (keyValue is ValueType || keyValue is string)
925                                     {
926                                         if (keyValue.Equals(keyInCollection.GetCurrentValue()))
927                                         {
928                                             itemToBeRemoved = item;
929                                             break;
930                                         }
931                                     }
932                                 }
933                             }
934
935                             if (itemToBeRemoved != null)
936                             {
937                                 itemsCollectionImpl.RemoveCore(itemToBeRemoved);
938                             }
939                         }
940                         finally
941                         {
942                             ItemsCollectionObject.ShouldUpdateDictionary = true;
943                         }
944                     }
945                     this.instance.Remove(key == null ? null : key.GetCurrentValue());
946                 }
947                 if (null != this.CollectionChanged)
948                 {
949                     this.CollectionChanged(this,
950                         new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair<ModelItem, ModelItem>(key, value)));
951                 }
952             }
953             finally
954             {
955                 this.EditInProgress = false;
956             }
957         }
958
959         void UpdateInstance()
960         {
961             IEnumerator dictionaryEnumerator = this.instance.GetEnumerator();
962             while (dictionaryEnumerator.MoveNext())
963             {
964                 object current = dictionaryEnumerator.Current;
965
966                 object keyObject = instance.GetKeyFromCurrent(current);
967                 ModelItem key = (keyObject == null) ? null : this.WrapObject(keyObject);
968                 ModelItem value = this.WrapObject(instance.GetValueFromCurrent(current));
969
970                 bool wasKeyInKeysOrValuesCollection = key != null && this.IsInKeysOrValuesCollection(key);
971                 bool wasValueInKeysOrValuesCollection = value != null && this.IsInKeysOrValuesCollection(value);
972
973                 this.modelItems.Add(key, value);
974
975                 if (key != null && !wasKeyInKeysOrValuesCollection)
976                 {
977                     this.modelTreeManager.OnItemEdgeAdded(this, key);
978                 }
979
980                 if (value != null && !wasValueInKeysOrValuesCollection && value != key)
981                 {
982                     this.modelTreeManager.OnItemEdgeAdded(this, value);
983                 }
984             }
985         }
986
987         ModelItem WrapObject(object value)
988         {
989             return this.ModelTreeManager.WrapAsModelItem(value);
990         }
991
992         // return true if the key already exist, false otherwise.
993         private bool KeyAsModelItem(object value, bool createNew, out ModelItem keyModelItem)
994         {
995             keyModelItem = value as ModelItem;
996             if (keyModelItem != null)
997             {
998                 return true;
999             }
1000
1001             bool found = false;
1002             keyModelItem = this.modelItems.Keys.SingleOrDefault<ModelItem>(p =>
1003             {
1004                 if ((p == null && value == null) || (p != null && object.Equals(p.GetCurrentValue(), value)))
1005                 {
1006                     found = true;
1007                     return true;
1008                 }
1009                 return false;
1010             });
1011
1012             if (createNew && keyModelItem == null)
1013             {
1014                 keyModelItem = WrapObject(value);
1015             }
1016
1017             return found;
1018         }
1019
1020         sealed class DictionaryWrapper
1021         {
1022             object instance;
1023             bool isDictionary = false;
1024             PropertyInfo isReadOnlyProperty;
1025             PropertyInfo countProperty;
1026             PropertyInfo indexingProperty;
1027             MethodInfo addMethod;
1028             MethodInfo removeMethod;
1029             MethodInfo clearMethod;
1030             MethodInfo getEnumeratorMethod;
1031             PropertyInfo keyProperty;
1032             PropertyInfo valueProperty;
1033
1034             public DictionaryWrapper(object instance)
1035             {
1036                 this.instance = instance;
1037                 if (instance is IDictionary)
1038                 {
1039                     this.isDictionary = true;
1040                     Type keyValuePairType = typeof(KeyValuePair<object, object>);
1041                 }
1042                 else
1043                 {
1044                     Type instanceType = instance.GetType();
1045                     instanceType.FindInterfaces(this.GetDictionaryInterface, null);
1046                 }
1047             }
1048
1049             public object Value
1050             {
1051                 get { return this.instance; }
1052             }
1053
1054             public bool IsReadOnly
1055             {
1056                 get
1057                 {
1058                     return (this.isDictionary ? ((IDictionary)instance).IsReadOnly : (bool)this.isReadOnlyProperty.GetValue(this.instance, null));
1059                 }
1060             }
1061
1062             public int Count
1063             {
1064                 get
1065                 {
1066                     return (this.isDictionary ? ((IDictionary)instance).Count : (int)this.countProperty.GetValue(this.instance, null));
1067                 }
1068             }
1069
1070             [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
1071             public object this[object key]
1072             {
1073                 get
1074                 {
1075                     if (this.isDictionary)
1076                     {
1077                         return ((IDictionary)instance)[key];
1078                     }
1079                     else
1080                     {
1081                         return this.indexingProperty.GetValue(this.instance, new object[] { key });
1082                     }
1083                 }
1084                 set
1085                 {
1086                     if (this.isDictionary)
1087                     {
1088                         ((IDictionary)instance)[key] = value;
1089                     }
1090                     else
1091                     {
1092                         this.indexingProperty.SetValue(this.instance, value, new object[] { key });
1093                     }
1094                 }
1095             }
1096
1097             public object GetKeyFromCurrent(object keyValuePair)
1098             {
1099                 if (isDictionary)
1100                 {
1101                     return ((DictionaryEntry)keyValuePair).Key;
1102                 }
1103                 else
1104                 {
1105                     return this.keyProperty.GetValue(keyValuePair, null);
1106                 }
1107             }
1108
1109             public object GetValueFromCurrent(object keyValuePair)
1110             {
1111                 if (isDictionary)
1112                 {
1113                     return ((DictionaryEntry)keyValuePair).Value;
1114                 }
1115                 else
1116                 {
1117                     return this.valueProperty.GetValue(keyValuePair, null);
1118                 }
1119             }
1120
1121
1122             bool GetDictionaryInterface(Type type, object dummy)
1123             {
1124                 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
1125                 {
1126                     this.addMethod = type.GetMethod("Add");
1127                     this.removeMethod = type.GetMethod("Remove");
1128                     this.indexingProperty = type.GetProperty("Item");
1129                     return true;
1130                 }
1131                 if (type.IsGenericType &&
1132                     type.GetGenericArguments()[0].IsGenericType &&
1133                     type.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(KeyValuePair<,>) &&
1134                     type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
1135                 {
1136                     Type keyValuePairType = type.GetGenericArguments()[0];
1137                     this.keyProperty = keyValuePairType.GetProperty("Key");
1138                     this.valueProperty = keyValuePairType.GetProperty("Value");
1139                     this.getEnumeratorMethod = type.GetMethod("GetEnumerator");
1140                     return true;
1141                 }
1142                 if (type.IsGenericType &&
1143                     type.GetGenericArguments()[0].IsGenericType &&
1144                     type.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(KeyValuePair<,>) &&
1145                     type.GetGenericTypeDefinition() == typeof(ICollection<>))
1146                 {
1147                     this.isReadOnlyProperty = type.GetProperty("IsReadOnly");
1148                     this.countProperty = type.GetProperty("Count");
1149                     this.clearMethod = type.GetMethod("Clear");
1150                 }
1151                 return false;
1152             }
1153
1154
1155             public void Add(object key, object value)
1156             {
1157                 if (this.isDictionary)
1158                 {
1159                     ((IDictionary)instance).Add(key, value);
1160                 }
1161                 else
1162                 {
1163                     this.addMethod.Invoke(this.instance, new object[] { key, value });
1164                 }
1165             }
1166
1167             public void Clear()
1168             {
1169                 if (this.isDictionary)
1170                 {
1171                     ((IDictionary)instance).Clear();
1172                 }
1173                 else
1174                 {
1175                     this.clearMethod.Invoke(this.instance, null);
1176                 }
1177             }
1178
1179             public IEnumerator GetEnumerator()
1180             {
1181                 if (this.isDictionary)
1182                 {
1183                     return ((IDictionary)instance).GetEnumerator();
1184                 }
1185                 else
1186                 {
1187                     return (IEnumerator)this.getEnumeratorMethod.Invoke(this.instance, null);
1188                 }
1189             }
1190
1191             public void Remove(object key)
1192             {
1193                 if (this.isDictionary)
1194                 {
1195                     ((IDictionary)instance).Remove(key);
1196                 }
1197                 else
1198                 {
1199                     this.removeMethod.Invoke(this.instance, new object[] { key });
1200                 }
1201             }
1202
1203
1204         }
1205
1206         AttributeCollection ICustomTypeDescriptor.GetAttributes()
1207         {
1208             return this.Attributes;
1209         }
1210
1211         string ICustomTypeDescriptor.GetClassName()
1212         {
1213             return TypeDescriptor.GetClassName(this);
1214         }
1215
1216         string ICustomTypeDescriptor.GetComponentName()
1217         {
1218             return TypeDescriptor.GetComponentName(this);
1219         }
1220
1221         TypeConverter ICustomTypeDescriptor.GetConverter()
1222         {
1223             return ModelUtilities.GetConverter(this);
1224         }
1225
1226         EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
1227         {
1228             // we dont support events;
1229             return null;
1230         }
1231
1232         PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
1233         {
1234             return ModelUtilities.GetDefaultProperty(this);
1235         }
1236
1237         object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
1238         {
1239             // we dont support editors
1240             return null;
1241         }
1242
1243         EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
1244         {
1245             // we dont support events;
1246             return null;
1247         }
1248
1249         EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
1250         {
1251             // we dont support events;
1252             return null;
1253         }
1254
1255         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
1256         {
1257             return ModelUtilities.WrapProperties(this);
1258         }
1259
1260         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
1261         {
1262             // get model properties
1263             List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
1264
1265
1266             foreach (PropertyDescriptor modelPropertyDescriptor in ModelUtilities.WrapProperties(this))
1267             {
1268                 properties.Add(modelPropertyDescriptor);
1269             }
1270
1271             // try to see if there are pseudo builtin properties for this type.
1272             AttachedPropertiesService AttachedPropertiesService = this.modelTreeManager.Context.Services.GetService<AttachedPropertiesService>();
1273             if (AttachedPropertiesService != null)
1274             {
1275                 foreach (AttachedProperty AttachedProperty in AttachedPropertiesService.GetAttachedProperties(this.itemType))
1276                 {
1277                     properties.Add(new AttachedPropertyDescriptor(AttachedProperty, this));
1278                 }
1279             }
1280             return new PropertyDescriptorCollection(properties.ToArray(), true);
1281         }
1282
1283         object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
1284         {
1285             return this;
1286         }
1287     }
1288 }