File: cdf\src\NetFx40\Tools\System.Activities.Presentation\System\Activities\Presentation\Xaml\ObjectToSourceLocationMapping.cs
Project: ndp\System.Data.csproj (System.Data)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.Activities.Presentation.Xaml
{
    using System.Activities.Debugger;
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.ViewState;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Collections;
 
    // This class is to create and hold the mapping of Activity to SourceLocation and SourceLocation to ModelItem.
    // First, XamlReader write in activity object and its SourceLocation. Then model search service can pass in the
    // ModelItem list to create a source location to model item mapping.
    internal class ObjectToSourceLocationMapping
    {
        private IDictionary<object, SourceLocation> deserializedObjectToSourceLocationMapping;
        private IDictionary<SourceLocation, ModelItem> sourceLocationToModelItemMapping;
        private IDictionary<SourceLocation, ModelItem> viewStateSourceLocationToModelItemMapping;
        private ModelSearchServiceImpl modelSearchService;
 
        // Is the Object to SourceLocation mapping updated? generally means the file was reloaded.
        private bool updateRequired;
 
        internal ObjectToSourceLocationMapping(ModelSearchServiceImpl modelSearchService)
        {
            this.modelSearchService = modelSearchService;
            this.deserializedObjectToSourceLocationMapping = new OrderedDictionary<object, SourceLocation>();
            this.sourceLocationToModelItemMapping = new Dictionary<SourceLocation, ModelItem>();
            this.viewStateSourceLocationToModelItemMapping = new Dictionary<SourceLocation, ModelItem>();
        }
 
        internal void Clear()
        {
            this.deserializedObjectToSourceLocationMapping.Clear();
            this.updateRequired = true;
        }
 
        internal Dictionary<object, object> SourceLocationObjectToModelItemObjectMapping
        {
            get;
            set;
        }
 
        internal Dictionary<string, SourceLocation> ViewStateDataSourceLocationMapping
        {
            get;
            set;
        }
 
        internal void UpdateMap(object key, SourceLocation sourceLocation)
        {
            if (this.deserializedObjectToSourceLocationMapping.ContainsKey(key))
            {
                this.deserializedObjectToSourceLocationMapping.Remove(key);
            }
 
            this.deserializedObjectToSourceLocationMapping.Add(key, new SourceLocation(/* fileName = */ null,
                sourceLocation.StartLine, sourceLocation.StartColumn,
                sourceLocation.EndLine, sourceLocation.EndColumn));
        }
 
        // create a SourceLocation to ModelItem mapping based on the current activity to SourceLocation mapping.
        private void UpdateSourceLocationToModelItemMapping(IEnumerable<ModelItem> modelItemsOnDesigner)
        {
            Dictionary<object, SourceLocation> validMapping = GetValidSourceLocationMapping();
            this.sourceLocationToModelItemMapping.Clear();
            this.viewStateSourceLocationToModelItemMapping.Clear();
            foreach (ModelItem modelItem in modelItemsOnDesigner)
            {
                SourceLocation srcLocation = FindMatchSrcLocation(modelItem, validMapping);
                if (srcLocation != null)
                {
                    sourceLocationToModelItemMapping.Add(srcLocation, modelItem);
                }
                string workflowViewStateIdRef = WorkflowViewState.GetIdRef(modelItem.GetCurrentValue());
                if (!String.IsNullOrEmpty(workflowViewStateIdRef))
                {
                    SourceLocation viewStateSrcLocation = this.FindViewStateDataSrcLocationByViewStateIdRef(workflowViewStateIdRef);
                    if (viewStateSrcLocation != null)
                    {
                        // In some cases duplicated key is possible, use indexer instead of Add() to avoid throw. 
                        // See TFS bug 523908 for detailed information
                        viewStateSourceLocationToModelItemMapping[viewStateSrcLocation] = modelItem;
                    }
                }
            }
 
            this.updateRequired = false;
        }
 
        // find a modelitem whose SourceLocation contains the srcLocation passed in.
        internal ModelItem FindModelItem(SourceLocation srcLocation)
        {
            this.EnsureUpdated();
            return FindModelItemInMap(srcLocation, this.sourceLocationToModelItemMapping);
        }
 
        internal ModelItem FindModelItemOfViewState(SourceLocation srcLocation)
        {
            this.EnsureUpdated();
            return FindModelItemInMap(srcLocation, this.viewStateSourceLocationToModelItemMapping);
        }
 
        internal SourceLocation FindSourceLocation(ModelItem modelItem)
        {
            this.EnsureUpdated();
            KeyValuePair<SourceLocation, ModelItem>? matchingMappingRecord = sourceLocationToModelItemMapping.SingleOrDefault(kvp => object.ReferenceEquals(kvp.Value, modelItem));
            if (matchingMappingRecord.HasValue)
            {
                return matchingMappingRecord.Value.Key;
            }
            else
            {
                return null;
            }
        }
 
        private static ModelItem FindModelItemInMap(SourceLocation sourceLocation, IDictionary<SourceLocation, ModelItem> map)
        {
            SourceLocation exactSourceLocation = GetExactLocation(sourceLocation, map);
            if (exactSourceLocation == null)
            {
                return null;
            }
 
            return map[exactSourceLocation];
        }
 
        internal IEnumerable<ModelItem> GetObjectsWithSourceLocation()
        {
            this.EnsureUpdated();
            return this.sourceLocationToModelItemMapping.Values;
        }
 
        private static SourceLocation FindSrcLocation(Dictionary<object, SourceLocation> mapping, Predicate<object> predicate)
        {
            object foundedObject = null;
            if (mapping == null)
            {
                return null;
            }
 
            foreach (object key in mapping.Keys)
            {
                if (predicate(key))
                {
                    foundedObject = key;
                    break;
                }
            }
 
            if (foundedObject != null)
            {
                SourceLocation result = mapping[foundedObject];
                mapping.Remove(foundedObject);
                return result;
            }
 
            return null;
        }
 
        private static SourceLocation FindMatchSrcLocation(ModelItem modelItem, Dictionary<object, SourceLocation> mapping)
        {
            object modelObject = modelItem.GetCurrentValue();
            return FindSrcLocation(mapping, (key) =>
                {
                    return object.ReferenceEquals(modelObject, key);
                });
        }
 
        private SourceLocation FindViewStateDataSrcLocationByViewStateIdRef(string workflowViewStateIdRef)
        {
            if (this.ViewStateDataSourceLocationMapping == null)
            {
                return null;
            }
 
            SourceLocation sourceLocation = null;
            this.ViewStateDataSourceLocationMapping.TryGetValue(workflowViewStateIdRef, out sourceLocation);
            return sourceLocation;
        }
 
        // get the minimum source location which contains this source location and is in the mapping store.
        private static SourceLocation GetExactLocation(SourceLocation approximateLocation, IDictionary<SourceLocation, ModelItem> mapping)
        {
            SourceLocation candidate = null;
            foreach (SourceLocation srcLocation in mapping.Keys)
            {
                // in the scope?
                if (srcLocation.Contains(approximateLocation))
                {
                    if (candidate != null)
                    {
                        // More approximate?
                        if (candidate.Contains(srcLocation))
                        {
                            candidate = srcLocation;
                        }
                    }
                    else
                    {
                        candidate = srcLocation;
                    }
                }
            }
 
            return candidate;
        }
 
        private void EnsureUpdated()
        {
            if (this.updateRequired)
            {
                IEnumerable<ModelItem> itemsOnDesigner = this.modelSearchService.GetItemsOnDesigner(preOrder: false, excludeRoot: false, excludeErrorActivity: false, excludeExpression: false, includeOtherObjects: true);
                this.UpdateSourceLocationToModelItemMapping(itemsOnDesigner);
            }
        }
 
        private Dictionary<object, SourceLocation> GetValidSourceLocationMapping()
        {
            Dictionary<object, SourceLocation> validSrcLocMapping = new Dictionary<object, SourceLocation>();
            foreach (KeyValuePair<object, SourceLocation> entry in deserializedObjectToSourceLocationMapping)
            {
                if (IsValidRange(entry.Value.StartLine, entry.Value.StartColumn, entry.Value.EndLine, entry.Value.EndColumn))
                {
                    object sourceLocationObject = entry.Key;
                    object modelItemObject;
                    if (this.SourceLocationObjectToModelItemObjectMapping == null)
                    {
                        modelItemObject = sourceLocationObject;
                    }
                    else
                    {
                        this.SourceLocationObjectToModelItemObjectMapping.TryGetValue(sourceLocationObject, out modelItemObject);
                    }
 
                    if (modelItemObject != null)
                    {
                        validSrcLocMapping.Add(modelItemObject, entry.Value);
                    }
                }
            }
            return validSrcLocMapping;
 
        }
 
        private static bool IsValidRange(int startLine, int startColumn, int endLine, int endColumn)
        {
            return
                (startLine > 0) && (startColumn > 0) && (endLine > 0) && (endColumn > 0) &&
                ((startLine < endLine) || (startLine == endLine && startColumn < endColumn));
        }
    }
}