1 //-----------------------------------------------------------------------
2 // <copyright file="UniqueModelItemHelper.cs" company="Microsoft Corporation">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace System.Activities.Presentation.View.TreeView
9 using System.Activities.Presentation.Internal.PropertyEditing;
10 using System.Activities.Presentation.Model;
11 using System.Activities.Presentation.View.OutlineView;
12 using System.Collections.Generic;
15 internal static class UniqueModelItemHelper
17 // Return HashSet containing ModelItems found only through given property: Cannot reach property.Parent without
18 // hitting property from these immediate descendents of property.Value
19 // Set may contain some internal duplication -- all nodes, not just the root, of a linked tree will be included
21 // Caveat 1: Due to problems removing Parents (e.g. Case content sometimes holds references to FlowSwitch after
22 // Case removed), this method is not entirely reliable -- customers may occasionally need to reopen Designer
23 // Caveat 2: Due to lazy loading of Properties (and therefore the back-pointing Parents collection), may
24 // temporarily include non-unique ModelItems in returned set -- cleared as tree or designer views expand
26 // (Throughout, cannot use ModelItem.GetParentEnumerator because that does not check all Sources and Parents)
27 internal static HashSet<ModelItem> FindUniqueChildren(ModelProperty property)
29 HashSet<ModelItem> retval = new HashSet<ModelItem>();
30 if (null != property && null != property.Parent && null != property.Value)
32 ModelItem target = property.Parent;
33 ModelItem expected = property.Value;
34 HashSet<ModelItem> visited = new HashSet<ModelItem>();
36 // Check all immediate children of property.Value
37 ModelItemCollection collection = expected as ModelItemCollection;
38 if (null == collection)
40 ModelItemDictionary dictionary = expected as ModelItemDictionary;
41 if (null == dictionary)
44 // Can't use UniqueRoute because we're starting at expected
45 // Can't use EnqueueParents because we need to special-case given property
46 // Instead confirm property.Value is not referenced anywhere else
47 ModelItemImpl expectedImpl = expected as ModelItemImpl;
48 if (null != expectedImpl)
50 bool justThisSource = true;
52 // expectedImpl.InternalParents does not include ModelItems that are just Sources
53 if (0 == expectedImpl.InternalParents.Count)
55 // expectedImpl.InternalSources would be similar but adds a wrapper we don't need here
56 foreach (ModelProperty source in expected.Sources)
58 if (null != source.Parent && !visited.Contains(source.Parent))
60 visited.Add(source.Parent);
61 if (!property.Equals(source) && !expected.Equals(source) &&
62 null == ExtensibilityAccessor.GetAttribute<HidePropertyInOutlineViewAttribute>(source))
64 // Found a non-ignored property from somewhere else referencing expected
65 justThisSource = false;
73 // Found a Parent that's not a Source.Parent: property.Value is in some collection
74 justThisSource = false;
85 // ModelItemDictionary
86 foreach (KeyValuePair<ModelItem, ModelItem> child in dictionary)
88 if (null != child.Key && !visited.Contains(child.Key))
90 visited.Add(child.Key);
91 if (UniqueModelItemHelper.UniqueRoute(child.Key, target, expected))
93 retval.Add(child.Key);
97 if (null != child.Value && !visited.Contains(child.Value))
99 visited.Add(child.Value);
100 if (UniqueModelItemHelper.UniqueRoute(child.Value, target, expected))
102 retval.Add(child.Value);
110 // ModelItemCollection
111 foreach (ModelItem child in collection)
113 if (null != child && !visited.Contains(child))
116 if (UniqueModelItemHelper.UniqueRoute(child, target, expected))
128 // Enqueue Parents of given ModelItem
129 // Do not enqueue source Properties with a ViewIgnore attribute
130 private static void EnqueueParents(ModelItem item, Queue<ModelItem> queue)
132 Dictionary<ModelItem, int> nonSources = new Dictionary<ModelItem, int>();
135 // Initialize nonSources dictionary to hold all Parents
136 foreach (ModelItem parent in item.Parents)
138 if (nonSources.ContainsKey(parent))
140 ++nonSources[parent];
144 nonSources.Add(parent, 1);
148 // Enqueue Sources and remove found items from nonSources
149 foreach (ModelProperty source in item.Sources)
151 if (null != source.Parent)
153 if (null == ExtensibilityAccessor.GetAttribute<HidePropertyInOutlineViewAttribute>(source))
155 queue.Enqueue(source.Parent);
158 if (nonSources.ContainsKey(source.Parent))
160 --nonSources[source.Parent];
165 // Deal with the collections that contain this ModelItem
166 foreach (KeyValuePair<ModelItem, int> kvp in nonSources.Where((kvp) => 0 < kvp.Value))
168 queue.Enqueue(kvp.Key);
173 // Determine if targetParent is only reachable from item via expectedParent
174 // Return true if the only routes from item to targetParent include expectedParent; false otherwise
175 // Do not search past source Properties with a ViewIgnore attribute but continue looking for targetParent
176 private static bool UniqueRoute(ModelItem item, ModelItem targetParent, ModelItem expectedParent)
185 HashSet<ModelItem> visited = new HashSet<ModelItem>();
186 Queue<ModelItem> todo = new Queue<ModelItem>();
189 while (0 < todo.Count)
191 ModelItem parent = todo.Dequeue();
192 if (null != parent && !visited.Contains(parent))
196 if (parent.Equals(targetParent))
198 // Failure: Route was not unique, have reached target without passing expectedParent
202 else if (!parent.Equals(expectedParent))
204 UniqueModelItemHelper.EnqueueParents(parent, todo);