[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / View / TreeView / UniqueModelItemHelper.cs
1 //-----------------------------------------------------------------------
2 // <copyright file="UniqueModelItemHelper.cs" company="Microsoft Corporation">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
6
7 namespace System.Activities.Presentation.View.TreeView
8 {
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;
13     using System.Linq;
14     
15     internal static class UniqueModelItemHelper
16     {
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
20         //
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
25         //
26         // (Throughout, cannot use ModelItem.GetParentEnumerator because that does not check all Sources and Parents)
27         internal static HashSet<ModelItem> FindUniqueChildren(ModelProperty property)
28         {
29             HashSet<ModelItem> retval = new HashSet<ModelItem>();
30             if (null != property && null != property.Parent && null != property.Value)
31             {
32                 ModelItem target = property.Parent;
33                 ModelItem expected = property.Value;
34                 HashSet<ModelItem> visited = new HashSet<ModelItem>();
35
36                 // Check all immediate children of property.Value
37                 ModelItemCollection collection = expected as ModelItemCollection;
38                 if (null == collection)
39                 {
40                     ModelItemDictionary dictionary = expected as ModelItemDictionary;
41                     if (null == dictionary)
42                     {
43                         // ModelItem
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)
49                         {
50                             bool justThisSource = true;
51
52                             // expectedImpl.InternalParents does not include ModelItems that are just Sources
53                             if (0 == expectedImpl.InternalParents.Count)
54                             {
55                                 // expectedImpl.InternalSources would be similar but adds a wrapper we don't need here
56                                 foreach (ModelProperty source in expected.Sources)
57                                 {
58                                     if (null != source.Parent && !visited.Contains(source.Parent))
59                                     {
60                                         visited.Add(source.Parent);
61                                         if (!property.Equals(source) && !expected.Equals(source) &&
62                                             null == ExtensibilityAccessor.GetAttribute<HidePropertyInOutlineViewAttribute>(source))
63                                         {
64                                             // Found a non-ignored property from somewhere else referencing expected
65                                             justThisSource = false;
66                                             break;
67                                         }
68                                     }
69                                 }
70                             }
71                             else
72                             {
73                                 // Found a Parent that's not a Source.Parent: property.Value is in some collection
74                                 justThisSource = false;
75                             }
76
77                             if (justThisSource)
78                             {
79                                 retval.Add(expected);
80                             }
81                         }
82                     }
83                     else
84                     {
85                         // ModelItemDictionary
86                         foreach (KeyValuePair<ModelItem, ModelItem> child in dictionary)
87                         {
88                             if (null != child.Key && !visited.Contains(child.Key))
89                             {
90                                 visited.Add(child.Key);
91                                 if (UniqueModelItemHelper.UniqueRoute(child.Key, target, expected))
92                                 {
93                                     retval.Add(child.Key);
94                                 }
95                             }
96
97                             if (null != child.Value && !visited.Contains(child.Value))
98                             {
99                                 visited.Add(child.Value);
100                                 if (UniqueModelItemHelper.UniqueRoute(child.Value, target, expected))
101                                 {
102                                     retval.Add(child.Value);
103                                 }
104                             }
105                         }
106                     }
107                 }
108                 else
109                 {
110                     // ModelItemCollection
111                     foreach (ModelItem child in collection)
112                     {
113                         if (null != child && !visited.Contains(child))
114                         {
115                             visited.Add(child);
116                             if (UniqueModelItemHelper.UniqueRoute(child, target, expected))
117                             {
118                                 retval.Add(child);
119                             }
120                         }
121                     }
122                 }
123             }
124
125             return retval;
126         }
127
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)
131         {
132             Dictionary<ModelItem, int> nonSources = new Dictionary<ModelItem, int>();
133             if (null != item)
134             {
135                 // Initialize nonSources dictionary to hold all Parents
136                 foreach (ModelItem parent in item.Parents)
137                 {
138                     if (nonSources.ContainsKey(parent))
139                     {
140                         ++nonSources[parent];
141                     }
142                     else
143                     {
144                         nonSources.Add(parent, 1);
145                     }
146                 }
147
148                 // Enqueue Sources and remove found items from nonSources
149                 foreach (ModelProperty source in item.Sources)
150                 {
151                     if (null != source.Parent)
152                     {
153                         if (null == ExtensibilityAccessor.GetAttribute<HidePropertyInOutlineViewAttribute>(source))
154                         {
155                             queue.Enqueue(source.Parent);
156                         }
157
158                         if (nonSources.ContainsKey(source.Parent))
159                         {
160                             --nonSources[source.Parent];
161                         }
162                     }
163                 }
164
165                 // Deal with the collections that contain this ModelItem
166                 foreach (KeyValuePair<ModelItem, int> kvp in nonSources.Where((kvp) => 0 < kvp.Value))
167                 {
168                     queue.Enqueue(kvp.Key);
169                 }
170             }
171         }
172
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)
177         {
178             bool retval = true;
179             if (null == item)
180             {
181                 retval = false;
182             }
183             else
184             {
185                 HashSet<ModelItem> visited = new HashSet<ModelItem>();
186                 Queue<ModelItem> todo = new Queue<ModelItem>();
187                 todo.Enqueue(item);
188
189                 while (0 < todo.Count)
190                 {
191                     ModelItem parent = todo.Dequeue();
192                     if (null != parent && !visited.Contains(parent))
193                     {
194                         visited.Add(parent);
195
196                         if (parent.Equals(targetParent))
197                         {
198                             // Failure: Route was not unique, have reached target without passing expectedParent
199                             retval = false;
200                             break;
201                         }
202                         else if (!parent.Equals(expectedParent))
203                         {
204                             UniqueModelItemHelper.EnqueueParents(parent, todo);
205                         }
206                     }
207                 }
208             }
209
210             return retval;
211         }
212     }
213 }