Skip to content

Commit dde3230

Browse files
Added EnumDescriptionConverter
1 parent c54b011 commit dde3230

3 files changed

Lines changed: 389 additions & 0 deletions

File tree

Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
2+
#Region " Usage Examples "
3+
4+
' <TypeConverter(GetType(EnumDescriptionConverter))>
5+
' <Flags>
6+
' Public Enum TestEnum
7+
' <Description("First")> One = 1
8+
' <Description("Second")> Two = 2
9+
' <Description("Third")> Three = 3
10+
' End Enum
11+
'
12+
' Public Class TestClass
13+
' <DefaultValue(TestEnum.MyUpperCamelCaseName)>
14+
' Public Property TestProperty As TestEnum = TestEnum.MyUpperCamelCaseName
15+
' End Class
16+
'
17+
' Public Class Form1
18+
' Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
19+
' Me.PropertyGrid.SelectedObject = New TestClass()
20+
' End Sub
21+
' End Class
22+
23+
#End Region
24+
25+
#Region " Option Statements "
26+
27+
Option Strict Off
28+
Option Explicit On
29+
Option Infer Off
30+
31+
#End Region
32+
33+
#Region " Imports "
34+
35+
Imports System.ComponentModel
36+
Imports System.Globalization
37+
Imports System.Linq
38+
Imports System.Reflection
39+
40+
#End Region
41+
42+
#Region " EnumDescriptionConverter "
43+
44+
' ReSharper disable once CheckNamespace
45+
46+
Namespace DevCase.Runtime.TypeConverters
47+
48+
''' <summary>
49+
''' Provides a way to convert <see cref="[Enum]"/> fields and flag combinations to and from their <see cref="DescriptionAttribute"/> attributes.
50+
''' <para></para>
51+
''' Note: A enumeration field must have a <see cref="DescriptionAttribute"/> attribute defined.
52+
''' </summary>
53+
'''
54+
''' <example> This is a code example.
55+
''' <code language="VB">
56+
''' &lt;TypeConverter(GetType(EnumDescriptionConverter))&gt;
57+
''' &lt;Flags&gt;
58+
''' Public Enum TestEnum
59+
''' &lt;Description("First")&gt; One = 1
60+
''' &lt;Description("Second")&gt; Two = 2
61+
''' &lt;Description("Third")&gt; Three = 3
62+
''' End Enum
63+
'''
64+
''' Public Class TestClass
65+
''' &lt;DefaultValue(TestEnum.MyUpperCamelCaseName)&gt;
66+
''' Public Property TestProperty As TestEnum = TestEnum.MyUpperCamelCaseName
67+
''' End Class
68+
'''
69+
''' Public Class Form1
70+
''' Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
71+
''' Me.PropertyGrid.SelectedObject = New TestClass()
72+
''' End Sub
73+
''' End Class
74+
''' </code>
75+
''' </example>
76+
'''
77+
''' <seealso cref="EnumConverter"/>
78+
Public NotInheritable Class EnumDescriptionConverter : Inherits EnumConverter
79+
80+
#Region " Private Fields "
81+
82+
Private ReadOnly Property HasFlags As Boolean = Attribute.IsDefined(Me.EnumType, GetType(FlagsAttribute))
83+
84+
#End Region
85+
86+
#Region " Constructors "
87+
88+
''' <summary>
89+
''' Initializes a new instance of the <see cref="EnumDescriptionConverter"/> class.
90+
''' </summary>
91+
'''
92+
''' <param name="type">
93+
''' A <see cref="Type"/> that represents the type of enumeration to associate with this enumeration converter.
94+
''' </param>
95+
Public Sub New(type As Type)
96+
MyBase.New(type)
97+
End Sub
98+
99+
#End Region
100+
101+
#Region " Public Methods "
102+
103+
''' <summary>
104+
''' Gets a value indicating whether this object supports a standard set of
105+
''' values that can be picked from a list using the specified context.
106+
''' </summary>
107+
'''
108+
''' <param name="context">
109+
''' An <see cref="ITypeDescriptorContext" /> that provides a format context.
110+
''' </param>
111+
'''
112+
''' <returns>
113+
''' Returns <see langword="True"/> because <see cref="TypeConverter.GetStandardValues"/>
114+
''' should be called to find a common set of values the object supports.
115+
''' <para></para>
116+
''' This method never returns <see langword="False"/>.
117+
''' </returns>
118+
Public Overrides Function GetStandardValuesSupported(context As ITypeDescriptorContext) As Boolean
119+
120+
Return True
121+
End Function
122+
123+
''' <summary>
124+
''' Gets a value indicating whether the list of standard values returned from
125+
''' <see cref="TypeConverter.GetStandardValues"/> is an exclusive list using the specified context.
126+
''' </summary>
127+
'''
128+
''' <param name="context">
129+
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
130+
''' </param>
131+
'''
132+
''' <returns>
133+
''' Returns <see langword="True"/> if the <see cref="StandardValuesCollection"/> returned from
134+
''' <see cref="TypeConverter.GetStandardValues"/> is an exhaustive list of possible values;
135+
''' Returns <see langword="False"/> if other values are possible.
136+
''' </returns>
137+
Public Overrides Function GetStandardValuesExclusive(context As ITypeDescriptorContext) As Boolean
138+
139+
Return False
140+
End Function
141+
142+
''' <summary>
143+
''' Gets a collection of standard values for the data type this validator is designed for.
144+
''' </summary>
145+
'''
146+
''' <param name="context">
147+
''' An <see cref="ITypeDescriptorContext" /> that provides a format context.
148+
''' </param>
149+
'''
150+
''' <returns>
151+
''' A <see cref="StandardValuesCollection"/> that holds a standard set of valid values,
152+
''' or <see langword="null"/> if the data type does not support a standard set of values.
153+
''' </returns>
154+
Public Overrides Function GetStandardValues(context As ITypeDescriptorContext) As StandardValuesCollection
155+
156+
Dim values As New SortedSet(Of Object)
157+
158+
For Each field As FieldInfo In Me.EnumType.GetFields(BindingFlags.Public Or BindingFlags.Static)
159+
Dim value As Object = field.GetValue(Nothing)
160+
values.Add(value)
161+
Next
162+
163+
Return New StandardValuesCollection(values)
164+
End Function
165+
166+
''' <summary>
167+
''' Returns whether this converter can convert the object to the specified type, using the specified context.
168+
''' </summary>
169+
'''
170+
''' <param name="context">
171+
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
172+
''' </param>
173+
'''
174+
''' <param name="destinationType">
175+
''' A <see cref="Type"/> that represents the type you want to convert to.
176+
''' </param>
177+
'''
178+
''' <returns>
179+
''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
180+
''' </returns>
181+
Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
182+
183+
Return destinationType Is GetType(String) OrElse
184+
MyBase.CanConvertTo(context, destinationType)
185+
186+
End Function
187+
188+
''' <summary>
189+
''' Returns whether this converter can convert an object of the given type to the type of this converter,
190+
''' using the specified context.
191+
''' </summary>
192+
'''
193+
''' <param name="context">
194+
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
195+
''' </param>
196+
'''
197+
''' <param name="sourceType">
198+
''' A <see cref="Type"/> that represents the type you want to convert from.
199+
''' </param>
200+
'''
201+
''' <returns>
202+
''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
203+
''' </returns>
204+
Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
205+
206+
Return sourceType Is GetType(String) OrElse
207+
MyBase.CanConvertFrom(context, sourceType)
208+
209+
End Function
210+
211+
''' <summary>
212+
''' Converts the given value object to the specified type, using the specified context and culture information.
213+
''' </summary>
214+
'''
215+
''' <param name="context">
216+
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
217+
''' </param>
218+
'''
219+
''' <param name="culture">
220+
''' A <see cref="CultureInfo"/>. If null is passed, the current culture is assumed.
221+
''' </param>
222+
'''
223+
''' <param name="value">
224+
''' The <see cref="Object"/> to convert.
225+
''' </param>
226+
'''
227+
''' <param name="destinationType">
228+
''' The <see cref="Type"/> to convert the <paramref name="value"/> parameter to.
229+
''' </param>
230+
'''
231+
''' <returns>
232+
''' An <see cref="Object"/> that represents the converted value.
233+
''' </returns>
234+
<DebuggerStepThrough>
235+
Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
236+
237+
If destinationType Is GetType(String) Then
238+
239+
If Not Me.HasFlags Then
240+
Dim fi As FieldInfo = Me.EnumType.GetField([Enum].GetName(Me.EnumType, value))
241+
Dim descAttr As DescriptionAttribute =
242+
CType(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
243+
Return If(descAttr IsNot Nothing, descAttr.Description, value.ToString())
244+
245+
Else
246+
' Allows to parse values like "None" (zero) or "All".
247+
Dim exactField As FieldInfo =
248+
Me.EnumType.GetFields(BindingFlags.Public Or BindingFlags.Static).
249+
FirstOrDefault(Function(fi As FieldInfo) Object.Equals(fi.GetValue(Nothing), value))
250+
If exactField IsNot Nothing Then
251+
Dim exactDescAttr As DescriptionAttribute =
252+
CType(Attribute.GetCustomAttribute(exactField, GetType(DescriptionAttribute)), DescriptionAttribute)
253+
If exactDescAttr IsNot Nothing Then
254+
Dim desc As String = exactDescAttr.Description
255+
If Not desc.Contains(","c) Then
256+
Return desc
257+
Else
258+
'Throw New Exception($"Description contains a comma: {desc}")
259+
Return exactField.Name
260+
End If
261+
Else
262+
Return exactField.Name
263+
End If
264+
265+
Else
266+
Dim names As New List(Of String)
267+
268+
Dim fieldsOrdered As FieldInfo() =
269+
Me.EnumType.GetFields(BindingFlags.Public Or BindingFlags.Static).
270+
OrderBy(Function(f As FieldInfo) f.GetValue(Nothing)).ToArray()
271+
272+
For Each fi As FieldInfo In fieldsOrdered
273+
Dim fiValue As Object = fi.GetValue(Nothing)
274+
If (value And fiValue) = fiValue AndAlso fiValue <> 0 Then
275+
Dim descAttr As DescriptionAttribute =
276+
CType(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
277+
If descAttr IsNot Nothing Then
278+
Dim desc As String = descAttr.Description
279+
' If 'DescriptionAttribute' contains a comma, adds the field name instead.
280+
' This prevents user mistakes.
281+
If Not desc.Contains(","c) Then
282+
names.Add(desc)
283+
Else
284+
' Throw New Exception($"Description contains a comma: {desc}")
285+
names.Add(fi.Name)
286+
End If
287+
Else
288+
names.Add(fi.Name)
289+
End If
290+
291+
End If
292+
Next
293+
294+
Return String.Join(", ", names)
295+
End If
296+
End If
297+
Else
298+
299+
Return MyBase.ConvertTo(context, culture, value, destinationType)
300+
End If
301+
302+
End Function
303+
304+
''' <summary>
305+
''' Converts the given object to the type of this converter, using the specified context and culture information.
306+
''' </summary>
307+
'''
308+
''' <param name="context">
309+
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
310+
''' </param>
311+
'''
312+
''' <param name="culture">
313+
''' The <see cref="CultureInfo"/> to use as the current culture.
314+
''' </param>
315+
'''
316+
''' <param name="value">
317+
''' The <see cref="Object"/> to convert.
318+
''' </param>
319+
'''
320+
''' <returns>
321+
''' An <see cref="Object"/> that represents the converted value.
322+
''' </returns>
323+
<DebuggerStepThrough>
324+
Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As CultureInfo, value As Object) As Object
325+
326+
If Not Me.HasFlags Then
327+
For Each fi As FieldInfo In Me.EnumType.GetFields()
328+
Dim descAttr As DescriptionAttribute =
329+
CType(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
330+
If (descAttr IsNot Nothing) AndAlso DirectCast(value, String) = descAttr.Description Then
331+
Return [Enum].Parse(Me.EnumType, fi.Name, ignoreCase:=False)
332+
End If
333+
Next fi
334+
Return [Enum].Parse(Me.EnumType, DirectCast(value, String))
335+
336+
Else
337+
' Allows to parse a numeric value (e.g. "3" > "Flag 1, Flag 2")
338+
Dim numericValue As ULong
339+
If ULong.TryParse(DirectCast(value, String).Trim(), numericValue) Then
340+
341+
Dim combinedValue As ULong = 0
342+
For Each fi As FieldInfo In Me.EnumType.GetFields(BindingFlags.Public Or BindingFlags.Static)
343+
Dim val As Object = fi.GetValue(Nothing)
344+
If val <> 0 Then combinedValue = combinedValue Or val
345+
Next
346+
347+
If (numericValue And Not combinedValue) = 0 Then
348+
Return [Enum].ToObject(Me.EnumType, numericValue)
349+
Else
350+
Throw New ArgumentException($"The value '{numericValue}' is not a valid combination of defined flags in '{Me.EnumType.Name}' enumeration.")
351+
End If
352+
End If
353+
354+
Dim result As Object = 0
355+
Dim names As String() = DirectCast(value, String).Split(","c).Select(Function(p) p.Trim()).ToArray()
356+
For Each name As String In names
357+
Dim matched As Boolean = False
358+
For Each fi As FieldInfo In Me.EnumType.GetFields(BindingFlags.Public Or BindingFlags.Static)
359+
Dim descAttr As DescriptionAttribute =
360+
CType(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
361+
If (descAttr IsNot Nothing AndAlso String.Equals(descAttr.Description, name, StringComparison.OrdinalIgnoreCase)) OrElse
362+
String.Equals(fi.Name, name, StringComparison.OrdinalIgnoreCase) Then
363+
result = result Or fi.GetValue(Nothing)
364+
matched = True
365+
Exit For
366+
End If
367+
Next
368+
369+
If Not matched Then
370+
Throw New ArgumentException($"No entry was found for '{name}' in the enumeration '{Me.EnumType.Name}'.")
371+
End If
372+
Next
373+
374+
Return [Enum].ToObject(Me.EnumType, result)
375+
End If
376+
377+
End Function
378+
379+
#End Region
380+
381+
End Class
382+
383+
End Namespace
384+
385+
#End Region

Source/Shared/DevCase/TargetExecutionMode.vb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
Imports System.ComponentModel
22

3+
Imports DevCase.Runtime.TypeConverters
4+
35
Namespace DevCase.Core.IO
46

57
''' <summary>
68
''' Defines the target execution strategy and path resolution mode for a shortcut file.
79
''' </summary>
10+
<TypeConverter(GetType(EnumDescriptionConverter))>
811
Public Enum ShortcutTargetExecutionMode As Integer
912

1013
''' <summary>

0 commit comments

Comments
 (0)