| File: Collections\IdentifierCollection.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// 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. using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using System.Diagnostics; namespace Microsoft.CodeAnalysis { /// <summary> /// A dictionary that maps strings to all known spellings of that string. Can be used to /// efficiently store the set of known type names for a module for both VB and C# while also /// answering questions like "do you have a type called Goo" in either a case sensitive or /// insensitive manner. /// </summary> internal partial class IdentifierCollection { // Maps an identifier to all spellings of that identifier in this module. The value type is // typed as object so that it can store either an individual element (the common case), or a // collection. // // Note: we use a case insensitive comparer so that we can quickly lookup if we know a name // regardless of its case. private readonly Dictionary<string, object> _map = new Dictionary<string, object>( StringComparer.OrdinalIgnoreCase); public IdentifierCollection() { } public IdentifierCollection(IEnumerable<string> identifiers) { this.AddIdentifiers(identifiers); } public void AddIdentifiers(IEnumerable<string> identifiers) { foreach (var identifier in identifiers) { AddIdentifier(identifier); } } public void AddIdentifier(string identifier) { RoslynDebug.Assert(identifier != null); object? value; if (!_map.TryGetValue(identifier, out value)) { AddInitialSpelling(identifier); } else { AddAdditionalSpelling(identifier, value); } } private void AddAdditionalSpelling(string identifier, object value) { // Had a mapping for it. It will either map to a single // spelling, or to a set of spellings. var strValue = value as string; if (strValue != null) { if (!string.Equals(identifier, strValue, StringComparison.Ordinal)) { // We now have two spellings. Create a collection for // that and map the name to it. _map[identifier] = new HashSet<string> { identifier, strValue }; } } else { // We have multiple spellings already. var spellings = (HashSet<string>)value; // Note: the set will prevent duplicates. spellings.Add(identifier); } } private void AddInitialSpelling(string identifier) { // We didn't have any spellings for this word already. Just // add the word as the single known spelling. _map.Add(identifier, identifier); } public bool ContainsIdentifier(string identifier, bool caseSensitive) { RoslynDebug.Assert(identifier != null); if (caseSensitive) { return CaseSensitiveContains(identifier); } else { return CaseInsensitiveContains(identifier); } } private bool CaseInsensitiveContains(string identifier) { // Simple case. Just check if we've mapped this word to // anything. The map will take care of the case insensitive // lookup for us. return _map.ContainsKey(identifier); } private bool CaseSensitiveContains(string identifier) { object? spellings; if (_map.TryGetValue(identifier, out spellings)) { var spelling = spellings as string; if (spelling != null) { return string.Equals(identifier, spelling, StringComparison.Ordinal); } var set = (HashSet<string>)spellings; return set.Contains(identifier); } return false; } public ICollection<string> AsCaseSensitiveCollection() { return new CaseSensitiveCollection(this); } public ICollection<string> AsCaseInsensitiveCollection() { return new CaseInsensitiveCollection(this); } } }