|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Implements a map from an assembly identity to a value. The map allows to look up the value by an identity
/// that either exactly matches the original identity key, or corresponds to a key with the lowest version among identities
/// with higher version than the requested identity key.
/// </summary>
internal sealed class AssemblyIdentityMap<TValue>
{
private readonly Dictionary<string, OneOrMany<KeyValuePair<AssemblyIdentity, TValue>>> _map;
public AssemblyIdentityMap()
{
_map = new Dictionary<string, OneOrMany<KeyValuePair<AssemblyIdentity, TValue>>>(AssemblyIdentityComparer.SimpleNameComparer);
}
public bool Contains(AssemblyIdentity identity, bool allowHigherVersion = true)
{
TValue value;
return TryGetValue(identity, out value, allowHigherVersion);
}
public bool TryGetValue(AssemblyIdentity identity, out TValue value, bool allowHigherVersion = true)
{
OneOrMany<KeyValuePair<AssemblyIdentity, TValue>> sameName;
if (_map.TryGetValue(identity.Name, out sameName))
{
int minHigherVersionCandidate = -1;
for (int i = 0; i < sameName.Count; i++)
{
AssemblyIdentity currentIdentity = sameName[i].Key;
if (AssemblyIdentity.EqualIgnoringNameAndVersion(currentIdentity, identity))
{
if (currentIdentity.Version == identity.Version)
{
value = sameName[i].Value;
return true;
}
// only higher version candidates are considered for match:
if (!allowHigherVersion || currentIdentity.Version < identity.Version)
{
continue;
}
if (minHigherVersionCandidate == -1 || currentIdentity.Version < sameName[minHigherVersionCandidate].Key.Version)
{
minHigherVersionCandidate = i;
}
}
}
if (minHigherVersionCandidate >= 0)
{
value = sameName[minHigherVersionCandidate].Value;
return true;
}
}
value = default(TValue);
return false;
}
public bool TryGetValue(AssemblyIdentity identity, out TValue value, Func<Version, Version, TValue, bool> comparer)
{
OneOrMany<KeyValuePair<AssemblyIdentity, TValue>> sameName;
if (_map.TryGetValue(identity.Name, out sameName))
{
for (int i = 0; i < sameName.Count; i++)
{
AssemblyIdentity currentIdentity = sameName[i].Key;
if (comparer(identity.Version, currentIdentity.Version, sameName[i].Value) &&
AssemblyIdentity.EqualIgnoringNameAndVersion(currentIdentity, identity))
{
value = sameName[i].Value;
return true;
}
}
}
value = default(TValue);
return false;
}
public void Add(AssemblyIdentity identity, TValue value)
{
var pair = KeyValuePairUtil.Create(identity, value);
OneOrMany<KeyValuePair<AssemblyIdentity, TValue>> sameName;
_map[identity.Name] = _map.TryGetValue(identity.Name, out sameName) ? sameName.Add(pair) : OneOrMany.Create(pair);
}
}
}
|