File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\NamingStyles\Serialization\NamingStylePreferences.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.NamingStyles;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
 
/// <summary>
/// Contains all information related to Naming Style Preferences.
/// 1. Symbol Specifications
/// 2. Name Style
/// 3. Naming Rule (points to Symbol Specification IDs)
/// </summary>
[DataContract]
internal sealed class NamingStylePreferences : IEquatable<NamingStylePreferences>
{
    private const int s_serializationVersion = 5;
 
    private static readonly string _defaultNamingPreferencesString = $@"
<NamingPreferencesInfo SerializationVersion=""{s_serializationVersion}"">
  <SymbolSpecifications>
    <SymbolSpecification ID=""5c545a62-b14d-460a-88d8-e936c0a39316"" Name=""{CompilerExtensionsResources.Class}"">
      <ApplicableSymbolKindList>
        <TypeKind>Class</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""23d856b4-5089-4405-83ce-749aada99153"" Name=""{CompilerExtensionsResources.Interface}"">
      <ApplicableSymbolKindList>
        <TypeKind>Interface</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""d1796e78-ff66-463f-8576-eb46416060c0"" Name=""{CompilerExtensionsResources.Struct}"">
      <ApplicableSymbolKindList>
        <TypeKind>Struct</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""d8af8dc6-1ade-441d-9947-8946922e198a"" Name=""{CompilerExtensionsResources.Enum}"">
      <ApplicableSymbolKindList>
        <TypeKind>Enum</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""408a3347-b908-4b54-a954-1355e64c1de3"" Name=""{CompilerExtensionsResources.Delegate}"">
      <ApplicableSymbolKindList>
        <TypeKind>Delegate</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""830657f6-e7e5-4830-b328-f109d3b6c165"" Name=""{CompilerExtensionsResources.Event}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Event</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""390caed4-f0a9-42bb-adbb-b44c4a302a22"" Name=""{CompilerExtensionsResources.Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""af410767-f189-47c6-b140-aeccf1ff242e"" Name=""{CompilerExtensionsResources.Private_Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Private</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""8076757e-6a4a-47f1-9b4b-ae8a3284e987"" Name=""{CompilerExtensionsResources.Abstract_Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsAbstract</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""16133061-a8e7-4392-92c3-1d93cd54c218"" Name=""{CompilerExtensionsResources.Static_Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsStatic</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""da6a2919-5aa6-4ad1-a24d-576776ed3974"" Name=""{CompilerExtensionsResources.Property}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Property</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""b24a91ce-3501-4799-b6df-baf044156c83"" Name=""{CompilerExtensionsResources.Public_or_Protected_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""70af42cb-1741-4027-969c-9edc4877d965"" Name=""{CompilerExtensionsResources.Static_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsStatic</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""10790aa6-0a0b-432d-a52d-d252ca92302b"" Name=""{CompilerExtensionsResources.Private_or_Internal_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""ac995be4-88de-4771-9dcc-a456a7c02d89"" Name=""{CompilerExtensionsResources.Private_or_Internal_Static_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsStatic</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""2c07f5bf-bc81-4c2b-82b4-ae9b3ffd0ba4"" Name=""{CompilerExtensionsResources.Types}"">
      <ApplicableSymbolKindList>
        <TypeKind>Class</TypeKind>
        <TypeKind>Struct</TypeKind>
        <TypeKind>Interface</TypeKind>
        <TypeKind>Enum</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""5f3ddba1-279f-486c-801e-5c097c36dd85"" Name=""{CompilerExtensionsResources.Non_Field_Members}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Property</SymbolKind>
        <SymbolKind>Event</SymbolKind>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
  </SymbolSpecifications>
  <NamingStyles>
    <NamingStyle ID=""87e7c501-9948-4b53-b1eb-a6cbe918feee"" Name=""{CompilerExtensionsResources.Pascal_Case}"" Prefix="""" Suffix="""" WordSeparator="""" CapitalizationScheme=""PascalCase"" />
    <NamingStyle ID=""1ecc5eb6-b5fc-49a5-a9f1-a980f3e48c92"" Name=""{CompilerExtensionsResources.Begins_with_I}"" Prefix=""I"" Suffix="""" WordSeparator="""" CapitalizationScheme=""PascalCase"" />
  </NamingStyles>
  <NamingRules>
    <SerializableNamingRule SymbolSpecificationID=""23d856b4-5089-4405-83ce-749aada99153"" NamingStyleID=""1ecc5eb6-b5fc-49a5-a9f1-a980f3e48c92"" EnforcementLevel=""Info"" />
    <SerializableNamingRule SymbolSpecificationID=""2c07f5bf-bc81-4c2b-82b4-ae9b3ffd0ba4"" NamingStyleID=""87e7c501-9948-4b53-b1eb-a6cbe918feee"" EnforcementLevel=""Info"" />
    <SerializableNamingRule SymbolSpecificationID=""5f3ddba1-279f-486c-801e-5c097c36dd85"" NamingStyleID=""87e7c501-9948-4b53-b1eb-a6cbe918feee"" EnforcementLevel=""Info"" />
  </NamingRules>
</NamingPreferencesInfo>
";
 
    [DataMember(Order = 0)]
    public readonly ImmutableArray<SymbolSpecification> SymbolSpecifications;
 
    [DataMember(Order = 1)]
    public readonly ImmutableArray<NamingStyle> NamingStyles;
 
    [DataMember(Order = 2)]
    public ImmutableArray<SerializableNamingRule> SerializableNamingRules
    {
        get
        {
            if (_lazySerializableRules.IsDefault)
            {
                ImmutableInterlocked.InterlockedInitialize(
                    ref _lazySerializableRules,
                    Rules.NamingRules.SelectAsArray(static rule => new SerializableNamingRule
                    {
                        SymbolSpecificationID = rule.SymbolSpecification.ID,
                        NamingStyleID = rule.NamingStyle.ID,
                        EnforcementLevel = rule.EnforcementLevel,
                    }));
            }
 
            return _lazySerializableRules;
        }
    }
 
    private ImmutableArray<SerializableNamingRule> _lazySerializableRules;
 
    public readonly NamingStyleRules Rules;
 
    // used for deserialization
    public NamingStylePreferences(
        ImmutableArray<SymbolSpecification> symbolSpecifications,
        ImmutableArray<NamingStyle> namingStyles,
        ImmutableArray<SerializableNamingRule> serializableRules)
    {
        Contract.ThrowIfFalse(serializableRules.IsEmpty == namingStyles.IsEmpty);
        Contract.ThrowIfFalse(serializableRules.IsEmpty == symbolSpecifications.IsEmpty);
 
        SymbolSpecifications = symbolSpecifications;
        NamingStyles = namingStyles;
        _lazySerializableRules = serializableRules;
 
        Rules = new NamingStyleRules(
            serializableRules.SelectAsArray(static (rule, arg) => new NamingRule(
                arg.symbolSpecifications.Single(static (s, id) => s.ID == id, rule.SymbolSpecificationID),
                arg.namingStyles.Single(static (s, id) => s.ID == id, rule.NamingStyleID),
                rule.EnforcementLevel), (symbolSpecifications, namingStyles)));
    }
 
    public NamingStylePreferences(
        ImmutableArray<SymbolSpecification> symbolSpecifications,
        ImmutableArray<NamingStyle> namingStyles,
        ImmutableArray<NamingRule> namingRules)
    {
        Contract.ThrowIfFalse(namingRules.IsEmpty == namingStyles.IsEmpty);
        Contract.ThrowIfFalse(namingRules.IsEmpty == symbolSpecifications.IsEmpty);
 
        SymbolSpecifications = symbolSpecifications;
        NamingStyles = namingStyles;
        Rules = new NamingStyleRules(namingRules);
    }
 
    public static NamingStylePreferences Default { get; } = FromXElement(XElement.Parse(DefaultNamingPreferencesString));
    public static NamingStylePreferences Empty { get; } = new([], [], ImmutableArray<NamingRule>.Empty);
 
    public static string DefaultNamingPreferencesString => _defaultNamingPreferencesString;
 
    public bool IsEmpty
        => Rules.NamingRules.IsEmpty;
 
    internal XElement CreateXElement()
    {
        return new XElement("NamingPreferencesInfo",
            new XAttribute("SerializationVersion", s_serializationVersion),
            new XElement("SymbolSpecifications", SymbolSpecifications.Select(s => s.CreateXElement())),
            new XElement("NamingStyles", NamingStyles.Select(n => n.CreateXElement())),
            new XElement("NamingRules", SerializableNamingRules.Select(n => n.CreateXElement())));
    }
 
    internal static NamingStylePreferences FromXElement(XElement element)
    {
        element = GetUpgradedSerializationIfNecessary(element);
 
        return new NamingStylePreferences(
            [.. element.Element("SymbolSpecifications")!.Elements(nameof(SymbolSpecification)).Select(SymbolSpecification.FromXElement)],
            [.. element.Element("NamingStyles")!.Elements(nameof(NamingStyle)).Select(NamingStyle.FromXElement)],
            [.. element.Element("NamingRules")!.Elements(nameof(SerializableNamingRule)).Select(SerializableNamingRule.FromXElement)]);
    }
 
    public override bool Equals(object? obj)
        => Equals(obj as NamingStylePreferences);
 
    public bool Equals(NamingStylePreferences? other)
    {
        if (other is null)
            return false;
 
        return SymbolSpecifications.SequenceEqual(other.SymbolSpecifications)
            && NamingStyles.SequenceEqual(other.NamingStyles)
            && Rules.NamingRules.SequenceEqual(other.Rules.NamingRules);
    }
 
    public static bool operator ==(NamingStylePreferences? left, NamingStylePreferences? right)
    {
        if (left is null && right is null)
        {
            return true;
        }
        else if (left is null || right is null)
        {
            return false;
        }
 
        return left.Equals(right);
    }
 
    public static bool operator !=(NamingStylePreferences left, NamingStylePreferences right)
        => !(left == right);
 
    public override int GetHashCode()
    {
        return Hash.Combine(Hash.CombineValues(SymbolSpecifications),
            Hash.Combine(Hash.CombineValues(NamingStyles),
                Hash.CombineValues(Rules.NamingRules)));
    }
 
    private static XElement GetUpgradedSerializationIfNecessary(XElement rootElement)
    {
        var serializationVersion = int.Parse(rootElement.Attribute("SerializationVersion")!.Value);
 
        if (serializationVersion == 4)
        {
            UpgradeSerialization_4To5(rootElement = new XElement(rootElement));
            serializationVersion = 5;
        }
 
        // Add future version checks here. If the version is off by more than 1, these upgrades will run in sequence.
        // The next one should check serializationVersion == 5 and update it to 6.
        // It is also important to create a new roaming location in NamingStyleOptions.NamingPreferences
        // so that we never store the new format in an older version.
        Debug.Assert(s_serializationVersion == 5, "After increasing the serialization version, add an upgrade path here.");
 
        return serializationVersion == s_serializationVersion
            ? rootElement
            : XElement.Parse(DefaultNamingPreferencesString);
    }
 
    private static void UpgradeSerialization_4To5(XElement rootElement)
    {
        var methodElements = rootElement
            .Descendants()
            .Where(e => e.Name.LocalName == "SymbolKind" && e.Value == "Method").ToList();
 
        foreach (var element in methodElements)
        {
            element.ReplaceWith(XElement.Parse("<MethodKind>Ordinary</MethodKind>"));
        }
    }
}