Skip to content

Commit 9a442b9

Browse files
committed
refactor ArgumentInfo to make immutable, reorganize code
1 parent f158d0e commit 9a442b9

File tree

2 files changed

+104
-91
lines changed

2 files changed

+104
-91
lines changed

src/Utility.CommandLine.Arguments/Arguments.cs

Lines changed: 99 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,52 @@ public ArgumentAttribute(char shortName, string longName, string helpText = null
121121
public char ShortName { get; set; }
122122
}
123123

124+
/// <summary>
125+
/// Encapsulates argument names and help text.
126+
/// </summary>
127+
public class ArgumentInfo
128+
{
129+
/// <summary>
130+
/// Initializes a new instance of the <see cref="ArgumentInfo"/> class.
131+
/// </summary>
132+
/// <param name="shortName">The short name of the argument.</param>
133+
/// <param name="longName">The long name of the argument.</param>
134+
/// <param name="helpText">The help text for the argument.</param>
135+
/// <param name="property">The property with which the argument is associated.</param>
136+
public ArgumentInfo(char shortName, string longName, string helpText, PropertyInfo property)
137+
{
138+
ShortName = shortName;
139+
LongName = longName;
140+
HelpText = helpText;
141+
Property = property;
142+
}
143+
144+
/// <summary>
145+
/// Gets the help text for the argument.
146+
/// </summary>
147+
public string HelpText { get; }
148+
149+
/// <summary>
150+
/// Gets a value indicating whether the argument backing Type is a collection.
151+
/// </summary>
152+
public bool IsCollection => Property.PropertyType.IsArray || (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(List<>));
153+
154+
/// <summary>
155+
/// Gets the long name of the argument.
156+
/// </summary>
157+
public string LongName { get; }
158+
159+
/// <summary>
160+
/// Gets the property with which the argument is associated.
161+
/// </summary>
162+
public PropertyInfo Property { get; }
163+
164+
/// <summary>
165+
/// Gets the short name of the argument.
166+
/// </summary>
167+
public char ShortName { get; }
168+
}
169+
124170
/// <summary>
125171
/// Provides static methods used to retrieve the command line arguments and operands with which the application was
126172
/// started, as well as a Type to contain them.
@@ -162,21 +208,16 @@ private Arguments(string commandLineString, List<KeyValuePair<string, string>> a
162208
}
163209

164210
/// <summary>
165-
/// Gets the target Type, if applicable.
211+
/// Gets a dictionary containing the arguments and values specified in the command line arguments with which the
212+
/// application was started.
166213
/// </summary>
167-
public Type TargetType { get; }
214+
public Dictionary<string, object> ArgumentDictionary { get; }
168215

169216
/// <summary>
170217
/// Gets the list of arguments specified in the command line arguments with which the application was started.
171218
/// </summary>
172219
public List<KeyValuePair<string, string>> ArgumentList { get; }
173220

174-
/// <summary>
175-
/// Gets a dictionary containing the arguments and values specified in the command line arguments with which the
176-
/// application was started.
177-
/// </summary>
178-
public Dictionary<string, object> ArgumentDictionary { get; }
179-
180221
/// <summary>
181222
/// Gets the command line string from which the arguments were parsed.
182223
/// </summary>
@@ -187,6 +228,11 @@ private Arguments(string commandLineString, List<KeyValuePair<string, string>> a
187228
/// </summary>
188229
public List<string> OperandList { get; private set; }
189230

231+
/// <summary>
232+
/// Gets the target Type, if applicable.
233+
/// </summary>
234+
public Type TargetType { get; }
235+
190236
/// <summary>
191237
/// Gets the argument value corresponding to the specified <paramref name="index"/>.
192238
/// </summary>
@@ -201,7 +247,8 @@ public object this[int index]
201247
}
202248

203249
/// <summary>
204-
/// Gets the argument value corresponding to the specified <paramref name="key"/> from the <see cref="ArgumentDictionary"/> property.
250+
/// Gets the argument value corresponding to the specified <paramref name="key"/> from the
251+
/// <see cref="ArgumentDictionary"/> property.
205252
/// </summary>
206253
/// <param name="key">The key for which the value is to be retrieved.</param>
207254
/// <returns>The argument value corresponding to the specified key.</returns>
@@ -213,43 +260,6 @@ public object this[string key]
213260
}
214261
}
215262

216-
private static Dictionary<string, object> GetArgumentDictionary(List<KeyValuePair<string, string>> argumentList, Type targetType = null)
217-
{
218-
var dict = new ConcurrentDictionary<string, object>();
219-
var argumentInfo = targetType == null ? new List<ArgumentInfo>() : GetArgumentInfo(targetType);
220-
221-
foreach (var arg in argumentList)
222-
{
223-
var info = argumentInfo.Where(i => i.ShortName.ToString() == arg.Key || i.LongName == arg.Key).SingleOrDefault();
224-
225-
if (info != default(ArgumentInfo))
226-
{
227-
bool added = false;
228-
229-
foreach (var k in new[] { info.ShortName.ToString(), info.LongName })
230-
{
231-
if (dict.ContainsKey(k))
232-
{
233-
dict.AddOrUpdate(k, arg.Value, (key, existingValue) => info.IsCollection ? ((List<object>)existingValue).Concat(new[] { arg.Value }).ToList() : (object)arg.Value);
234-
added = true;
235-
break;
236-
}
237-
}
238-
239-
if (!added)
240-
{
241-
dict.TryAdd(arg.Key, info.IsCollection ? new List<object>(new[] { arg.Value }) : (object)arg.Value);
242-
}
243-
}
244-
else
245-
{
246-
dict.AddOrUpdate(arg.Key, arg.Value, (key, existingValue) => arg.Value);
247-
}
248-
}
249-
250-
return dict.ToDictionary(a => a.Key, a => a.Value);
251-
}
252-
253263
/// <summary>
254264
/// Retrieves a collection of <see cref="ArgumentInfo"/> gathered from properties in the target <paramref name="type"/>
255265
/// marked with the <see cref="ArgumentAttribute"/><see cref="Attribute"/> along with the short and long names and help text.
@@ -268,13 +278,11 @@ private static Dictionary<string, object> GetArgumentDictionary(List<KeyValuePai
268278

269279
if (attribute != default(CustomAttributeData))
270280
{
271-
retVal.Add(new ArgumentInfo()
272-
{
273-
ShortName = (char)attribute.ConstructorArguments[0].Value,
274-
LongName = (string)attribute.ConstructorArguments[1].Value,
275-
HelpText = (string)attribute.ConstructorArguments[2].Value,
276-
Type = property.PropertyType,
277-
});
281+
retVal.Add(new ArgumentInfo(
282+
shortName: (char)attribute.ConstructorArguments[0].Value,
283+
longName: (string)attribute.ConstructorArguments[1].Value,
284+
helpText: (string)attribute.ConstructorArguments[2].Value,
285+
property: property));
278286
}
279287
}
280288

@@ -526,6 +534,43 @@ private static void ClearProperties(Dictionary<string, PropertyInfo> properties)
526534
}
527535
}
528536

537+
private static Dictionary<string, object> GetArgumentDictionary(List<KeyValuePair<string, string>> argumentList, Type targetType = null)
538+
{
539+
var dict = new ConcurrentDictionary<string, object>();
540+
var argumentInfo = targetType == null ? new List<ArgumentInfo>() : GetArgumentInfo(targetType);
541+
542+
foreach (var arg in argumentList)
543+
{
544+
var info = argumentInfo.Where(i => i.ShortName.ToString() == arg.Key || i.LongName == arg.Key).SingleOrDefault();
545+
546+
if (info != default(ArgumentInfo))
547+
{
548+
bool added = false;
549+
550+
foreach (var k in new[] { info.ShortName.ToString(), info.LongName })
551+
{
552+
if (dict.ContainsKey(k))
553+
{
554+
dict.AddOrUpdate(k, arg.Value, (key, existingValue) => info.IsCollection ? ((List<object>)existingValue).Concat(new[] { arg.Value }).ToList() : (object)arg.Value);
555+
added = true;
556+
break;
557+
}
558+
}
559+
560+
if (!added)
561+
{
562+
dict.TryAdd(arg.Key, info.IsCollection ? new List<object>(new[] { arg.Value }) : (object)arg.Value);
563+
}
564+
}
565+
else
566+
{
567+
dict.AddOrUpdate(arg.Key, arg.Value, (key, existingValue) => arg.Value);
568+
}
569+
}
570+
571+
return dict.ToDictionary(a => a.Key, a => a.Value);
572+
}
573+
529574
private static List<KeyValuePair<string, string>> GetArgumentList(string commandLineString)
530575
{
531576
var argumentList = new List<KeyValuePair<string, string>>();
@@ -640,37 +685,6 @@ private static PropertyInfo GetOperandsProperty(Type type)
640685
}
641686
}
642687

643-
/// <summary>
644-
/// Encapsulates argument names and help text.
645-
/// </summary>
646-
public class ArgumentInfo
647-
{
648-
/// <summary>
649-
/// Gets or sets the help text for the argument.
650-
/// </summary>
651-
public string HelpText { get; set; }
652-
653-
/// <summary>
654-
/// Gets or sets the long name of the argument.
655-
/// </summary>
656-
public string LongName { get; set; }
657-
658-
/// <summary>
659-
/// Gets or sets the short name of the argument.
660-
/// </summary>
661-
public char ShortName { get; set; }
662-
663-
/// <summary>
664-
/// Gets or sets the backing Type of the argument.
665-
/// </summary>
666-
public Type Type { get; set; }
667-
668-
/// <summary>
669-
/// Gets a value indicating whether the argument backing Type is a collection.
670-
/// </summary>
671-
public bool IsCollection => Type.IsArray || (Type.IsGenericType && Type.GetGenericTypeDefinition() == typeof(List<>));
672-
}
673-
674688
/// <summary>
675689
/// Indicates that the property is to be used as the target for automatic population of command line operands when invoking
676690
/// the <see cref="Arguments.Populate(string, bool, string)"/> method.
@@ -679,4 +693,4 @@ public class ArgumentInfo
679693
public class OperandsAttribute : Attribute
680694
{
681695
}
682-
}
696+
}

tests/Utility.CommandLine.Arguments.Tests/ArgumentsTests.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
using System;
3232
using System.Collections.Generic;
3333
using System.Linq;
34+
using System.Reflection;
3435
using Xunit;
3536

3637
namespace Utility.CommandLine.Tests
@@ -53,19 +54,17 @@ public void Constructor()
5354
[Collection("ArgumentInfo")]
5455
public class ArgumentInfoTests
5556
{
57+
private string Test { get; set; }
58+
5659
[Fact]
5760
public void Constructor()
5861
{
59-
ArgumentInfo test = new ArgumentInfo()
60-
{
61-
ShortName = 'a',
62-
LongName = "aa",
63-
HelpText = "help",
64-
};
62+
ArgumentInfo test = new ArgumentInfo('a', "aa", "help", GetType().GetProperty("Test", BindingFlags.Instance | BindingFlags.NonPublic));
6563

6664
Assert.Equal('a', test.ShortName);
6765
Assert.Equal("aa", test.LongName);
6866
Assert.Equal("help", test.HelpText);
67+
Assert.Equal("Test", test.Property.Name);
6968
}
7069

7170
}

0 commit comments

Comments
 (0)