1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.Activities.Presentation.Xaml
7 using System.Activities.Debugger;
8 using System.Activities.Presentation.Model;
9 using System.Activities.Presentation.ViewState;
10 using System.Collections.Generic;
12 using System.Runtime.Collections;
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
19 private IDictionary<object, SourceLocation> deserializedObjectToSourceLocationMapping;
20 private IDictionary<SourceLocation, ModelItem> sourceLocationToModelItemMapping;
21 private IDictionary<SourceLocation, ModelItem> viewStateSourceLocationToModelItemMapping;
22 private ModelSearchServiceImpl modelSearchService;
24 // Is the Object to SourceLocation mapping updated? generally means the file was reloaded.
25 private bool updateRequired;
27 internal ObjectToSourceLocationMapping(ModelSearchServiceImpl modelSearchService)
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>();
37 this.deserializedObjectToSourceLocationMapping.Clear();
38 this.updateRequired = true;
41 internal Dictionary<object, object> SourceLocationObjectToModelItemObjectMapping
47 internal Dictionary<string, SourceLocation> ViewStateDataSourceLocationMapping
53 internal void UpdateMap(object key, SourceLocation sourceLocation)
55 if (this.deserializedObjectToSourceLocationMapping.ContainsKey(key))
57 this.deserializedObjectToSourceLocationMapping.Remove(key);
60 this.deserializedObjectToSourceLocationMapping.Add(key, new SourceLocation(/* fileName = */ null,
61 sourceLocation.StartLine, sourceLocation.StartColumn,
62 sourceLocation.EndLine, sourceLocation.EndColumn));
65 // create a SourceLocation to ModelItem mapping based on the current activity to SourceLocation mapping.
66 private void UpdateSourceLocationToModelItemMapping(IEnumerable<ModelItem> modelItemsOnDesigner)
68 Dictionary<object, SourceLocation> validMapping = GetValidSourceLocationMapping();
69 this.sourceLocationToModelItemMapping.Clear();
70 this.viewStateSourceLocationToModelItemMapping.Clear();
71 foreach (ModelItem modelItem in modelItemsOnDesigner)
73 SourceLocation srcLocation = FindMatchSrcLocation(modelItem, validMapping);
74 if (srcLocation != null)
76 sourceLocationToModelItemMapping.Add(srcLocation, modelItem);
78 string workflowViewStateIdRef = WorkflowViewState.GetIdRef(modelItem.GetCurrentValue());
79 if (!String.IsNullOrEmpty(workflowViewStateIdRef))
81 SourceLocation viewStateSrcLocation = this.FindViewStateDataSrcLocationByViewStateIdRef(workflowViewStateIdRef);
82 if (viewStateSrcLocation != null)
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;
91 this.updateRequired = false;
94 // find a modelitem whose SourceLocation contains the srcLocation passed in.
95 internal ModelItem FindModelItem(SourceLocation srcLocation)
98 return FindModelItemInMap(srcLocation, this.sourceLocationToModelItemMapping);
101 internal ModelItem FindModelItemOfViewState(SourceLocation srcLocation)
103 this.EnsureUpdated();
104 return FindModelItemInMap(srcLocation, this.viewStateSourceLocationToModelItemMapping);
107 internal SourceLocation FindSourceLocation(ModelItem modelItem)
109 this.EnsureUpdated();
110 KeyValuePair<SourceLocation, ModelItem>? matchingMappingRecord = sourceLocationToModelItemMapping.SingleOrDefault(kvp => object.ReferenceEquals(kvp.Value, modelItem));
111 if (matchingMappingRecord.HasValue)
113 return matchingMappingRecord.Value.Key;
121 private static ModelItem FindModelItemInMap(SourceLocation sourceLocation, IDictionary<SourceLocation, ModelItem> map)
123 SourceLocation exactSourceLocation = GetExactLocation(sourceLocation, map);
124 if (exactSourceLocation == null)
129 return map[exactSourceLocation];
132 internal IEnumerable<ModelItem> GetObjectsWithSourceLocation()
134 this.EnsureUpdated();
135 return this.sourceLocationToModelItemMapping.Values;
138 private static SourceLocation FindSrcLocation(Dictionary<object, SourceLocation> mapping, Predicate<object> predicate)
140 object foundedObject = null;
146 foreach (object key in mapping.Keys)
155 if (foundedObject != null)
157 SourceLocation result = mapping[foundedObject];
158 mapping.Remove(foundedObject);
165 private static SourceLocation FindMatchSrcLocation(ModelItem modelItem, Dictionary<object, SourceLocation> mapping)
167 object modelObject = modelItem.GetCurrentValue();
168 return FindSrcLocation(mapping, (key) =>
170 return object.ReferenceEquals(modelObject, key);
174 private SourceLocation FindViewStateDataSrcLocationByViewStateIdRef(string workflowViewStateIdRef)
176 if (this.ViewStateDataSourceLocationMapping == null)
181 SourceLocation sourceLocation = null;
182 this.ViewStateDataSourceLocationMapping.TryGetValue(workflowViewStateIdRef, out sourceLocation);
183 return sourceLocation;
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)
189 SourceLocation candidate = null;
190 foreach (SourceLocation srcLocation in mapping.Keys)
193 if (srcLocation.Contains(approximateLocation))
195 if (candidate != null)
198 if (candidate.Contains(srcLocation))
200 candidate = srcLocation;
205 candidate = srcLocation;
213 private void EnsureUpdated()
215 if (this.updateRequired)
217 IEnumerable<ModelItem> itemsOnDesigner = this.modelSearchService.GetItemsOnDesigner(preOrder: false, excludeRoot: false, excludeErrorActivity: false, excludeExpression: false, includeOtherObjects: true);
218 this.UpdateSourceLocationToModelItemMapping(itemsOnDesigner);
222 private Dictionary<object, SourceLocation> GetValidSourceLocationMapping()
224 Dictionary<object, SourceLocation> validSrcLocMapping = new Dictionary<object, SourceLocation>();
225 foreach (KeyValuePair<object, SourceLocation> entry in deserializedObjectToSourceLocationMapping)
227 if (IsValidRange(entry.Value.StartLine, entry.Value.StartColumn, entry.Value.EndLine, entry.Value.EndColumn))
229 object sourceLocationObject = entry.Key;
230 object modelItemObject;
231 if (this.SourceLocationObjectToModelItemObjectMapping == null)
233 modelItemObject = sourceLocationObject;
237 this.SourceLocationObjectToModelItemObjectMapping.TryGetValue(sourceLocationObject, out modelItemObject);
240 if (modelItemObject != null)
242 validSrcLocMapping.Add(modelItemObject, entry.Value);
246 return validSrcLocMapping;
250 private static bool IsValidRange(int startLine, int startColumn, int endLine, int endColumn)
253 (startLine > 0) && (startColumn > 0) && (endLine > 0) && (endColumn > 0) &&
254 ((startLine < endLine) || (startLine == endLine && startColumn < endColumn));