forked from UbiquityDotNET/Ubiquity.NET.Utils
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNamespaceQualifiedNameFormatter.cs
More file actions
186 lines (165 loc) · 9.05 KB
/
NamespaceQualifiedNameFormatter.cs
File metadata and controls
186 lines (165 loc) · 9.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
namespace Ubiquity.NET.CodeAnalysis.Utils
{
/// <summary>Custom formatter for <see cref="NamespaceQualifiedName"/> and <see cref="NamespaceQualifiedTypeName"/></summary>
public class NamespaceQualifiedNameFormatter
: IFormatProvider
, ICustomFormatter<NamespaceQualifiedName>
, ICustomFormatter<NamespaceQualifiedTypeName>
{
/// <summary>Initializes a new instance of the <see cref="NamespaceQualifiedNameFormatter"/> class.</summary>
/// <param name="globalPrefix">Global prefix for the language this formatter will produce</param>
/// <param name="aliasMap">Map of aliases for the language (Key is the full namespace name of a type, the values are the alias)</param>
public NamespaceQualifiedNameFormatter( string globalPrefix, IReadOnlyDictionary<string, string> aliasMap )
{
GlobalPrefix = globalPrefix ?? string.Empty;
AliasMap = aliasMap ?? throw new ArgumentNullException( nameof( aliasMap ) );
}
/// <summary>Gets the global prefix for the language formatting (example: "global::")</summary>
public string GlobalPrefix { get; }
/// <summary>Gets the map of type aliases this formatter uses to format an alias</summary>
/// <remarks>
/// The keys for this map are a fully qualified type names and the values are the aliases
/// used in place of the full name.
/// </remarks>
public IReadOnlyDictionary<string, string> AliasMap { get; }
/// <inheritdoc/>
public string Format( string format, object arg, IFormatProvider? formatProvider )
{
if(arg is NamespaceQualifiedName name)
{
return Format( format, name, formatProvider );
}
if(arg is NamespaceQualifiedTypeName typeName)
{
return Format( format, typeName, formatProvider );
}
// Not a type supported; use the type to do it, if it's formattable itself use that
// otherwise fall back to simple Object.ToString().
return arg is IFormattable formattable
? formattable.ToString( format, formatProvider )
: arg.ToString();
}
/// <summary>Formats <paramref name="arg"/> according to <paramref name="format"/></summary>
/// <param name="format">Format string for this instance (see remarks)</param>
/// <param name="arg">The value to format</param>
/// <param name="formatProvider">[ignored]</param>
/// <returns>Formatted string representation of this instance</returns>
/// <remarks>
/// The supported values for <paramref name="format"/> are:
/// <list type="table">
/// <listheader><term>Value</term><description>Description</description></listheader>
/// <item><term>A</term><description>Format as a language specific alias if possible</description></item>
/// <item><term>G</term><description>Format with a language specific global prefix.</description></item>
/// <item><term>AG</term><description>Format with a language specific alias if possible, otherwise include a global prefix.</description></item>
/// <item><term>R</term><description>The raw full name without any qualifications</description></item>
/// </list>
/// </remarks>
/// <exception cref="NotSupportedException"><paramref name="format"/> is not supported</exception>
public string Format( string format, NamespaceQualifiedName arg, IFormatProvider? formatProvider )
{
// default to global prefix or alias if not specified.
format ??= "R";
string rawName = string.Join( ".", arg.FullNameParts );
if(format == "R")
{
return rawName;
}
// Try an alias first as that might be the return value
if((format == "A" || format == "AG") && TryFormatLanguageAlias( rawName, out string? alias ))
{
return alias;
}
// not an alias so, try global prefix if requested.
// The only reason that won't add a prefix is if the GlobalPrefix is null, empty or all whitespace.
// In that case (or if the prefix isn't requested), the full name is returned.
return (format == "G" || format == "AG") && TryPrefixGlobal( rawName, out string? prefixedName )
? prefixedName
: rawName;
}
/// <summary>Formats <paramref name="arg"/> according to <paramref name="format"/></summary>
/// <param name="format">Format string for this instance (see remarks)</param>
/// <param name="arg">The value to format</param>
/// <param name="formatProvider">[ignored]</param>
/// <returns>Formatted string representation of this instance</returns>
/// <remarks>
/// The supported values for <paramref name="format"/> are:
/// <list type="table">
/// <listheader><term>Value</term><description>Description</description></listheader>
/// <item><term>A</term><description>Format as a language specific alias if possible</description></item>
/// <item><term>G</term><description>Format with a language specific global prefix.</description></item>
/// <item><term>AG</term><description>Format with a language specific alias if possible, otherwise include a global prefix.</description></item>
/// <item><term>R</term><description>The raw full name without any qualifications</description></item>
/// </list>
/// <note type="important">
/// This will always append a <c>?</c> to the end of the formatted value IF the <see cref="NamespaceQualifiedTypeName.NullableAnnotation"/>
/// indicates the value is nullable.
/// </note>
/// </remarks>
/// <exception cref="NotSupportedException"><paramref name="format"/> is not supported</exception>
public string Format( string format, NamespaceQualifiedTypeName arg, IFormatProvider? formatProvider )
{
if(arg.IsArray)
{
string formattedElement = Format(format, arg.ElementType, formatProvider);
return $"{formattedElement}[]";
}
string formattedString = Format(format, (NamespaceQualifiedName)arg, formatProvider);
return arg.NullableAnnotation == NullableAnnotation.Annotated
? $"{formattedString}?"
: formattedString;
}
/// <inheritdoc/>
public object? GetFormat( Type formatType )
{
return formatType == typeof( ICustomFormatter<NamespaceQualifiedName> )
|| formatType == typeof( ICustomFormatter<NamespaceQualifiedTypeName> )
? this
: null;
}
private bool TryFormatLanguageAlias( string fullName, [MaybeNullWhen( false )] out string alias )
{
return AliasMap.TryGetValue( fullName, out alias );
}
private bool TryPrefixGlobal( string fullName, [MaybeNullWhen( false )] out string prefixedName )
{
prefixedName = null;
if(string.IsNullOrWhiteSpace( GlobalPrefix ))
{
return false;
}
prefixedName = $"{GlobalPrefix}{fullName}";
return true;
}
/// <summary>Gets a formatter for the C# language</summary>
public static NamespaceQualifiedNameFormatter CSharp { get; }
= new NamespaceQualifiedNameFormatter(
"global::",
/* see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types */
new Dictionary<string, string>()
{
[ "System.Boolean" ] = "bool",
[ "System.Byte" ] = "byte",
[ "System.SByte" ] = "sbyte",
[ "System.Char" ] = "char",
[ "System.Decimal" ] = "decimal",
[ "System.Double" ] = "double",
[ "System.Single" ] = "float",
[ "System.Int32" ] = "int",
[ "System.UInt32" ] = "uint",
// These aren't quite an alias in C# 9 & 10, but C# 11 made them FULL aliases as used here
[ "System.IntPtr" ] = "nint",
[ "System.UIntPtr" ] = "nuint",
[ "System.Int64" ] = "long",
[ "System.UInt64" ] = "ulong",
[ "System.Int16" ] = "short",
[ "System.UInt16" ] = "ushort",
[ "System.Object" ] = "object",
[ "System.String" ] = "string",
[ "System.Delegate" ] = "delegate",
// ["????"] = "dynamic", // No way to map in this direction
}
);
}
}