[corlib] Avoid unnecessary ephemeron array resizes
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / ModelTreeManager.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Model
6 {
7     using System.Activities.Expressions;
8     using System.Activities.Presentation;
9     using System.Activities.Presentation.Hosting;
10     using System.Activities.Presentation.Internal.PropertyEditing;
11     using System.Activities.Presentation.Services;
12     using System.Activities.Presentation.Validation;
13     using System.Activities.Presentation.View;
14     using System.Collections;
15     using System.Collections.Generic;
16     using System.ComponentModel;
17     using System.Diagnostics;
18     using System.Diagnostics.CodeAnalysis;
19     using System.Linq;
20     using System.Runtime;
21     using System.Text;
22     using Microsoft.Activities.Presentation.Xaml;
23
24     // This class manages the model tree, provides the root model item and the modelservice
25     // This also provides syncing the model tree with the xaml text
26     // The model service is publishes on the editing context passed to the constructor.
27
28     [Fx.Tag.XamlVisible(false)]
29     public class ModelTreeManager
30     {
31         internal ModelServiceImpl modelService;
32         EditingContext context;
33         // The value of this dictionary is a WeakReference to ModelItem.
34         // This need to be a WeakReference because if the ModelItem has a strong reference, it 
35         // will have a strong reference to the underlying object instance as well.
36         WeakKeyDictionary<object, WeakReference> objectMap;
37         ModelItem rootItem;
38         ImmediateEditingScope immediateEditingScope;
39         Stack<ModelEditingScope> editingScopes;
40         int redoUndoInProgressCount = 0;
41         FeatureManager featureManager;
42         ModelGraphManager graphManager;
43
44         public ModelTreeManager(EditingContext context)
45         {
46             if (context == null)
47             {
48                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
49             }
50             this.context = context;
51             // We want to check reference equality for keys in ObjectMap. 
52             // If the user overrides Equals method for their class we still want to use referential equality.
53             objectMap = new WeakKeyDictionary<object, WeakReference>(ObjectReferenceEqualityComparer<object>.Default);
54             editingScopes = new Stack<ModelEditingScope>();
55             this.graphManager = new ModelGraphManager(this);
56         }
57
58         public event EventHandler<EditingScopeEventArgs> EditingScopeCompleted;
59
60         // Private event only for EditingScope.
61         private event EventHandler<ModelItemsRemovedEventArgs> ModelItemsRemoved;
62
63         private event EventHandler<ModelItemsAddedEventArgs> ModelItemsAdded;
64
65         public ModelItem Root
66         {
67             get
68             {
69                 return this.rootItem;
70             }
71         }
72
73         internal EditingContext Context
74         {
75             get
76             {
77                 return this.context;
78             }
79         }
80
81         FeatureManager FeatureManager
82         {
83             get
84             {
85                 if (this.featureManager == null)
86                 {
87                     this.featureManager = this.context.Services.GetService<FeatureManager>();
88                 }
89                 return this.featureManager;
90             }
91         }
92
93         internal bool RedoUndoInProgress
94         {
95             get
96             {
97                 return this.redoUndoInProgressCount > 0;
98             }
99         }
100
101         internal void StartTracking()
102         {
103             redoUndoInProgressCount--;
104         }
105
106         internal void StopTracking()
107         {
108             redoUndoInProgressCount++;
109         }
110
111         public ModelItem CreateModelItem(ModelItem parent, object instance)
112         {
113             if (instance == null)
114             {
115                 throw FxTrace.Exception.AsError(new ArgumentNullException("instance"));
116             }
117             ModelItem retval;
118
119             Type instanceType = instance.GetType();
120             object[] result = new object[2] { false, false };
121
122             Type[] interfaces = instanceType.FindInterfaces(ModelTreeManager.CheckInterface, result);
123
124             bool isList = (bool)result[0];
125             bool isDictionary = (bool)result[1];
126
127             if (isDictionary)
128             {
129                 foreach (Type type in interfaces)
130                 {
131                     if (type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
132                     {
133                         // To expose one more property, a collection of MutableKeyValuePairs, to the model tree.
134                         TypeDescriptor.AddProvider(new DictionaryTypeDescriptionProvider(instanceType), instance);
135                         break;
136                     }
137                 }
138
139                 ModelItemDictionary modelItem = new ModelItemDictionaryImpl(this, instance.GetType(), instance, parent);
140                 retval = modelItem;
141             }
142             else if (isList)
143             {
144                 ModelItemCollectionImpl modelItem = new ModelItemCollectionImpl(this, instance.GetType(), instance, parent);
145                 retval = modelItem;
146             }
147             else
148             {
149                 retval = new ModelItemImpl(this, instance.GetType(), instance, parent);
150             }
151             if (!((instance is ValueType) || (instance is string)))
152             {
153                 //
154                 // ValueType do not have a concept of shared reference, they are always copied.
155                 // strings are immutatable, therefore the risk of making all shared string references to different
156                 // string ModelItems is low.
157                 // 
158                 // To special case string is because underlying OM are sharing string objects for DisplayName across
159                 // Different activity object instances. These shared references is causing memory leak because of bugs.
160                 // 
161                 // We will need to fix these issues in Beta2.
162                 //
163                 objectMap[instance] = new WeakReference(retval);
164             }
165
166             if (this.FeatureManager != null)
167             {
168                 this.FeatureManager.InitializeFeature(instance.GetType());
169             }
170             return retval;
171         }
172
173         static bool CheckInterface(Type type, object result)
174         {
175             object[] values = (object[])result;
176             if (typeof(IList).IsAssignableFrom(type))
177             {
178                 values[0] = true;
179                 return true;
180             }
181             if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>))
182             {
183                 values[0] = true;
184                 return true;
185             }
186             if (typeof(IDictionary).IsAssignableFrom(type))
187             {
188                 values[1] = true;
189                 return true;
190             }
191             if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
192             {
193                 values[1] = true;
194                 return true;
195             }
196             return false;
197         }
198
199         public void Load(object rootInstance)
200         {
201             if (rootInstance == null)
202             {
203                 throw FxTrace.Exception.AsError(new ArgumentNullException("rootInstance"));
204             }
205             objectMap.Clear();
206
207             ModelItem oldRoot = this.rootItem;
208             this.rootItem = WrapAsModelItem(rootInstance);
209             this.graphManager.OnRootChanged(oldRoot, this.rootItem);
210             if (this.modelService == null)
211             {
212                 this.modelService = new ModelServiceImpl(this);
213                 this.context.Services.Publish<ModelService>(modelService);
214             }
215         }
216
217         // This methods clears the value of a property , if the property is
218         // a reference type then its set to null, if its a value type the 
219         // property value is reset to the default. this also clears the sub ModelItem corresponding
220         // to the old value from the parent ModelItem's modelPropertyStore.
221         internal void ClearValue(ModelPropertyImpl modelProperty)
222         {
223             Fx.Assert(modelProperty != null, "modelProperty should not be null");
224             Fx.Assert(modelProperty.Parent is IModelTreeItem, "modelProperty.Parent should be an IModelTreeItem");
225             ModelItem newValueModelItem = null;
226             newValueModelItem = WrapAsModelItem(modelProperty.DefaultValue);
227             PropertyChange propertyChange = new PropertyChange()
228                 {
229                     Owner = modelProperty.Parent,
230                     PropertyName = modelProperty.Name,
231                     OldValue = modelProperty.Value,
232                     NewValue = newValueModelItem,
233                     ModelTreeManager = this
234                 };
235             AddToCurrentEditingScope(propertyChange);
236         }
237
238         internal void CollectionAdd(ModelItemCollectionImpl dataModelItemCollection, ModelItem item)
239         {
240             CollectionInsert(dataModelItemCollection, -1, item);
241         }
242
243         internal void CollectionInsert(ModelItemCollectionImpl dataModelItemCollection, int index, ModelItem item)
244         {
245             Fx.Assert(dataModelItemCollection != null, "collection should not be null");
246             CollectionChange change = new CollectionChange()
247                 {
248                     Collection = dataModelItemCollection,
249                     Item = item,
250                     Index = index,
251                     ModelTreeManager = this,
252                     Operation = CollectionChange.OperationType.Insert
253                 };
254             AddToCurrentEditingScope(change);
255         }
256
257         internal void CollectionClear(ModelItemCollectionImpl modelItemCollectionImpl)
258         {
259             Fx.Assert(modelItemCollectionImpl != null, "collection should not be null");
260             Fx.Assert(this.modelService != null, "modelService should not be null");
261             List<ModelItem> removedItems = new List<ModelItem>();
262             removedItems.AddRange(modelItemCollectionImpl);
263             using (ModelEditingScope editingScope = CreateEditingScope(SR.CollectionClearEditingScopeDescription))
264             {
265                 foreach (ModelItem modelItem in removedItems)
266                 {
267                     this.CollectionRemove(modelItemCollectionImpl, modelItem);
268                 }
269                 editingScope.Complete();
270             }
271             this.modelService.OnModelItemsRemoved(removedItems);
272         }
273
274         internal void NotifyCollectionInsert(ModelItem item, ModelChangeInfo changeInfo)
275         {
276             this.modelService.OnModelItemAdded(item, changeInfo);
277         }
278
279         internal void CollectionRemove(ModelItemCollectionImpl dataModelItemCollection, ModelItem item)
280         {
281             CollectionRemove(dataModelItemCollection, item, -1);
282         }
283
284         internal void CollectionRemoveAt(ModelItemCollectionImpl dataModelItemCollection, int index)
285         {
286             ModelItem item = dataModelItemCollection[index];
287             CollectionRemove(dataModelItemCollection, item, index);
288         }
289
290         private void CollectionRemove(ModelItemCollectionImpl dataModelItemCollection, ModelItem item, int index)
291         {
292             Fx.Assert(dataModelItemCollection != null, "collection should not be null");
293             CollectionChange change = new CollectionChange()
294                 {
295                     Collection = dataModelItemCollection,
296                     Item = item,
297                     Index = index,
298                     ModelTreeManager = this,
299                     Operation = CollectionChange.OperationType.Delete
300                 };
301             AddToCurrentEditingScope(change);
302         }
303
304         internal void NotifyCollectionRemove(ModelItem item, ModelChangeInfo changeInfo)
305         {
306             this.modelService.OnModelItemRemoved(item, changeInfo);
307         }
308
309         internal void DictionaryClear(ModelItemDictionaryImpl modelDictionary)
310         {
311             Fx.Assert(modelDictionary != null, "dictionary should not be null");
312             Fx.Assert(this.modelService != null, "modelService should not be null");
313             ModelItem[] keys = modelDictionary.Keys.ToArray<ModelItem>();
314
315             using (ModelEditingScope editingScope = CreateEditingScope(SR.DictionaryClearEditingScopeDescription))
316             {
317                 foreach (ModelItem key in keys)
318                 {
319                     this.DictionaryRemove(modelDictionary, key);
320                 }
321                 editingScope.Complete();
322             }
323         }
324
325         internal void DictionaryEdit(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key, ModelItem newValue, ModelItem oldValue)
326         {
327             Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
328             Fx.Assert(this.modelService != null, "modelService should not be null");
329             DictionaryEditChange change = new DictionaryEditChange()
330                 {
331                     Dictionary = dataModelItemDictionary,
332                     Key = key,
333                     NewValue = newValue,
334                     OldValue = oldValue,
335                     ModelTreeManager = this
336                 };
337             AddToCurrentEditingScope(change);
338         }
339
340         internal void DictionaryAdd(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key, ModelItem value)
341         {
342             Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
343             Fx.Assert(this.modelService != null, "modelService should not be null");
344             DictionaryChange change = new DictionaryChange()
345                 {
346                     Dictionary = dataModelItemDictionary,
347                     Key = key,
348                     Value = value,
349                     Operation = DictionaryChange.OperationType.Insert,
350                     ModelTreeManager = this
351                 };
352             AddToCurrentEditingScope(change);
353         }
354
355         internal void OnPropertyEdgeAdded(string propertyName, ModelItem from, ModelItem to)
356         {
357             this.graphManager.OnPropertyEdgeAdded(propertyName, from, to);
358         }
359
360         internal void OnItemEdgeAdded(ModelItem from, ModelItem to)
361         {
362             this.graphManager.OnItemEdgeAdded(from, to);
363         }
364
365         internal void OnPropertyEdgeRemoved(string propertyName, ModelItem from, ModelItem to)
366         {
367             this.graphManager.OnPropertyEdgeRemoved(propertyName, from, to);
368         }
369
370         internal void OnItemEdgeRemoved(ModelItem from, ModelItem to)
371         {
372             this.graphManager.OnItemEdgeRemoved(from, to);
373         }
374
375         internal void DictionaryRemove(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key)
376         {
377             Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
378             Fx.Assert(this.modelService != null, "modelService should not be null");
379             ModelItem value = dataModelItemDictionary[key];
380             DictionaryChange change = new DictionaryChange()
381                 {
382                     Dictionary = dataModelItemDictionary,
383                     Key = key,
384                     Value = value,
385                     Operation = DictionaryChange.OperationType.Delete,
386                     ModelTreeManager = this
387                 };
388             AddToCurrentEditingScope(change);
389
390         }
391
392         internal static IEnumerable<ModelItem> Find(ModelItem startingItem, Predicate<ModelItem> matcher, bool skipCollapsedAndUnrootable)
393         {
394             Fx.Assert(startingItem != null, "starting item should not be null");
395             Fx.Assert(matcher != null, "matching predicate should not be null");
396             WorkflowViewService viewService = startingItem.GetEditingContext().Services.GetService<ViewService>() as WorkflowViewService;
397             if (skipCollapsedAndUnrootable)
398             {
399                 Fx.Assert(viewService != null, "ViewService must be available in order to skip exploring ModelItems whose views are collapsed.");
400             }
401
402             Predicate<ModelItem> shouldSearchThroughProperties = (currentModelItem) => (!skipCollapsedAndUnrootable)
403                 || (!typeof(WorkflowViewElement).IsAssignableFrom(viewService.GetDesignerType(currentModelItem.ItemType)))
404                 || (ViewUtilities.IsViewExpanded(currentModelItem, startingItem.GetEditingContext()) && viewService.ShouldAppearOnBreadCrumb(currentModelItem, true));
405
406             List<ModelItem> foundItems = new List<ModelItem>();
407             Queue<ModelItem> modelItems = new Queue<ModelItem>();
408             modelItems.Enqueue(startingItem);
409             HashSet<ModelItem> alreadyVisited = new HashSet<ModelItem>();
410             while (modelItems.Count > 0)
411             {
412                 ModelItem currentModelItem = modelItems.Dequeue();
413                 if (currentModelItem == null)
414                 {
415                     continue;
416                 }
417
418                 if (matcher(currentModelItem))
419                 {
420                     foundItems.Add(currentModelItem);
421                 }
422
423                 List<ModelItem> neighbors = GetNeighbors(currentModelItem, shouldSearchThroughProperties);
424
425                 foreach (ModelItem neighbor in neighbors)
426                 {
427                     if (!alreadyVisited.Contains(neighbor))
428                     {
429                         alreadyVisited.Add(neighbor);
430                         modelItems.Enqueue(neighbor);
431                     }
432                 }
433             }
434
435             return foundItems;
436         }
437
438         private static List<ModelItem> GetNeighbors(ModelItem currentModelItem, Predicate<ModelItem> extraShouldSearchThroughProperties)
439         {
440             List<ModelItem> neighbors = new List<ModelItem>();
441
442             // do not search through Type and its derivatives
443             if (typeof(Type).IsAssignableFrom(currentModelItem.ItemType))
444             {
445                 return neighbors;
446             }
447
448             ModelItemCollection collection = currentModelItem as ModelItemCollection;
449             if (collection != null)
450             {
451                 foreach (ModelItem modelItem in collection)
452                 {
453                     if (modelItem != null)
454                     {
455                         neighbors.Add(modelItem);
456                     }
457                 }
458             }
459             else
460             {
461                 ModelItemDictionary dictionary = currentModelItem as ModelItemDictionary;
462                 if (dictionary != null)
463                 {
464                     foreach (KeyValuePair<ModelItem, ModelItem> kvp in dictionary)
465                     {
466                         ModelItem miKey = kvp.Key;
467                         if (miKey != null)
468                         {
469                             neighbors.Add(miKey);
470                         }
471
472                         ModelItem miValue = kvp.Value;
473                         if (miValue != null)
474                         {
475                             neighbors.Add(miValue);
476                         }
477                     }
478                 }
479             }
480
481             if (extraShouldSearchThroughProperties(currentModelItem))
482             {
483                 ModelPropertyCollection modelProperties = currentModelItem.Properties;
484                 foreach (ModelProperty property in modelProperties)
485                 {
486                     if (currentModelItem is ModelItemDictionary && string.Equals(property.Name, "ItemsCollection"))
487                     {
488                         // Don't search the item collection since we already search the items above.
489                         continue;
490                     }
491
492                     // we don't want to even try to get the value for a value type property
493                     // because that will create a new ModelItem every time.
494
495                     // System.Type has properties that throw when we try to get value
496                     // we don't want to expand system.type further during a search.
497                     if (typeof(Type).IsAssignableFrom(property.PropertyType) || property.PropertyType.IsValueType)
498                     {
499                         continue;
500                     }
501
502                     else
503                     {
504                         if (property.Value != null)
505                         {
506                             neighbors.Add(property.Value);
507                         }
508                     }
509                 }
510             }
511
512             return neighbors;
513         }
514
515         internal static ModelItem FindFirst(ModelItem startingItem, Predicate<ModelItem> matcher)
516         {
517             return FindFirst(startingItem, matcher, (m) => true);
518         }
519
520         internal static ModelItem FindFirst(ModelItem startingItem, Predicate<ModelItem> matcher, Predicate<ModelItem> extraShouldSearchThroughProperties)
521         {
522             Fx.Assert(startingItem != null, "starting item should not be null");
523             Fx.Assert(matcher != null, "matching predicate should not be null");
524             Fx.Assert(extraShouldSearchThroughProperties != null, "extraShouldSearchThroughProperties should not be null");
525             ModelItem foundItem = null;
526             Queue<ModelItem> modelItems = new Queue<ModelItem>();
527             modelItems.Enqueue(startingItem);
528             HashSet<ModelItem> alreadyVisited = new HashSet<ModelItem>();
529             while (modelItems.Count > 0)
530             {
531                 ModelItem currentModelItem = modelItems.Dequeue();
532                 if (currentModelItem == null)
533                 {
534                     continue;
535                 }
536
537                 if (matcher(currentModelItem))
538                 {
539                     foundItem = currentModelItem;
540                     break;
541                 }
542
543                 List<ModelItem> neighbors = GetNeighbors(currentModelItem, extraShouldSearchThroughProperties);
544
545                 foreach (ModelItem neighbor in neighbors)
546                 {
547                     if (!alreadyVisited.Contains(neighbor))
548                     {
549                         alreadyVisited.Add(neighbor);
550                         modelItems.Enqueue(neighbor);
551                     }
552                 }
553             }
554             return foundItem;
555         }
556
557         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
558           Justification = "If the property getter threw here we dont want to crash, we just dont want to wrap that property value")]
559         [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
560             Justification = "If the property getter threw here we dont want to crash, we just dont want to wrap that property value")]
561         internal ModelItem GetValue(ModelPropertyImpl dataModelProperty)
562         {
563             Fx.Assert(dataModelProperty != null, "modelproperty should not be null");
564             Fx.Assert(dataModelProperty.Parent is IModelTreeItem, "modelproperty.Parent should be an IModelTreeItem");
565             IModelTreeItem parent = (IModelTreeItem)dataModelProperty.Parent;
566             ModelItem value;
567
568             // always reevaluate attached properties. the cache in attached properties case is only to remember the old value.
569             if (!dataModelProperty.IsAttached && parent.ModelPropertyStore.ContainsKey(dataModelProperty.Name))
570             {
571                 value = parent.ModelPropertyStore[dataModelProperty.Name];
572             }
573             // Create a ModelItem on demand for the value of the property.
574             else
575             {
576                 try
577                 {
578                     value = WrapAsModelItem(dataModelProperty.PropertyDescriptor.GetValue(parent.ModelItem.GetCurrentValue()));
579                 }
580                 catch (System.Exception)
581                 {
582                     // GetValue throws an exception if Value is not available
583                     value = null;
584                 }
585                 if (value != null)
586                 {
587                     if (!dataModelProperty.IsAttached)
588                     {
589                         parent.ModelPropertyStore.Add(dataModelProperty.Name, value);
590                         this.graphManager.OnPropertyEdgeAdded(dataModelProperty.Name, (ModelItem)parent, value);
591                     }
592                 }
593             }
594             return value;
595         }
596
597         internal ModelItem SetValue(ModelPropertyImpl modelProperty, object value)
598         {
599             Fx.Assert(modelProperty != null, "modelProperty should not be null");
600             ModelItem newValueModelItem = null;
601
602             RefreshPropertiesAttribute refreshPropertiesAttribute = ExtensibilityAccessor.GetAttribute<RefreshPropertiesAttribute>(modelProperty.Attributes);
603             if (refreshPropertiesAttribute != null && refreshPropertiesAttribute.RefreshProperties == RefreshProperties.All)
604             {
605                 Dictionary<string, ModelItem> modelPropertyStore = ((IModelTreeItem)modelProperty.Parent).ModelPropertyStore;
606                 Dictionary<string, ModelItem> removedModelItems = new Dictionary<string, ModelItem>(modelPropertyStore);
607                 modelPropertyStore.Clear();
608
609                 foreach (KeyValuePair<string, ModelItem> kvp in removedModelItems)
610                 {
611                     if (kvp.Value != null)
612                     {
613                         this.OnPropertyEdgeRemoved(kvp.Key, modelProperty.Parent, kvp.Value);
614                     }
615                 }
616             }
617
618             if (value is ModelItem)
619             {
620                 newValueModelItem = (ModelItem)value;
621             }
622             else
623             {
624                 newValueModelItem = WrapAsModelItem(value);
625             }
626             // dont do deferred updates for attached properties
627             if (modelProperty.IsAttached)
628             {
629                 modelProperty.SetValueCore(newValueModelItem);
630             }
631             else
632             {
633                 PropertyChange propertyChange = new PropertyChange()
634                 {
635                     Owner = modelProperty.Parent,
636                     PropertyName = modelProperty.Name,
637                     OldValue = modelProperty.Value,
638                     NewValue = newValueModelItem,
639                     ModelTreeManager = this
640                 };
641                 AddToCurrentEditingScope(propertyChange);
642             }
643
644
645             return newValueModelItem;
646         }
647
648         internal void AddToCurrentEditingScope(Change change)
649         {
650             EditingScope editingScope;
651             if (editingScopes.Count > 0)
652             {
653                 editingScope = (EditingScope)editingScopes.Peek();
654                 // Automatic generated change during apply changes of Redo/Undo should be ignored.
655                 if (!RedoUndoInProgress)
656                 {
657                     editingScope.Changes.Add(change);
658                 }
659             }
660             else
661             {
662                 //edit operation without editingscope create an editing scope and complete it immediately.
663                 editingScope = CreateEditingScope(change.Description);
664                 editingScope.Changes.Add(change);
665                 try
666                 {
667                     editingScope.Complete();
668                 }
669                 catch
670                 {
671                     editingScope.Revert();
672                     throw;
673                 }
674
675             }
676         }
677
678         internal bool CanCreateImmediateEditingScope()
679         {
680             return this.editingScopes.Count == 0 && !this.Context.Services.GetService<UndoEngine>().IsBookmarkInPlace;
681         }
682
683         internal EditingScope CreateEditingScope(string description, bool shouldApplyChangesImmediately)
684         {
685             EditingScope editingScope = null;
686             EditingScope outerScope = editingScopes.Count > 0 ? (EditingScope)editingScopes.Peek() : null;
687             if (shouldApplyChangesImmediately)
688             {
689                 // shouldApplyChangesImmediately won't have any effect if outer scope exists
690                 if (outerScope != null)
691                 {
692                     return null;
693                 }
694                 this.immediateEditingScope = context.Services.GetRequiredService<UndoEngine>().CreateImmediateEditingScope(description, this);
695                 editingScope = this.immediateEditingScope;
696                 // ImmediateEditingScope should not be pushed onto the editingScopes,
697                 //  otherwise it will become delay applied instead of immediate applied.
698             }
699             else
700             {
701                 editingScope = new EditingScope(this, outerScope);
702                 editingScopes.Push(editingScope);
703             }
704             editingScope.Description = description;
705             return editingScope;
706         }
707
708         internal EditingScope CreateEditingScope(string description)
709         {
710             return this.CreateEditingScope(description, false);
711         }
712
713         internal void NotifyPropertyChange(ModelPropertyImpl dataModelProperty, ModelChangeInfo changeInfo)
714         {
715             modelService.OnModelPropertyChanged(dataModelProperty, changeInfo);
716         }
717
718         internal void SyncModelAndText()
719         {
720             // Place holder for xaml generation ModelTreeManager now is instance only.
721         }
722
723         internal ModelItem WrapAsModelItem(object instance)
724         {
725             ModelItem modelItem = GetModelItem(instance);
726
727             if (null != instance && null == modelItem)
728             {
729                 modelItem = CreateModelItem(null, instance);
730             }
731             return modelItem;
732         }
733
734         internal ModelItem GetModelItem(object instance)
735         {
736             return this.GetModelItem(instance, false);
737         }
738
739         public ModelItem GetModelItem(object instance, bool shouldExpandModelTree)
740         {
741             if (instance == null)
742             {
743                 return null;
744             }
745
746             ModelItem modelItem = null;
747             WeakReference mappedModelItem = null;
748             objectMap.TryGetValue(instance, out mappedModelItem);
749             if (mappedModelItem != null)
750             {
751                 modelItem = (ModelItem)mappedModelItem.Target;
752             }
753
754             if (modelItem == null && shouldExpandModelTree)
755             {
756                 if (instance is ValueType || instance is string)
757                 {
758                     return null;
759                 }
760
761                 modelItem = FindFirst(this.Root, (m) => { return m.GetCurrentValue() == instance; });
762             }
763
764             return modelItem;
765         }
766
767         internal void RegisterModelTreeChangeEvents(EditingScope editingScope)
768         {
769             this.ModelItemsAdded += editingScope.EditingScope_ModelItemsAdded;
770             this.ModelItemsRemoved += editingScope.EditingScope_ModelItemsRemoved;
771         }
772
773         internal void UnregisterModelTreeChangeEvents(EditingScope editingScope)
774         {
775             this.ModelItemsAdded -= editingScope.EditingScope_ModelItemsAdded;
776             this.ModelItemsRemoved -= editingScope.EditingScope_ModelItemsRemoved;
777         }
778
779         // The method should be called when an EditingScope completed. But if the EditingScope has an outer EditingScope,
780         // Changes are not applied, so itemsAdded and itemsRemoved won't update until the outer EditingScope.Complete()
781         internal void OnEditingScopeCompleted(EditingScope modelEditingScopeImpl)
782         {
783             if (editingScopes.Contains(modelEditingScopeImpl))
784             {
785                 editingScopes.Pop();
786             }
787
788             if (editingScopes.Count == 0 && this.immediateEditingScope != null)
789             {
790                 // if immediateEditingScope is in place and last nested normal editing scope completes,
791                 // we copy the information from the nested normal editing scope to the immediateEditingScope
792                 // and put the Changes into undo engine, so that when immediateEditingScope completes the undo unit
793                 // generated by nested editing scope will be collected as one undo unit
794                 if (this.immediateEditingScope != modelEditingScopeImpl)
795                 {
796                     UndoEngine undoEngine = this.Context.Services.GetService<UndoEngine>();
797
798                     this.immediateEditingScope.Changes.AddRange(modelEditingScopeImpl.Changes);
799                     this.immediateEditingScope.HasModelChanges = this.immediateEditingScope.HasModelChanges || modelEditingScopeImpl.HasModelChanges;
800                     this.immediateEditingScope.HasEffectiveChanges = this.immediateEditingScope.HasEffectiveChanges || modelEditingScopeImpl.HasEffectiveChanges;
801                     this.immediateEditingScope.HandleModelItemsAdded(modelEditingScopeImpl.ItemsAdded);
802                     this.immediateEditingScope.HandleModelItemsRemoved(modelEditingScopeImpl.ItemsRemoved);
803
804                     if (modelEditingScopeImpl.HasEffectiveChanges)
805                     {
806                         if (!this.RedoUndoInProgress && !modelEditingScopeImpl.SuppressUndo && undoEngine != null)
807                         {
808                             undoEngine.AddUndoUnit(new EditingScopeUndoUnit(this.Context, this, modelEditingScopeImpl));
809                         }
810                     }
811                 }
812             }
813
814             if (editingScopes.Count == 0 && !(modelEditingScopeImpl is ImmediateEditingScope) && modelEditingScopeImpl.HasModelChanges)
815             {
816                 if (!modelEditingScopeImpl.SuppressValidationOnComplete)
817                 {
818                     ValidationService validationService = this.Context.Services.GetService<ValidationService>();
819                     if (validationService != null)
820                     {
821                         validationService.ValidateWorkflow(ValidationReason.ModelChange);
822                     }
823                 }
824             }
825
826             if (this.immediateEditingScope == modelEditingScopeImpl)
827             {
828                 this.immediateEditingScope = null;
829             }
830
831             // if the outer most scope completed notify listeners
832             if (this.EditingScopeCompleted != null && editingScopes.Count == 0 && this.immediateEditingScope == null)
833             {
834                 this.EditingScopeCompleted(this, new EditingScopeEventArgs() { EditingScope = modelEditingScopeImpl });
835             }
836
837             Fx.Assert(editingScopes.Count == 0 || (modelEditingScopeImpl.ItemsAdded.Count == 0 && modelEditingScopeImpl.ItemsRemoved.Count == 0), "Inner editing scope shouldn't have changes applied.");
838 #if DEBUG
839             this.graphManager.VerifyBackPointers();
840 #endif
841         }
842
843         internal bool CanEditingScopeComplete(EditingScope modelEditingScopeImpl)
844         {
845             ReadOnlyState readOnlyState = this.Context.Items.GetValue<ReadOnlyState>();
846             return (modelEditingScopeImpl == editingScopes.Peek()) && (readOnlyState == null || !readOnlyState.IsReadOnly);
847         }
848
849         internal void OnEditingScopeReverted(EditingScope modelEditingScopeImpl)
850         {
851             if (editingScopes.Contains(modelEditingScopeImpl))
852             {
853                 editingScopes.Pop();
854             }
855
856             if (this.immediateEditingScope == modelEditingScopeImpl)
857             {
858                 this.immediateEditingScope = null;
859             }
860         }
861
862         internal static IList<ModelItem> DepthFirstSearch(ModelItem currentItem, Predicate<Type> filter, Predicate<ModelItem> shouldTraverseSubTree, bool preOrder)
863         {
864             IList<ModelItem> foundItems = new List<ModelItem>();
865             HashSet<ModelItem> alreadyVisitedItems = new HashSet<ModelItem>();
866             RecursiveDepthFirstSearch(currentItem, filter, shouldTraverseSubTree, foundItems, alreadyVisitedItems, preOrder);
867             return foundItems;
868         }
869
870         private static void RecursiveDepthFirstSearch(ModelItem currentItem, Predicate<Type> filter, Predicate<ModelItem> shouldTraverseSubTree, IList<ModelItem> foundItems, HashSet<ModelItem> alreadyVisitedItems, bool preOrder)
871         {
872             if (currentItem == null)
873             {
874                 return;
875             }
876
877             if (typeof(Type).IsAssignableFrom(currentItem.ItemType))
878             {
879                 return;
880             }
881
882             if (currentItem.ItemType.IsGenericType && currentItem.ItemType.GetGenericTypeDefinition() == typeof(Action<>))
883             {
884                 return;
885             }
886
887             if (!shouldTraverseSubTree(currentItem))
888             {
889                 return;
890             }
891
892             if (preOrder)
893             {
894                 if (filter(currentItem.ItemType))
895                 {
896                     foundItems.Add(currentItem);
897                 }
898             }
899
900             alreadyVisitedItems.Add(currentItem);
901
902             List<ModelItem> neighbors = GetNeighbors(currentItem, (m) => true);
903             foreach (ModelItem neighbor in neighbors)
904             {
905                 if (!alreadyVisitedItems.Contains(neighbor))
906                 {
907                     RecursiveDepthFirstSearch(neighbor, filter, shouldTraverseSubTree, foundItems, alreadyVisitedItems, preOrder);
908                 }
909             }
910
911             if (!preOrder)
912             {
913                 if (filter(currentItem.ItemType))
914                 {
915                     foundItems.Add(currentItem);
916                 }
917             }
918         }
919
920         private void OnModelItemsAdded(IEnumerable<ModelItem> addedModelItems)
921         {
922             Fx.Assert(addedModelItems != null, "addedModelItems should not be null.");
923             EventHandler<ModelItemsAddedEventArgs> tempModelItemsAdded = this.ModelItemsAdded;
924             if (tempModelItemsAdded != null)
925             {
926                 tempModelItemsAdded(this, new ModelItemsAddedEventArgs(addedModelItems));
927             }
928         }
929
930         private void OnModelItemsRemoved(IEnumerable<ModelItem> removedModelItems)
931         {
932             Fx.Assert(removedModelItems != null, "removedModelItems should not be null.");
933             EventHandler<ModelItemsRemovedEventArgs> tempModelItemsRemoved = this.ModelItemsRemoved;
934             if (tempModelItemsRemoved != null)
935             {
936                 tempModelItemsRemoved(this, new ModelItemsRemovedEventArgs(removedModelItems));
937             }
938         }
939
940         internal class ModelGraphManager : GraphManager<ModelItem, Edge, BackPointer>
941         {
942             private ModelTreeManager modelTreeManager;
943
944             public ModelGraphManager(ModelTreeManager modelTreeManager)
945             {
946                 Fx.Assert(modelTreeManager != null, "modelTreeManager should not be null");
947
948                 this.modelTreeManager = modelTreeManager;
949             }
950
951             public void OnPropertyEdgeAdded(string propertyName, ModelItem from, ModelItem to)
952             {
953                 base.OnEdgeAdded(new Edge(propertyName, from, to));
954             }
955
956             public void OnItemEdgeAdded(ModelItem from, ModelItem to)
957             {
958                 base.OnEdgeAdded(new Edge(from, to));
959             }
960
961             public void OnPropertyEdgeRemoved(string propertyName, ModelItem from, ModelItem to)
962             {
963                 base.OnEdgeRemoved(new Edge(propertyName, from, to));
964             }
965
966             public void OnItemEdgeRemoved(ModelItem from, ModelItem to)
967             {
968                 base.OnEdgeRemoved(new Edge(from, to));
969             }
970
971             public new void OnRootChanged(ModelItem oldRoot, ModelItem newRoot)
972             {
973                 base.OnRootChanged(oldRoot, newRoot);
974             }
975
976             protected override ModelItem Root
977             {
978                 get { return this.modelTreeManager.Root; }
979             }
980
981             protected override IEnumerable<ModelItem> GetVertices()
982             {
983                 foreach (WeakReference weakReference in this.modelTreeManager.objectMap.Values)
984                 {
985                     ModelItem modelItem = weakReference.Target as ModelItem;
986                     if (modelItem != null)
987                     {
988                         yield return modelItem;
989                     }
990                 }
991             }
992
993             // This method will not expand any ModelItem
994             protected override IEnumerable<Edge> GetOutEdges(ModelItem vertex)
995             {
996                 Fx.Assert(vertex != null, "vertex should not be null");
997
998                 List<Edge> edges = new List<Edge>();
999
1000                 foreach (KeyValuePair<string, ModelItem> kvp in ((IModelTreeItem)vertex).ModelPropertyStore)
1001                 {
1002                     if (kvp.Value != null)
1003                     {
1004                         edges.Add(new Edge(kvp.Key, vertex, kvp.Value));
1005                     }
1006                 }
1007
1008                 ModelItemCollection collection = vertex as ModelItemCollection;
1009                 if (collection != null)
1010                 {
1011                     foreach (ModelItem modelItem in collection.Distinct())
1012                     {
1013                         if (modelItem != null)
1014                         {
1015                             edges.Add(new Edge(vertex, modelItem));
1016                         }
1017                     }
1018                 }
1019
1020                 ModelItemDictionaryImpl dictionary = vertex as ModelItemDictionaryImpl;
1021                 if (dictionary != null)
1022                 {
1023                     List<ModelItem> items = new List<ModelItem>(dictionary.Keys);
1024                     items.AddRange(dictionary.Values);
1025                     items.Add(dictionary.updateKeySavedValue);
1026                     foreach (ModelItem modelItem in items.Distinct())
1027                     {
1028                         if (modelItem != null)
1029                         {
1030                             edges.Add(new Edge(vertex, modelItem));
1031                         }
1032                     }
1033                 }
1034
1035                 return edges;
1036             }
1037
1038             protected override IEnumerable<BackPointer> GetBackPointers(ModelItem vertex)
1039             {
1040                 List<BackPointer> backPointers = new List<BackPointer>();
1041
1042                 foreach (ModelItem parent in ((IModelTreeItem)vertex).ItemBackPointers)
1043                 {
1044                     backPointers.Add(new BackPointer(vertex, parent));
1045                 }
1046
1047                 foreach (ModelProperty property in vertex.Sources)
1048                 {
1049                     backPointers.Add(new BackPointer(property.Name, vertex, property.Parent));
1050                 }
1051
1052                 foreach (BackPointer backPointer in ((IModelTreeItem)vertex).ExtraPropertyBackPointers)
1053                 {
1054                     backPointers.Add(backPointer);
1055                 }
1056
1057                 return backPointers;
1058             }
1059
1060             protected override ModelItem GetDestinationVertexFromEdge(Edge edge)
1061             {
1062                 return edge.DestinationVertex;
1063             }
1064
1065             protected override ModelItem GetSourceVertexFromEdge(Edge edge)
1066             {
1067                 return edge.SourceVertex;
1068             }
1069
1070             protected override ModelItem GetDestinationVertexFromBackPointer(BackPointer backPointer)
1071             {
1072                 return backPointer.DestinationVertex;
1073             }
1074
1075             protected override void RemoveAssociatedBackPointer(Edge edge)
1076             {
1077                 if (edge.LinkType == LinkType.Property)
1078                 {
1079                     ModelProperty modelProperty = edge.SourceVertex.Properties[edge.PropertyName];
1080                     if (modelProperty != null)
1081                     {
1082                         ((IModelTreeItem)edge.DestinationVertex).RemoveSource(modelProperty);
1083                     }
1084                     else
1085                     {
1086                         // in case of custom type descriptor, it may return a list of ModelProperties different from IModelTreeItem.ModelPropertyStore
1087                         // manually manipulate IModelTreeItem.Sources to remove back pointer
1088                         ((IModelTreeItem)edge.DestinationVertex).RemoveSource(edge.SourceVertex, edge.PropertyName);
1089                     }
1090                 }
1091                 else
1092                 {
1093                     Fx.Assert(edge.LinkType == LinkType.Item, "unknown LinkType");
1094                     ((IModelTreeItem)edge.DestinationVertex).RemoveParent(edge.SourceVertex);
1095                 }
1096             }
1097
1098             protected override void AddAssociatedBackPointer(Edge edge)
1099             {
1100                 if (edge.LinkType == LinkType.Property)
1101                 {
1102                     ModelProperty modelProperty = edge.SourceVertex.Properties[edge.PropertyName];
1103                     if (modelProperty != null)
1104                     {
1105                         ((IModelTreeItem)edge.DestinationVertex).SetSource(modelProperty);
1106                     }
1107                     else
1108                     {
1109                         ((IModelTreeItem)edge.DestinationVertex).ExtraPropertyBackPointers.Add(new BackPointer(edge.PropertyName, edge.DestinationVertex, edge.SourceVertex));
1110                     }
1111                 }
1112                 else
1113                 {
1114                     Fx.Assert(edge.LinkType == LinkType.Item, "unknown LinkType");
1115                     ((IModelTreeItem)edge.DestinationVertex).SetParent(edge.SourceVertex);
1116                 }
1117             }
1118
1119             protected override bool HasBackPointer(Edge edge)
1120             {
1121                 ModelItem from = edge.SourceVertex;
1122
1123                 if (edge.LinkType == LinkType.Property)
1124                 {
1125                     foreach (ModelProperty p in edge.DestinationVertex.Sources)
1126                     {
1127                         if (p.Parent == from && p.Name == edge.PropertyName)
1128                         {
1129                             return true;
1130                         }
1131                     }
1132
1133                     foreach (BackPointer bp in ((IModelTreeItem)edge.DestinationVertex).ExtraPropertyBackPointers)
1134                     {
1135                         if (bp.DestinationVertex == from && bp.PropertyName == edge.PropertyName)
1136                         {
1137                             return true;
1138                         }
1139                     }
1140
1141                     return false;
1142                 }
1143                 else
1144                 {
1145                     Fx.Assert(edge.LinkType == LinkType.Item, "unknown LinkType");
1146                     return ((IModelTreeItem)edge.DestinationVertex).ItemBackPointers.Contains(from);
1147                 }
1148             }
1149
1150             protected override bool HasAssociatedEdge(BackPointer backPointer)
1151             {
1152                 if (backPointer.LinkType == LinkType.Property)
1153                 {
1154                     return ((IModelTreeItem)backPointer.DestinationVertex).ModelPropertyStore[backPointer.PropertyName] == backPointer.SourceVertex;
1155                 }
1156                 else
1157                 {
1158                     Fx.Assert(backPointer.LinkType == LinkType.Item, "unknown LinkType");
1159
1160                     ModelItemCollection collection = backPointer.DestinationVertex as ModelItemCollection;
1161                     if (collection != null)
1162                     {
1163                         return collection.Contains(backPointer.SourceVertex);
1164                     }
1165
1166                     ModelItemDictionary dictionary = (ModelItemDictionary)backPointer.DestinationVertex;
1167                     return dictionary.Keys.Concat(dictionary.Values).Contains(backPointer.SourceVertex);
1168                 }
1169             }
1170
1171             protected override void OnVerticesBecameReachable(IEnumerable<ModelItem> reachableVertices)
1172             {
1173                 Fx.Assert(reachableVertices != null, "reachableVertices should not be null");
1174                 this.modelTreeManager.OnModelItemsAdded(reachableVertices);
1175             }
1176
1177             protected override void OnVerticesBecameUnreachable(IEnumerable<ModelItem> unreachableVertices)
1178             {
1179                 Fx.Assert(unreachableVertices != null, "unreachableVertices should not be null");
1180                 this.modelTreeManager.OnModelItemsRemoved(unreachableVertices);
1181             }
1182         }
1183
1184         class DictionaryTypeDescriptionProvider : TypeDescriptionProvider
1185         {
1186             Type type;
1187             public DictionaryTypeDescriptionProvider(Type type)
1188                 : base(TypeDescriptor.GetProvider(type))
1189             {
1190                 this.type = type;
1191             }
1192
1193             public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
1194             {
1195                 ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
1196                 return new DictionaryTypeDescriptor(defaultDescriptor, this.type);
1197             }
1198         }
1199
1200         class DictionaryTypeDescriptor : CustomTypeDescriptor
1201         {
1202             Type type;
1203
1204             public DictionaryTypeDescriptor(ICustomTypeDescriptor parent, Type type)
1205                 : base(parent)
1206             {
1207                 this.type = type;
1208             }
1209
1210             // Expose one more property, a collection of MutableKeyValuePairs,  described by ItemsCollectionPropertyDescriptor
1211             public override PropertyDescriptorCollection GetProperties()
1212             {
1213                 return new PropertyDescriptorCollection(base.GetProperties().Cast<PropertyDescriptor>()
1214                     .Union(new PropertyDescriptor[] { new ItemsCollectionPropertyDescriptor(type) }).ToArray());
1215             }
1216
1217             // Expose one more property, a collection of MutableKeyValuePairs,  described by ItemsCollectionPropertyDescriptor
1218             public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
1219             {
1220                 return new PropertyDescriptorCollection(base.GetProperties(attributes).Cast<PropertyDescriptor>()
1221                     .Union(new PropertyDescriptor[] { new ItemsCollectionPropertyDescriptor(type) }).ToArray());
1222             }
1223         }
1224
1225         class ItemsCollectionPropertyDescriptor : PropertyDescriptor
1226         {
1227             Type dictionaryType;
1228             Type[] genericArguments;
1229             Type kvpairType;
1230             Type itemType;
1231             Type propertyType;
1232
1233             internal ItemsCollectionPropertyDescriptor(Type type)
1234                 : base("ItemsCollection", null)
1235             {
1236                 this.dictionaryType = type;
1237             }
1238
1239             Type[] GenericArguments
1240             {
1241                 get
1242                 {
1243                     if (this.genericArguments == null)
1244                     {
1245                         object[] result = new object[2] { false, false };
1246                         Type[] interfaces = this.ComponentType.FindInterfaces(ModelTreeManager.CheckInterface, result);
1247                         foreach (Type type in interfaces)
1248                         {
1249                             if (type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
1250                             {
1251                                 this.genericArguments = type.GetGenericArguments();
1252                                 Fx.Assert(this.genericArguments.Length == 2, "this.genericArguments.Length should be = 2");
1253                                 return this.genericArguments;
1254                             }
1255                         }
1256                         Debug.Fail("Cannot find generic arguments for IDictionary<,>.");
1257                     }
1258                     return this.genericArguments;
1259                 }
1260             }
1261
1262             [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
1263             Type KVPairType
1264             {
1265                 get
1266                 {
1267                     if (this.kvpairType == null)
1268                     {
1269                         this.kvpairType = typeof(KeyValuePair<,>).MakeGenericType(this.GenericArguments);
1270                     }
1271                     return this.kvpairType;
1272                 }
1273             }
1274
1275             [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
1276             Type ItemType
1277             {
1278                 get
1279                 {
1280                     if (this.itemType == null)
1281                     {
1282                         this.itemType = typeof(ModelItemKeyValuePair<,>).MakeGenericType(this.GenericArguments);
1283                     }
1284                     return this.itemType;
1285                 }
1286             }
1287
1288             public override Type ComponentType
1289             {
1290                 get { return this.dictionaryType; }
1291             }
1292
1293             public override bool IsReadOnly
1294             {
1295                 get
1296                 {
1297                     return true;
1298                 }
1299             }
1300
1301             public override Type PropertyType
1302             {
1303                 get
1304                 {
1305                     if (this.propertyType == null)
1306                     {
1307                         this.propertyType = typeof(DictionaryItemsCollection<,>).MakeGenericType(this.GenericArguments);
1308                     }
1309                     return this.propertyType;
1310                 }
1311             }
1312
1313             public override bool IsBrowsable
1314             {
1315                 get
1316                 {
1317                     return false;
1318                 }
1319             }
1320
1321             public override bool CanResetValue(object component)
1322             {
1323                 return false;
1324             }
1325
1326             public override object GetValue(object component)
1327             {
1328                 return Activator.CreateInstance(this.PropertyType, new object[] { component });
1329             }
1330
1331             public override void ResetValue(object component)
1332             {
1333                 Debug.Fail("ResetValue is not implemented.");
1334                 throw FxTrace.Exception.AsError(new NotImplementedException());
1335             }
1336
1337             public override void SetValue(object component, object value)
1338             {
1339                 Debug.Fail("SetValue is not implemented.");
1340                 throw FxTrace.Exception.AsError(new NotImplementedException());
1341             }
1342
1343             public override bool ShouldSerializeValue(object component)
1344             {
1345                 return false;
1346             }
1347         }
1348     }
1349 }