Fix XMM scanning on Mac x86.
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Debugger / DebuggerService.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Debug
6 {
7     using System;
8     using System.Activities;
9     using System.Activities.Debugger;
10     using System.Activities.Presentation;
11     using System.Activities.Presentation.Hosting;
12     using System.Activities.Presentation.Model;
13     using System.Activities.Presentation.Services;
14     using System.Activities.Presentation.Validation;
15     using System.Activities.Presentation.View;
16     using System.Activities.Presentation.Xaml;
17     using System.Activities.XamlIntegration;
18     using System.Collections.Generic;
19     using System.Linq;
20     using System.Runtime;
21     using System.Windows.Documents;
22     using System.Windows.Threading;
23
24     [Fx.Tag.XamlVisible(false)]
25     public class DebuggerService : IDesignerDebugView
26     {
27         EditingContext context;
28         ModelItem selectedModelItem;
29         SourceLocation currentLocation;
30         SourceLocation currentContext;
31         ModelItem currentModelItem;
32         ModelItem currentModelItemContext;
33         WorkflowViewService viewService;
34         ModelSearchServiceImpl modelSearchService;
35         ModelTreeManager modelTreeManager;
36         const string unresolvedPrefix = "unresolved:";
37
38         AttachedProperty<bool> isBreakpointEnabledProperty;
39         AttachedProperty<bool> isBreakpointBoundedProperty;
40         AttachedProperty<bool> isBreakpointConditionalProperty;
41         AttachedProperty<bool> isCurrentLocationProperty;
42         AttachedProperty<bool> isCurrentContextProperty;
43
44         Dictionary<object, SourceLocation> instanceToSourceLocationMapping;
45         Dictionary<ModelItem, SourceLocation> modelItemToSourceLocation;
46         Dictionary<SourceLocation, ModelItem> sourceLocationToModelItem;
47
48         Dictionary<ModelItem, BreakpointTypes> breakpoints;              // The map contains breakpoint that has its ModelItem on the modelTree.
49         Dictionary<ModelItem, BreakpointTypes> transientBreakpoints;     // The map contains breakpoint that has its ModelItem not on the modelTree.
50         Dictionary<SourceLocation, BreakpointTypes> unmappedBreakpoints; // The map contains breakpoint that has no ModelItem
51
52         // This is used to generate unique source line no when the view element does not have a source location
53         int lastSourceLineNo = 1;
54
55         bool isReadOnly = false;
56         bool isDebugging = false;
57         private string fileName;
58         private bool requiresUpdateSourceLocation;
59
60         // Storing background BringToViewCurrentLocation operation.
61         DispatcherOperation bringToViewCurrentLocationOperation = null;
62
63         public DebuggerService(EditingContext context)
64         {
65             if (context == null)
66             {
67                 throw FxTrace.Exception.ArgumentNull("context");
68             }
69
70             this.context = context;
71             this.modelItemToSourceLocation = new Dictionary<ModelItem, SourceLocation>();
72             this.sourceLocationToModelItem = new Dictionary<SourceLocation, ModelItem>();
73             this.breakpoints = new Dictionary<ModelItem, BreakpointTypes>();
74             // Breakpoints transiently removed from the document (copy/paste/undo/redo).
75             this.transientBreakpoints = new Dictionary<ModelItem, BreakpointTypes>();
76             this.unmappedBreakpoints = new Dictionary<SourceLocation, BreakpointTypes>(4);
77             this.instanceToSourceLocationMapping = new Dictionary<object, SourceLocation>();
78
79             this.context.Items.Subscribe<Selection>(new SubscribeContextCallback<Selection>(this.SelectionChanged));
80             this.context.Services.Subscribe<ViewService>(new SubscribeServiceCallback<ViewService>(this.OnViewServiceAvailable));
81             this.context.Services.Subscribe<ModelSearchService>(new SubscribeServiceCallback<ModelSearchService>(this.OnModelSearchServiceAvailable));
82             this.context.Services.Subscribe<AttachedPropertiesService>(new SubscribeServiceCallback<AttachedPropertiesService>(this.OnAttachedPropertiesServiceAvailable));
83             this.context.Services.Subscribe<ModelTreeManager>(new SubscribeServiceCallback<ModelTreeManager>(this.OnModelTreeManagerServiceAvailable));
84
85             this.requiresUpdateSourceLocation = true;
86         }
87
88         // IDesignerDebugView
89
90         // Get the currently selected location from the designer
91         // generally this is the location of the object currently selected by the user
92         public SourceLocation SelectedLocation
93         {
94             get
95             {
96                 return (this.selectedModelItem != null && AllowBreakpointAttribute.IsBreakpointAllowed(this.selectedModelItem.ItemType)) ?
97                     this.GetSourceLocationFromModelItem(this.selectedModelItem) : null;
98             }
99         }
100
101         // Set current location of execution.
102         // The location to shown the "yellow" arrow.
103         public SourceLocation CurrentLocation
104         {
105             get
106             {
107                 return this.currentLocation;
108             }
109
110             set
111             {
112                 this.currentLocation = value;
113                 ModelItem previousModelItem = this.currentModelItem;
114                 UpdateCurrentModelItem();
115                 if (this.currentLocation != null && this.currentModelItem == null)
116                 {   // This is a rare case but it happens when the designer is not all done with bringing up the view but
117                     // Debugger already set this location.
118                     PostBringToViewCurrentLocation(previousModelItem);
119                 }
120                 else
121                 {
122                     BringToViewCurrentLocation(previousModelItem);
123                 }
124             }
125         }
126
127         public void EnsureVisible(SourceLocation sourceLocation)
128         {
129             SourceLocation exactLocation = GetExactLocation(sourceLocation);
130             ModelItem mi = this.GetModelItemFromSourceLocation(exactLocation, /* forceCreate */ true);
131             if (mi != null)
132             {
133                 BringToView(mi);
134             }
135         }
136
137         void BringToViewCurrentLocation(ModelItem previousModelItem)
138         {
139             SetPropertyValue(previousModelItem, isCurrentLocationProperty, this.currentModelItem);
140             if (this.currentModelItem != this.currentModelItemContext)
141             {
142                 BringToView(this.currentModelItem);
143             }
144         }
145
146         // Post new BringToViewCurrentLocation operation
147         void PostBringToViewCurrentLocation(ModelItem previousModelItem)
148         {
149             // Abort pending operation.
150             if (this.bringToViewCurrentLocationOperation != null)
151             {
152                 this.bringToViewCurrentLocationOperation.Abort();
153                 this.bringToViewCurrentLocationOperation = null;
154             }
155
156             // Post a new background operation.
157             this.bringToViewCurrentLocationOperation = Dispatcher.CurrentDispatcher.BeginInvoke(
158                 DispatcherPriority.Background,
159                 (DispatcherOperationCallback)delegate(object arg)
160                 {
161                     this.UpdateCurrentModelItem();
162                     this.BringToViewCurrentLocation(previousModelItem);
163                     this.bringToViewCurrentLocationOperation = null;
164                     return null;
165                 },
166                 null);
167         }
168
169
170         // Set current context (stack frame scope).
171         // The highlighted scope of execution.
172         public SourceLocation CurrentContext
173         {
174             get
175             {
176                 return this.currentContext;
177             }
178             set
179             {
180                 this.currentContext = value;
181                 ModelItem previousModelItem = this.currentModelItemContext;
182                 UpdateCurrentModelItemContext();
183                 SetPropertyValue(previousModelItem, this.isCurrentContextProperty, this.currentModelItemContext);
184                 BringToView(this.currentModelItemContext);
185             }
186         }
187
188         // Set to true while debugging
189         public bool IsDebugging
190         {
191             get
192             {
193                 return this.isDebugging;
194             }
195
196             set
197             {
198                 ReadOnlyState readOnlyState = this.context.Items.GetValue<ReadOnlyState>();
199                 if (readOnlyState != null)
200                 {
201                     // start debugging
202                     if (value && !this.isDebugging)
203                     {
204                         this.isDebugging = true;
205                         // backup the read-only state
206                         this.isReadOnly = readOnlyState.IsReadOnly;
207                         readOnlyState.IsReadOnly = true;
208                     }
209                     // finish debugging
210                     else if (!value && this.isDebugging)
211                     {
212                         this.isDebugging = false;
213                         // restore to previous state before debugging
214                         readOnlyState.IsReadOnly = this.isReadOnly;
215                     }
216                     this.context.Items.SetValue(new ReadOnlyState() { IsReadOnly = readOnlyState.IsReadOnly });
217                 }
218             }
219         }
220
221         public bool HideSourceFileName
222         {
223             get;
224             set;
225         }
226
227         void UpdateCurrentModelItem()
228         {
229             this.currentModelItem = this.GetModelItemFromSourceLocation(this.currentLocation, /* forceCreate */ true);
230         }
231
232         void UpdateCurrentModelItemContext()
233         {
234             this.currentModelItemContext = this.GetModelItemFromSourceLocation(this.currentContext, /* forceCreate */ true);
235         }
236
237         void BringToView(ModelItem modelItem)
238         {
239             if (modelItem != null)
240             {
241                 modelItem.Focus();
242             }
243         }
244
245         void OnAttachedPropertiesServiceAvailable(AttachedPropertiesService attachedPropertiesService)
246         {
247             this.isBreakpointEnabledProperty = new AttachedProperty<bool>()
248                 {
249                     Getter = (modelItem) => IsBreakpointOfType(modelItem, BreakpointTypes.Enabled),
250                     Name = "IsBreakpointEnabled",
251                     OwnerType = typeof(object)
252                 };
253
254             this.isBreakpointBoundedProperty = new AttachedProperty<bool>()
255                 {
256                     Getter = (modelItem) => IsBreakpointOfType(modelItem, BreakpointTypes.Bounded),
257                     Name = "IsBreakpointBounded",
258                     OwnerType = typeof(object)
259                 };
260
261             this.isBreakpointConditionalProperty = new AttachedProperty<bool>()
262                 {
263                     Getter = (modelItem) => IsBreakpointOfType(modelItem, BreakpointTypes.Conditional),
264                     Name = "IsBreakpointConditional",
265                     OwnerType = typeof(object)
266                 };
267
268             this.isCurrentLocationProperty = new AttachedProperty<bool>()
269                 {
270                     Getter = (modelItem) => IsCurrentLocation(modelItem),
271                     Name = "IsCurrentLocation",
272                     OwnerType = typeof(object)
273                 };
274
275             this.isCurrentContextProperty = new AttachedProperty<bool>()
276                 {
277                     Getter = (modelItem) => IsCurrentContext(modelItem),
278                     Name = "IsCurrentContext",
279                     OwnerType = typeof(object)
280                 };
281
282             attachedPropertiesService.AddProperty(isBreakpointEnabledProperty);
283             attachedPropertiesService.AddProperty(isBreakpointBoundedProperty);
284             attachedPropertiesService.AddProperty(isBreakpointConditionalProperty);
285             attachedPropertiesService.AddProperty(isCurrentLocationProperty);
286             attachedPropertiesService.AddProperty(isCurrentContextProperty);
287         }
288
289         void OnModelTreeManagerServiceAvailable(ModelTreeManager modelTreeManager)
290         {
291             this.modelTreeManager = modelTreeManager;
292             this.modelTreeManager.EditingScopeCompleted += OnEditingScopeCompleted;
293         }
294
295         private void OnEditingScopeCompleted(object sender, EditingScopeEventArgs e)
296         {
297             Fx.Assert(e.EditingScope != null, "e.EditingScope should not be null.");
298             foreach (ModelItem removedModelItem in e.EditingScope.ItemsRemoved)
299             {
300                 DeleteModelItem(removedModelItem);
301             }
302         }
303
304         private void DeleteModelItem(ModelItem modelItem)
305         {
306             if (modelItem != null)
307             {
308                 BreakpointTypes breakpointType;
309                 if (this.breakpoints.TryGetValue(modelItem, out breakpointType))
310                 {
311                     this.transientBreakpoints[modelItem] = breakpointType;   // cache it in case it's added later (move case).
312                     SetBreakpointType(modelItem, BreakpointTypes.None); // clear breakpoint
313                 }
314
315                 DeleteFromMapping(modelItem.GetCurrentValue());
316             }
317         }
318
319         // Delete a single object from the mappings.
320         // We only delete unresolved object, i.e. object that never been 
321         // saved.  We leave the resolved object untouch (otherwise undoing
322         // the removed object will make it as "unresolved" the next time around).
323         void DeleteFromMapping(object unresolvedObject)
324         {
325             SourceLocation sourceLocation;
326             if (this.instanceToSourceLocationMapping.TryGetValue(unresolvedObject, out sourceLocation))
327             {
328                 if (IsUnresolved(sourceLocation))
329                 {
330                     this.instanceToSourceLocationMapping.Remove(unresolvedObject);
331                     ModelItem modelItem;
332                     if (this.sourceLocationToModelItem.TryGetValue(sourceLocation, out modelItem))
333                     {
334                         this.sourceLocationToModelItem.Remove(sourceLocation);
335                         this.modelItemToSourceLocation.Remove(modelItem);
336                     }
337                 }
338             }
339         }
340
341         bool IsCurrentLocation(ModelItem modelItem)
342         {
343             UpdateCurrentModelItem();
344             return this.currentModelItem == modelItem;
345         }
346
347         bool IsCurrentContext(ModelItem modelItem)
348         {
349             UpdateCurrentModelItemContext();
350             return this.currentModelItemContext == modelItem;
351         }
352
353         void SelectionChanged(Selection selection)
354         {
355             this.selectedModelItem = selection.PrimarySelection;
356         }
357
358         // Check if unmapped breakpoint exists for the given sourceLocation,
359         // if so, mapped it to the given model item & remove it from unmapped breakpoints.
360         void TryActivateUnmappedBreakpoint(SourceLocation sourceLocation, ModelItem modelItem)
361         {
362             BreakpointTypes breakpointType;
363             if (this.unmappedBreakpoints.TryGetValue(sourceLocation, out breakpointType))
364             {
365                 this.SetBreakpointType(modelItem, breakpointType);
366                 this.unmappedBreakpoints.Remove(sourceLocation);
367             }
368         }
369
370         void TryActivateAllUnmappedBreakpoints()
371         {
372             if (this.unmappedBreakpoints.Count > 0)
373             {
374                 List<SourceLocation> unmappedLocations = new List<SourceLocation>();
375                 unmappedLocations.AddRange(this.unmappedBreakpoints.Keys);
376                 foreach (SourceLocation unmappedLocation in unmappedLocations)
377                 {
378                     ModelItem modelItem = this.GetModelItemFromSourceLocation(unmappedLocation);
379                     if (modelItem != null)
380                     {
381                         TryActivateUnmappedBreakpoint(unmappedLocation, modelItem);
382                     }
383                 }
384             }
385         }
386
387         bool IsBreakpointOfType(ModelItem modelItem, BreakpointTypes breakpointType)
388         {
389             bool result = false;
390             BreakpointTypes actualBreakpointType;
391             TryActivateAllUnmappedBreakpoints();
392             if (this.breakpoints.TryGetValue(modelItem, out actualBreakpointType))
393             {
394                 result = (actualBreakpointType & breakpointType) > 0;
395             }
396             return result;
397         }
398
399         void SetBreakpointType(ModelItem modelItem, BreakpointTypes newBreakpointType)
400         {
401             BreakpointTypes oldBreakpointType = BreakpointTypes.None;
402             if (this.breakpoints.TryGetValue(modelItem, out oldBreakpointType))
403             {
404                 Fx.Assert(oldBreakpointType != BreakpointTypes.None, "Should not store BreakpointType.None");
405                 if (newBreakpointType == BreakpointTypes.None)
406                 {
407                     this.breakpoints.Remove(modelItem);
408                 }
409                 else
410                 {
411                     this.breakpoints[modelItem] = newBreakpointType;
412                 }
413             }
414             else if (newBreakpointType != BreakpointTypes.None)
415             {
416                 this.breakpoints.Add(modelItem, newBreakpointType);
417             }
418
419             // Now notifying corresponding properties.
420             if ((oldBreakpointType & BreakpointTypes.Bounded) !=
421                 (newBreakpointType & BreakpointTypes.Bounded))
422             {
423                 this.isBreakpointBoundedProperty.NotifyPropertyChanged(modelItem);
424             }
425
426             if ((oldBreakpointType & BreakpointTypes.Enabled) !=
427                 (newBreakpointType & BreakpointTypes.Enabled))
428             {
429                 this.isBreakpointEnabledProperty.NotifyPropertyChanged(modelItem);
430             }
431
432             if ((oldBreakpointType & BreakpointTypes.Conditional) !=
433                 (newBreakpointType & BreakpointTypes.Conditional))
434             {
435                 this.isBreakpointConditionalProperty.NotifyPropertyChanged(modelItem);
436             }
437         }
438
439         // Return exact source location given approximate location.
440         public SourceLocation GetExactLocation(SourceLocation approximateLocation)
441         {
442             this.EnsureSourceLocationUpdated();
443
444             if (approximateLocation == null)
445             {
446                 throw FxTrace.Exception.ArgumentNull("approximateLocation");
447             }
448
449             SourceLocation exactLocation = null;
450
451             foreach (SourceLocation sourceLocation in this.instanceToSourceLocationMapping.Values)
452             {
453                 if (sourceLocation.StartLine == approximateLocation.StartLine)
454                 {
455                     exactLocation = sourceLocation;
456                     break;
457                 }
458             }
459
460             if (exactLocation == null)
461             {
462                 exactLocation = FindClosestSourceLocation(approximateLocation, this.instanceToSourceLocationMapping.Values);
463             }
464
465             return exactLocation;
466         }
467
468         // This method tries to find the inner most outer source location from a list
469         // The outer source locations of a source location is ones that contains it
470         // The inner most outer source location is the one nested most deeply, right outside of the source location being contained.
471         private static SourceLocation FindInnerMostContainer(SourceLocation approximateLocation, IEnumerable<SourceLocation> availableSourceLocations)
472         {
473             Fx.Assert(approximateLocation != null && availableSourceLocations != null, "Argument should not be null");
474
475             SourceLocation innerMostOuterSourceLocation = null;
476
477             foreach (SourceLocation sourceLocation in availableSourceLocations)
478             {
479                 if (sourceLocation.Contains(approximateLocation))
480                 {
481                     if (innerMostOuterSourceLocation == null)
482                     {
483                         innerMostOuterSourceLocation = sourceLocation;
484                     }
485                     else
486                     {
487                         if (innerMostOuterSourceLocation.Contains(sourceLocation))
488                         {
489                             innerMostOuterSourceLocation = sourceLocation;
490                         }
491                     }
492                 }
493             }
494
495             return innerMostOuterSourceLocation;
496         }
497
498         internal static SourceLocation FindClosestSourceLocation(SourceLocation approximateLocation, IEnumerable<SourceLocation> availableSourceLocations)
499         {
500             Fx.Assert(approximateLocation != null && availableSourceLocations != null, "Argument should not be null");
501
502             SourceLocation exactLocation = null;
503             SourceLocation innerMostOuterSourceLocation =
504                 FindInnerMostContainer(approximateLocation, availableSourceLocations);
505
506             if (innerMostOuterSourceLocation != null)
507             {
508                 exactLocation = innerMostOuterSourceLocation;
509             }
510             else
511             {
512                 // Find the next line of the approximateLocation.
513                 int minimumDistance = int.MaxValue;
514                 foreach (SourceLocation sourceLocation in availableSourceLocations)
515                 {
516                     int lineDistance = sourceLocation.StartLine - approximateLocation.StartLine;
517                     if ((lineDistance > 0) &&
518                         ((lineDistance < minimumDistance) ||
519                          ((lineDistance == minimumDistance) && (sourceLocation.StartColumn < exactLocation.StartColumn))))  // if same distance, then compare the start column
520                     {
521                         exactLocation = sourceLocation;
522                         minimumDistance = lineDistance;
523                     }
524                 }
525             }
526
527             return exactLocation;
528         }
529
530         // Called after a Save by AddIn to update breakpoints with new locations 
531         public IDictionary<SourceLocation, BreakpointTypes> GetBreakpointLocations()
532         {
533             IDictionary<SourceLocation, BreakpointTypes> breakpointLocations = new Dictionary<SourceLocation, BreakpointTypes>();
534
535             // Collect source locations of model items with breakpoints
536             if (this.breakpoints.Count > 0 || this.unmappedBreakpoints.Count > 0)
537             {
538                 foreach (KeyValuePair<ModelItem, BreakpointTypes> entry in this.breakpoints)
539                 {
540                     SourceLocation breakpointLocation = this.GetSourceLocationFromModelItem(entry.Key);
541                     // BreakpointLocation can be null, if the model item is deleted but without notification
542                     // through OnModelChanged.  This happens when the breakpoint is located inside child
543                     // of a deleted object.
544                     if (breakpointLocation != null)
545                     {
546                         breakpointLocations.Add(breakpointLocation, entry.Value);
547                     }
548                 }
549                 foreach (KeyValuePair<SourceLocation, BreakpointTypes> entry in this.unmappedBreakpoints)
550                 {
551                     breakpointLocations.Add(entry.Key, entry.Value);
552                 }
553             }
554             return breakpointLocations;
555         }
556
557         // Inserting a new breakpoint of a given type.
558         public void InsertBreakpoint(SourceLocation sourceLocation, BreakpointTypes breakpointType)
559         {
560             this.UpdateBreakpoint(sourceLocation, breakpointType);
561         }
562
563         // Update the appearance of a given breakpoint to show the given type.
564         public void UpdateBreakpoint(SourceLocation sourceLocation, BreakpointTypes newBreakpointType)
565         {
566             ModelItem modelItem = this.GetModelItemFromSourceLocation(sourceLocation);
567             if (modelItem != null)
568             {
569                 SetBreakpointType(modelItem, newBreakpointType);
570             }
571             else
572             {
573                 BreakpointTypes oldBreakpointType;
574                 if (this.unmappedBreakpoints.TryGetValue(sourceLocation, out oldBreakpointType))
575                 {
576                     if (newBreakpointType == BreakpointTypes.None)
577                     {
578                         this.unmappedBreakpoints.Remove(sourceLocation);
579                     }
580                     else
581                     {
582                         this.unmappedBreakpoints[sourceLocation] = newBreakpointType;
583                     }
584                 }
585                 else if (newBreakpointType != BreakpointTypes.None)
586                 {
587                     this.unmappedBreakpoints.Add(sourceLocation, newBreakpointType);
588                 }
589             }
590         }
591
592         // Delete a breakpoint.
593         public void DeleteBreakpoint(SourceLocation sourceLocation)
594         {
595             UpdateBreakpoint(sourceLocation, BreakpointTypes.None);
596         }
597
598         // Reset breakpoints: delete and prepare for breakpoint refresh.
599         public void ResetBreakpoints()
600         {
601             ModelItem[] oldModelItems = new ModelItem[this.breakpoints.Keys.Count];
602             this.breakpoints.Keys.CopyTo(oldModelItems, 0);
603             this.breakpoints.Clear();
604             this.unmappedBreakpoints.Clear();
605
606             // Now notifying update to corresponding properties.
607             foreach (ModelItem modelItem in oldModelItems)
608             {
609                 this.isBreakpointBoundedProperty.NotifyPropertyChanged(modelItem);
610                 this.isBreakpointEnabledProperty.NotifyPropertyChanged(modelItem);
611                 this.isBreakpointConditionalProperty.NotifyPropertyChanged(modelItem);
612             }
613         }
614
615         public void UpdateSourceLocations(Dictionary<object, SourceLocation> newSourceLocationMapping)
616         {
617             if (newSourceLocationMapping == null)
618             {
619                 throw FxTrace.Exception.ArgumentNull("newSourceLocationMapping");
620             }
621
622             // Update unmappedBreakpoints before refreshing the instanceToSourceLocationMapping.
623             if (this.unmappedBreakpoints.Count > 0)
624             {
625                 Dictionary<SourceLocation, BreakpointTypes> newUnmappedBreakpoints = new Dictionary<SourceLocation, BreakpointTypes>(this.unmappedBreakpoints.Count);
626                 foreach (KeyValuePair<object, SourceLocation> kvpEntry in this.instanceToSourceLocationMapping)
627                 {
628                     if (this.unmappedBreakpoints.ContainsKey(kvpEntry.Value))
629                     {
630                         if (newSourceLocationMapping.ContainsKey(kvpEntry.Key))
631                         {
632                             newUnmappedBreakpoints.Add(newSourceLocationMapping[kvpEntry.Key], this.unmappedBreakpoints[kvpEntry.Value]);
633                         }
634                     }
635                 }
636                 this.unmappedBreakpoints = newUnmappedBreakpoints;
637             }
638
639             // It is possible that after InvalidateSourceLocationMapping, before UpdateSourceLocations, we introduced new unresolvedEntries. 
640             // These entries should not be dropped, or we will not be able to add breakpoint before UpdateSourceLocation.
641             List<KeyValuePair<object, SourceLocation>> unresolvedEntries = this.instanceToSourceLocationMapping.Where(entry => IsUnresolved(entry.Value)).ToList();
642
643             this.instanceToSourceLocationMapping = newSourceLocationMapping;
644             this.sourceLocationToModelItem.Clear();
645             this.modelItemToSourceLocation.Clear();
646             this.transientBreakpoints.Clear();
647
648             if (this.modelTreeManager != null)
649             {
650                 foreach (KeyValuePair<object, SourceLocation> kvp in newSourceLocationMapping)
651                 {
652                     ModelItem modelItem = this.modelTreeManager.GetModelItem(kvp.Key);
653                     if (modelItem != null)
654                     {
655                         SourceLocation sourceLocation = kvp.Value;
656                         this.modelItemToSourceLocation.Add(modelItem, sourceLocation);
657                         this.sourceLocationToModelItem.Add(sourceLocation, modelItem);
658                     }
659                 }
660
661                 foreach (KeyValuePair<object, SourceLocation> unresolvedEntry in unresolvedEntries)
662                 {
663                     object unresolvedObject = unresolvedEntry.Key;
664                     SourceLocation sourceLocation = unresolvedEntry.Value;
665                     if (!this.instanceToSourceLocationMapping.ContainsKey(unresolvedObject))
666                     {
667                         this.instanceToSourceLocationMapping.Add(unresolvedObject, sourceLocation);
668                         ModelItem modelItem = this.modelTreeManager.GetModelItem(unresolvedObject);
669                         if (modelItem != null)
670                         {
671                             this.modelItemToSourceLocation.Add(modelItem, sourceLocation);
672                             this.sourceLocationToModelItem.Add(sourceLocation, modelItem);
673                         }
674                     }
675                 }
676             }
677
678             TryActivateAllUnmappedBreakpoints();
679         }
680
681         // Called by View Service when a new view element is created 
682         private void ViewCreated(object sender, ViewCreatedEventArgs e)
683         {
684             if (e.View != null)
685             {
686                 ModelItem modelItem = e.View.ModelItem;
687                 object addedObject = modelItem.GetCurrentValue();
688
689                 // Create a mapping between SourceLocation and this View Element
690                 SourceLocation sourceLocation = this.GetSourceLocationFromModelItemInstance(addedObject);
691                 if (sourceLocation == null)
692                 {
693                     // The current view element has not been saved yet to the Xaml file
694                     sourceLocation = GenerateUnresolvedLocation();
695                     this.instanceToSourceLocationMapping.Add(addedObject, sourceLocation);
696                 }
697
698                 this.modelItemToSourceLocation[modelItem] = sourceLocation;
699                 this.sourceLocationToModelItem[sourceLocation] = modelItem;
700
701                 BreakpointTypes breakpointType;
702                 // check if it's in the transient breakpoint list.
703                 if (this.transientBreakpoints.TryGetValue(modelItem, out breakpointType))
704                 {
705                     this.transientBreakpoints.Remove(modelItem);
706                     SetBreakpointType(modelItem, breakpointType);
707                 }
708                 else
709                 {
710                     TryActivateUnmappedBreakpoint(sourceLocation, modelItem);
711                 }
712             }
713         }
714
715         private SourceLocation GenerateUnresolvedLocation()
716         {
717             return new SourceLocation(unresolvedPrefix + this.context.Items.GetValue<WorkflowFileItem>().LoadedFile, this.lastSourceLineNo++);
718         }
719
720         private static bool IsUnresolved(SourceLocation sourceLocation)
721         {
722             return !string.IsNullOrEmpty(sourceLocation.FileName) && sourceLocation.FileName.StartsWith(unresolvedPrefix, StringComparison.OrdinalIgnoreCase);
723         }
724
725         // This method is called during Load/Save - the resolved mapping should be invalidated.
726         internal void InvalidateSourceLocationMapping(string fileName)
727         {
728             this.fileName = fileName;
729             this.requiresUpdateSourceLocation = true;
730
731             // Remove, from the SourceLocationMappings, the entries with resolved SourceLocation - they are no longer valid and should be refreshed.
732             List<KeyValuePair<ModelItem, SourceLocation>> resolvedEntries = this.modelItemToSourceLocation.Where(entry => !IsUnresolved(entry.Value)).ToList();
733             foreach (KeyValuePair<ModelItem, SourceLocation> resolvedEntry in resolvedEntries)
734             {
735                 this.modelItemToSourceLocation.Remove(resolvedEntry.Key);
736                 this.sourceLocationToModelItem.Remove(resolvedEntry.Value);
737                 this.instanceToSourceLocationMapping.Remove(resolvedEntry.Key.GetCurrentValue());
738             }
739
740             // All breakpoint should simply stay - unmappedBreakpoint will get updated to newSourceLocation when we have the newSourceLocation.
741         }
742
743         private void EnsureSourceLocationUpdated()
744         {
745             Fx.Assert(this.modelSearchService != null, "ModelSearchService should be available and is ensured in WorkflowDesigner constructor");
746             if (this.requiresUpdateSourceLocation)
747             {
748                 Dictionary<object, SourceLocation> updatedSourceLocations = new Dictionary<object, SourceLocation>();
749                 foreach (ModelItem key in this.modelSearchService.GetObjectsWithSourceLocation())
750                 {
751                     // disallow expressions
752                     if (AllowBreakpointAttribute.IsBreakpointAllowed(key.ItemType) && !typeof(IValueSerializableExpression).IsAssignableFrom(key.ItemType))
753                     {
754                         SourceLocation sourceLocationWithoutFileName = this.modelSearchService.FindSourceLocation(key);
755
756                         // Appending the fileName
757                         SourceLocation sourceLocationWithFileName = new SourceLocation(this.fileName,
758                             sourceLocationWithoutFileName.StartLine,
759                             sourceLocationWithoutFileName.StartColumn,
760                             sourceLocationWithoutFileName.EndLine,
761                             sourceLocationWithoutFileName.EndColumn);
762                         updatedSourceLocations.Add(key.GetCurrentValue(), sourceLocationWithFileName);
763                     }
764                 }
765
766                 this.UpdateSourceLocations(updatedSourceLocations);
767                 this.requiresUpdateSourceLocation = false;
768             }
769         }
770
771         private void OnModelSearchServiceAvailable(ModelSearchService modelSearchService)
772         {
773             this.modelSearchService = (ModelSearchServiceImpl)modelSearchService;
774         }
775
776         private void OnViewServiceAvailable(ViewService viewService)
777         {
778             this.viewService = (WorkflowViewService)viewService;
779             this.viewService.ViewCreated += this.ViewCreated;
780         }
781
782         private SourceLocation GetSourceLocationFromModelItemInstance(object instance)
783         {
784             SourceLocation sourceLocation;
785
786             // instanceToSourceLocationMapping contains source locations for all instances 
787             // immediately after a Load or save.  For instances that have been just dropped into
788             // the Designer from the Toolbox, we want to return null from here and treat them
789             // as "Unresolved" in the caller.
790             if (this.instanceToSourceLocationMapping.TryGetValue(instance, out sourceLocation))
791             {
792                 return sourceLocation;
793             }
794             else
795             {
796                 return null;
797             }
798         }
799
800         private void SetPropertyValue(ModelItem oldModelItem, AttachedProperty property, ModelItem newModelItem)
801         {
802             // update the previous ModelItem (what was current before)
803             if (oldModelItem != null)
804             {
805                 property.NotifyPropertyChanged(oldModelItem);
806             }
807
808             // update the current Modelitem
809             if (newModelItem != null)
810             {
811                 property.NotifyPropertyChanged(newModelItem);
812             }
813         }
814
815         private SourceLocation GetSourceLocationFromModelItem(ModelItem modelItem)
816         {
817             this.EnsureSourceLocationUpdated();
818             SourceLocation sourceLocation = null;
819             if (modelItem != null)
820             {
821                 this.modelItemToSourceLocation.TryGetValue(modelItem, out sourceLocation);
822             }
823             return sourceLocation;
824         }
825
826         private ModelItem GetModelItemFromSourceLocation(SourceLocation sourceLocation)
827         {
828             return GetModelItemFromSourceLocation(sourceLocation, /* forceCreate = */ false);
829         }
830
831         private ModelItem GetModelItemFromSourceLocation(SourceLocation sourceLocation, bool forceCreate)
832         {
833             ModelItem modelItem = null;
834             if (sourceLocation != null)
835             {
836                 if (!this.sourceLocationToModelItem.TryGetValue(sourceLocation, out modelItem))
837                 {
838                     if (forceCreate)
839                     {
840                         object foundElement = null;
841                         foreach (KeyValuePair<object, SourceLocation> kvp in this.instanceToSourceLocationMapping)
842                         {
843                             if (kvp.Value.Equals(sourceLocation))
844                             {
845                                 foundElement = kvp.Key;
846                                 break;
847                             }
848                         }
849
850                         if (foundElement != null)
851                         {
852                             modelItem = Validation.ValidationService.FindModelItem(this.modelTreeManager, foundElement);
853                             
854                             if (modelItem != null)
855                             {
856                                 this.modelItemToSourceLocation.Add(modelItem, sourceLocation);
857                                 this.sourceLocationToModelItem.Add(sourceLocation, modelItem);
858                             }
859                         }
860                     }
861                 }
862             }
863
864             return modelItem;
865         }
866     }
867 }