File: ResolveComReferenceCache.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Shared;
 
#nullable disable
 
namespace Microsoft.Build.Tasks
{
    /// <remarks>
    /// Stores timestamps of COM components processed in the last run. The problem here is that installing/uninstalling
    /// COM components does not update their timestamps with the current time (for a good reason). So if you revert to
    /// an earlier revision of a COM component, its timestamp can go back in time and we still need to regenerate its
    /// wrapper. So in ResolveComReference we compare the stored timestamp with the current component timestamp, and if
    /// they are different, we regenerate the wrapper.
    /// </remarks>
    internal sealed class ResolveComReferenceCache : StateFileBase, ITranslatable
    {
        /// <summary>
        /// Component timestamps.
        /// Key: Component path on disk
        /// Value: DateTime struct
        /// </summary>
        internal Dictionary<string, DateTime> componentTimestamps;
        internal string tlbImpLocation;
        internal string axImpLocation;
 
        /// <summary>
        /// indicates whether the cache contents have changed since it's been created
        /// </summary>
        internal bool Dirty => _dirty;
 
        [NonSerialized]
        private bool _dirty;
 
        /// <summary>
        /// Construct.
        /// </summary>
        internal ResolveComReferenceCache(string tlbImpPath, string axImpPath)
        {
            ErrorUtilities.VerifyThrowArgumentNull(tlbImpPath, nameof(tlbImpPath));
            ErrorUtilities.VerifyThrowArgumentNull(axImpPath, nameof(axImpPath));
 
            tlbImpLocation = tlbImpPath;
            axImpLocation = axImpPath;
            componentTimestamps = new();
        }
 
        /// <summary>
        /// Compares the tlbimp and aximp paths to what the paths were when the cache was created
        /// If these are different return false.
        /// </summary>
        /// <returns>True if both paths match what is in the cache, false otherwise</returns>
        internal bool ToolPathsMatchCachePaths(string tlbImpPath, string axImpPath)
        {
            return String.Equals(tlbImpLocation, tlbImpPath, StringComparison.OrdinalIgnoreCase) &&
                String.Equals(axImpLocation, axImpPath, StringComparison.OrdinalIgnoreCase);
        }
 
        /// <summary>
        /// Gets or sets the timestamp associated with the specified component file
        /// </summary>
        /// <param name="componentPath"></param>
        /// <returns></returns>
        internal DateTime this[string componentPath]
        {
            get
            {
                if (componentTimestamps.TryGetValue(componentPath, out DateTime time))
                {
                    return time;
                }
 
                // If the entry is not present in the cache, return the current time. Since no component should be timestamped
                // with the current time, this will effectively always regenerate the wrapper.
                return DateTime.Now;
            }
            set
            {
                // only set the value and dirty the cache if the timestamp doesn't exist yet or is different than the current one
                if (!DateTime.Equals(this[componentPath], value))
                {
                    componentTimestamps[componentPath] = value;
                    _dirty = true;
                }
            }
        }
 
        public ResolveComReferenceCache(ITranslator translator)
        {
            Translate(translator);
        }
 
        public override void Translate(ITranslator translator)
        {
            translator.Translate(ref axImpLocation);
            translator.Translate(ref tlbImpLocation);
            translator.TranslateDictionary(ref componentTimestamps, StringComparer.Ordinal);
        }
    }
}