CodeStyleOption.cs 7.4 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// 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.
4

5
using System;
6
using System.Collections.Generic;
7
using System.Xml.Linq;
8
using Microsoft.CodeAnalysis.Diagnostics;
9

10 11 12
#if CODE_STYLE
namespace Microsoft.CodeAnalysis.Internal.Options
#else
13
namespace Microsoft.CodeAnalysis.CodeStyle
14
#endif
15
{
16 17 18
    internal interface ICodeStyleOption
    {
        XElement ToXElement();
19 20 21
        object Value { get; }
        NotificationOption Notification { get; }
        ICodeStyleOption WithValue(object value);
22
        ICodeStyleOption WithNotification(NotificationOption notification);
23 24
    }

25
    /// <summary>
26
    /// Represents a code style option and an associated notification option.  Supports
27
    /// being instantiated with T as a <see cref="bool"/> or an <c>enum type</c>.
C
CyrusNajmabadi 已提交
28 29
    /// 
    /// CodeStyleOption also has some basic support for migration a <see cref="bool"/> option
30
    /// forward to an <c>enum type</c> option.  Specifically, if a previously serialized
C
CyrusNajmabadi 已提交
31 32 33
    /// bool-CodeStyleOption is then deserialized into an enum-CodeStyleOption then 'false' 
    /// values will be migrated to have the 0-value of the enum, and 'true' values will be
    /// migrated to have the 1-value of the enum.
C
CyrusNajmabadi 已提交
34 35 36 37
    /// 
    /// Similarly, enum-type code options will serialize out in a way that is compatible with 
    /// hosts that expect the value to be a boolean.  Specifically, if the enum value is 0 or 1
    /// then those values will write back as false/true.
38
    /// </summary>
39
    public class CodeStyleOption<T> : ICodeStyleOption, IEquatable<CodeStyleOption<T>>
40
    {
41
        public static CodeStyleOption<T> Default => new CodeStyleOption<T>(default, NotificationOption.Silent);
42

C
CyrusNajmabadi 已提交
43
        private const int SerializationVersion = 1;
44

45 46
        private NotificationOption _notification;

47
        public CodeStyleOption(T value, NotificationOption notification)
48
        {
49
            Value = value;
50
            _notification = notification ?? throw new ArgumentNullException(nameof(notification));
51 52
        }

53
        public T Value { get; set; }
54

55 56
        object ICodeStyleOption.Value => this.Value;
        ICodeStyleOption ICodeStyleOption.WithValue(object value) => new CodeStyleOption<T>((T)value, Notification);
57
        ICodeStyleOption ICodeStyleOption.WithNotification(NotificationOption notification) => new CodeStyleOption<T>(Value, notification);
58

C
CyrusNajmabadi 已提交
59 60
        private int EnumValueAsInt32 => (int)(object)Value;

61 62 63 64 65
        public NotificationOption Notification
        {
            get => _notification;
            set => _notification = value ?? throw new ArgumentNullException(nameof(value));
        }
66

67
        public XElement ToXElement() =>
68
            new XElement(nameof(CodeStyleOption<T>), // `nameof()` returns just "CodeStyleOption"
69
                new XAttribute(nameof(SerializationVersion), SerializationVersion),
70 71
                new XAttribute("Type", GetTypeNameForSerialization()),
                new XAttribute(nameof(Value), GetValueForSerialization()),
72
                new XAttribute(nameof(DiagnosticSeverity), Notification.Severity.ToDiagnosticSeverity() ?? DiagnosticSeverity.Hidden));
73

74 75
        private object GetValueForSerialization()
        {
C
CyrusNajmabadi 已提交
76 77 78 79 80
            if (typeof(T) == typeof(string))
            {
                return Value;
            }
            else if (typeof(T) == typeof(bool))
81 82 83
            {
                return Value;
            }
C
CyrusNajmabadi 已提交
84 85 86 87
            else if (IsZeroOrOneValueOfEnum())
            {
                return EnumValueAsInt32 == 1;
            }
88 89
            else
            {
C
CyrusNajmabadi 已提交
90
                return EnumValueAsInt32;
91 92 93 94 95
            }
        }

        private string GetTypeNameForSerialization()
        {
96 97 98 99
            if (typeof(T) == typeof(string))
            {
                return nameof(String);
            }
C
CyrusNajmabadi 已提交
100
            if (typeof(T) == typeof(bool) || IsZeroOrOneValueOfEnum())
101 102 103 104 105 106 107 108 109
            {
                return nameof(Boolean);
            }
            else
            {
                return nameof(Int32);
            }
        }

C
CyrusNajmabadi 已提交
110 111 112 113 114 115
        private bool IsZeroOrOneValueOfEnum()
        {
            var intVal = EnumValueAsInt32;
            return intVal == 0 || intVal == 1;
        }

116
        public static CodeStyleOption<T> FromXElement(XElement element)
117
        {
118 119
            var typeAttribute = element.Attribute("Type");
            var valueAttribute = element.Attribute(nameof(Value));
120
            var severityAttribute = element.Attribute(nameof(DiagnosticSeverity));
121
            var version = (int)element.Attribute(nameof(SerializationVersion));
122

123
            if (typeAttribute == null || valueAttribute == null || severityAttribute == null)
124 125 126 127 128
            {
                // data from storage is corrupt, or nothing has been stored yet.
                return Default;
            }

129 130 131 132 133
            if (version != SerializationVersion)
            {
                return Default;
            }

134
            var parser = GetParser(typeAttribute.Value);
135
            var value = parser(valueAttribute.Value);
136
            var severity = (DiagnosticSeverity)Enum.Parse(typeof(DiagnosticSeverity), severityAttribute.Value);
137

C
Cyrus Najmabadi 已提交
138
            return new CodeStyleOption<T>(value, severity switch
139
            {
C
Cyrus Najmabadi 已提交
140 141 142 143 144 145
                DiagnosticSeverity.Hidden => NotificationOption.Silent,
                DiagnosticSeverity.Info => NotificationOption.Suggestion,
                DiagnosticSeverity.Warning => NotificationOption.Warning,
                DiagnosticSeverity.Error => NotificationOption.Error,
                _ => throw new ArgumentException(nameof(element)),
            });
146 147
        }

148
        private static Func<string, T> GetParser(string type)
C
Cyrus Najmabadi 已提交
149
            => type switch
150
            {
C
Cyrus Najmabadi 已提交
151
                nameof(Boolean) =>
152 153 154
                    // Try to map a boolean value.  Either map it to true/false if we're a 
                    // CodeStyleOption<bool> or map it to the 0 or 1 value for an enum if we're
                    // a CodeStyleOption<SomeEnumType>.
C
Cyrus Najmabadi 已提交
155 156 157 158 159
                    (Func<string, T>)(v => Convert(bool.Parse(v))),
                nameof(Int32) => v => Convert(int.Parse(v)),
                nameof(String) => v => (T)(object)v,
                _ => throw new ArgumentException(nameof(type)),
            };
160

161
        private static T Convert(bool b)
162
        {
C
CyrusNajmabadi 已提交
163
            // If we had a bool and we wanted a bool, then just return this value.
164
            if (typeof(T) == typeof(bool))
165
            {
166
                return (T)(object)b;
167 168
            }

C
CyrusNajmabadi 已提交
169 170 171 172 173 174 175 176 177 178 179
            // Map booleans to the 1/0 value of the enum.
            return b ? (T)(object)1 : (T)(object)0;
        }

        private static T Convert(int i)
        {
            // We got an int, but we wanted a bool.  Map 0 to false, 1 to true, and anything else to default.
            if (typeof(T) == typeof(bool))
            {
                return (T)(object)(i == 1);
            }
180

C
CyrusNajmabadi 已提交
181 182
            // If had an int and we wanted an enum, then just return this value.
            return (T)(object)(i);
183 184
        }

185 186 187 188 189 190 191 192
        public bool Equals(CodeStyleOption<T> other)
            => EqualityComparer<T>.Default.Equals(Value, other.Value) &&
               Notification == other.Notification;

        public override bool Equals(object obj)
            => obj is CodeStyleOption<T> option &&
               Equals(option);

193
        public override int GetHashCode()
194
            => unchecked((Notification.GetHashCode() * (int)0xA5555529) + Value.GetHashCode());
195
    }
S
Sam Harwell 已提交
196
}