Fix XMM scanning on Mac x86.
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Xaml / ObjectToSourceLocationMapping.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Xaml
6 {
7     using System.Activities.Debugger;
8     using System.Activities.Presentation.Model;
9     using System.Activities.Presentation.ViewState;
10     using System.Collections.Generic;
11     using System.Linq;
12     using System.Runtime.Collections;
13
14     // This class is to create and hold the mapping of Activity to SourceLocation and SourceLocation to ModelItem.
15     // First, XamlReader write in activity object and its SourceLocation. Then model search service can pass in the
16     // ModelItem list to create a source location to model item mapping.
17     internal class ObjectToSourceLocationMapping
18     {
19         private IDictionary<object, SourceLocation> deserializedObjectToSourceLocationMapping;
20         private IDictionary<SourceLocation, ModelItem> sourceLocationToModelItemMapping;
21         private IDictionary<SourceLocation, ModelItem> viewStateSourceLocationToModelItemMapping;
22         private ModelSearchServiceImpl modelSearchService;
23
24         // Is the Object to SourceLocation mapping updated? generally means the file was reloaded.
25         private bool updateRequired;
26
27         internal ObjectToSourceLocationMapping(ModelSearchServiceImpl modelSearchService)
28         {
29             this.modelSearchService = modelSearchService;
30             this.deserializedObjectToSourceLocationMapping = new OrderedDictionary<object, SourceLocation>();
31             this.sourceLocationToModelItemMapping = new Dictionary<SourceLocation, ModelItem>();
32             this.viewStateSourceLocationToModelItemMapping = new Dictionary<SourceLocation, ModelItem>();
33         }
34
35         internal void Clear()
36         {
37             this.deserializedObjectToSourceLocationMapping.Clear();
38             this.updateRequired = true;
39         }
40
41         internal Dictionary<object, object> SourceLocationObjectToModelItemObjectMapping
42         {
43             get;
44             set;
45         }
46
47         internal Dictionary<string, SourceLocation> ViewStateDataSourceLocationMapping
48         {
49             get;
50             set;
51         }
52
53         internal void UpdateMap(object key, SourceLocation sourceLocation)
54         {
55             if (this.deserializedObjectToSourceLocationMapping.ContainsKey(key))
56             {
57                 this.deserializedObjectToSourceLocationMapping.Remove(key);
58             }
59
60             this.deserializedObjectToSourceLocationMapping.Add(key, new SourceLocation(/* fileName = */ null,
61                 sourceLocation.StartLine, sourceLocation.StartColumn,
62                 sourceLocation.EndLine, sourceLocation.EndColumn));
63         }
64
65         // create a SourceLocation to ModelItem mapping based on the current activity to SourceLocation mapping.
66         private void UpdateSourceLocationToModelItemMapping(IEnumerable<ModelItem> modelItemsOnDesigner)
67         {
68             Dictionary<object, SourceLocation> validMapping = GetValidSourceLocationMapping();
69             this.sourceLocationToModelItemMapping.Clear();
70             this.viewStateSourceLocationToModelItemMapping.Clear();
71             foreach (ModelItem modelItem in modelItemsOnDesigner)
72             {
73                 SourceLocation srcLocation = FindMatchSrcLocation(modelItem, validMapping);
74                 if (srcLocation != null)
75                 {
76                     sourceLocationToModelItemMapping.Add(srcLocation, modelItem);
77                 }
78                 string workflowViewStateIdRef = WorkflowViewState.GetIdRef(modelItem.GetCurrentValue());
79                 if (!String.IsNullOrEmpty(workflowViewStateIdRef))
80                 {
81                     SourceLocation viewStateSrcLocation = this.FindViewStateDataSrcLocationByViewStateIdRef(workflowViewStateIdRef);
82                     if (viewStateSrcLocation != null)
83                     {
84                         // In some cases duplicated key is possible, use indexer instead of Add() to avoid throw. 
85                         // See TFS bug 523908 for detailed information
86                         viewStateSourceLocationToModelItemMapping[viewStateSrcLocation] = modelItem;
87                     }
88                 }
89             }
90
91             this.updateRequired = false;
92         }
93
94         // find a modelitem whose SourceLocation contains the srcLocation passed in.
95         internal ModelItem FindModelItem(SourceLocation srcLocation)
96         {
97             this.EnsureUpdated();
98             return FindModelItemInMap(srcLocation, this.sourceLocationToModelItemMapping);
99         }
100
101         internal ModelItem FindModelItemOfViewState(SourceLocation srcLocation)
102         {
103             this.EnsureUpdated();
104             return FindModelItemInMap(srcLocation, this.viewStateSourceLocationToModelItemMapping);
105         }
106
107         internal SourceLocation FindSourceLocation(ModelItem modelItem)
108         {
109             this.EnsureUpdated();
110             KeyValuePair<SourceLocation, ModelItem>? matchingMappingRecord = sourceLocationToModelItemMapping.SingleOrDefault(kvp => object.ReferenceEquals(kvp.Value, modelItem));
111             if (matchingMappingRecord.HasValue)
112             {
113                 return matchingMappingRecord.Value.Key;
114             }
115             else
116             {
117                 return null;
118             }
119         }
120
121         private static ModelItem FindModelItemInMap(SourceLocation sourceLocation, IDictionary<SourceLocation, ModelItem> map)
122         {
123             SourceLocation exactSourceLocation = GetExactLocation(sourceLocation, map);
124             if (exactSourceLocation == null)
125             {
126                 return null;
127             }
128
129             return map[exactSourceLocation];
130         }
131
132         internal IEnumerable<ModelItem> GetObjectsWithSourceLocation()
133         {
134             this.EnsureUpdated();
135             return this.sourceLocationToModelItemMapping.Values;
136         }
137
138         private static SourceLocation FindSrcLocation(Dictionary<object, SourceLocation> mapping, Predicate<object> predicate)
139         {
140             object foundedObject = null;
141             if (mapping == null)
142             {
143                 return null;
144             }
145
146             foreach (object key in mapping.Keys)
147             {
148                 if (predicate(key))
149                 {
150                     foundedObject = key;
151                     break;
152                 }
153             }
154
155             if (foundedObject != null)
156             {
157                 SourceLocation result = mapping[foundedObject];
158                 mapping.Remove(foundedObject);
159                 return result;
160             }
161
162             return null;
163         }
164
165         private static SourceLocation FindMatchSrcLocation(ModelItem modelItem, Dictionary<object, SourceLocation> mapping)
166         {
167             object modelObject = modelItem.GetCurrentValue();
168             return FindSrcLocation(mapping, (key) =>
169                 {
170                     return object.ReferenceEquals(modelObject, key);
171                 });
172         }
173
174         private SourceLocation FindViewStateDataSrcLocationByViewStateIdRef(string workflowViewStateIdRef)
175         {
176             if (this.ViewStateDataSourceLocationMapping == null)
177             {
178                 return null;
179             }
180
181             SourceLocation sourceLocation = null;
182             this.ViewStateDataSourceLocationMapping.TryGetValue(workflowViewStateIdRef, out sourceLocation);
183             return sourceLocation;
184         }
185
186         // get the minimum source location which contains this source location and is in the mapping store.
187         private static SourceLocation GetExactLocation(SourceLocation approximateLocation, IDictionary<SourceLocation, ModelItem> mapping)
188         {
189             SourceLocation candidate = null;
190             foreach (SourceLocation srcLocation in mapping.Keys)
191             {
192                 // in the scope?
193                 if (srcLocation.Contains(approximateLocation))
194                 {
195                     if (candidate != null)
196                     {
197                         // More approximate?
198                         if (candidate.Contains(srcLocation))
199                         {
200                             candidate = srcLocation;
201                         }
202                     }
203                     else
204                     {
205                         candidate = srcLocation;
206                     }
207                 }
208             }
209
210             return candidate;
211         }
212
213         private void EnsureUpdated()
214         {
215             if (this.updateRequired)
216             {
217                 IEnumerable<ModelItem> itemsOnDesigner = this.modelSearchService.GetItemsOnDesigner(preOrder: false, excludeRoot: false, excludeErrorActivity: false, excludeExpression: false, includeOtherObjects: true);
218                 this.UpdateSourceLocationToModelItemMapping(itemsOnDesigner);
219             }
220         }
221
222         private Dictionary<object, SourceLocation> GetValidSourceLocationMapping()
223         {
224             Dictionary<object, SourceLocation> validSrcLocMapping = new Dictionary<object, SourceLocation>();
225             foreach (KeyValuePair<object, SourceLocation> entry in deserializedObjectToSourceLocationMapping)
226             {
227                 if (IsValidRange(entry.Value.StartLine, entry.Value.StartColumn, entry.Value.EndLine, entry.Value.EndColumn))
228                 {
229                     object sourceLocationObject = entry.Key;
230                     object modelItemObject;
231                     if (this.SourceLocationObjectToModelItemObjectMapping == null)
232                     {
233                         modelItemObject = sourceLocationObject;
234                     }
235                     else
236                     {
237                         this.SourceLocationObjectToModelItemObjectMapping.TryGetValue(sourceLocationObject, out modelItemObject);
238                     }
239
240                     if (modelItemObject != null)
241                     {
242                         validSrcLocMapping.Add(modelItemObject, entry.Value);
243                     }
244                 }
245             }
246             return validSrcLocMapping;
247
248         }
249
250         private static bool IsValidRange(int startLine, int startColumn, int endLine, int endColumn)
251         {
252             return
253                 (startLine > 0) && (startColumn > 0) && (endLine > 0) && (endColumn > 0) &&
254                 ((startLine < endLine) || (startLine == endLine && startColumn < endColumn));
255         }
256     }
257 }