Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added DefaultProvider

  • Loading branch information...
commit 2b87c36f07d321fa04eea07fe080d0e73c78a469 1 parent 5fc09fb
@adrianaisemberg authored
View
4 CLAP/CLAP.csproj
@@ -83,7 +83,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CollectionValidationAttribute.cs" />
+ <Compile Include="DefaultProvider.cs" />
<Compile Include="HelpInfo.cs" />
+ <Compile Include="Interception\UserVerbExecutionContext.cs" />
<Compile Include="Parser.WinForms.cs" />
<Compile Include="EnvironmentParserHandlers.cs" />
<Compile Include="Parser.Console.cs" />
@@ -102,7 +104,7 @@
<Compile Include="Interception\ParameterAndValue.cs" />
<Compile Include="Interception\PreVerbExecutionAttribute.cs" />
<Compile Include="Interception\PreVerbExecutionContext.cs" />
- <Compile Include="Interception\VerbExecutionContext.cs" />
+ <Compile Include="VerbExecutionContext.cs" />
<Compile Include="Interception\VerbInterception.cs" />
<Compile Include="Method.cs" />
<Compile Include="MethodInvoker.cs" />
View
16 CLAP/DefaultProvider.cs
@@ -0,0 +1,16 @@
+
+namespace CLAP
+{
+ public abstract class DefaultProvider
+ {
+ public abstract object GetDefault(VerbExecutionContext context);
+
+ public virtual string Description
+ {
+ get
+ {
+ return GetType().Name;
+ }
+ }
+ }
+}
View
46 CLAP/Exceptions.cs
@@ -138,12 +138,12 @@ public class MissingRequiredArgumentException : CommandLineParserException
/// <summary>
/// The verb that requires the parameter
/// </summary>
- public string Verb { get; private set; }
+ public Method Method { get; private set; }
- public MissingRequiredArgumentException(string verb, string parameter)
+ public MissingRequiredArgumentException(Method method, string parameter)
: base("Missing argument for required parameter '{0}'".FormatWith(parameter))
{
- Verb = verb;
+ Method = method;
ParameterName = parameter;
}
@@ -377,4 +377,44 @@ public InvalidHelpHandlerException(MethodInfo method, Exception ex)
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
+
+ [Serializable]
+ public class AmbiguousParameterDefaultException : CommandLineParserException
+ {
+ /// <summary>
+ /// The method that is defined as help
+ /// </summary>
+ public ParameterInfo Parameter { get; private set; }
+
+ public AmbiguousParameterDefaultException(ParameterInfo parameter)
+ : base("Parameter '{0}' of '{1}' has both a Default and a DefaultProvider".FormatWith(parameter.Name, parameter.Member.Name))
+ {
+ Parameter = parameter;
+ }
+
+ protected AmbiguousParameterDefaultException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
+
+ [Serializable]
+ public class InvalidParameterDefaultProviderException : CommandLineParserException
+ {
+ /// <summary>
+ /// The method that is defined as help
+ /// </summary>
+ public ParameterInfo Parameter { get; private set; }
+
+ public InvalidParameterDefaultProviderException(ParameterInfo parameter)
+ : base("Parameter '{0}' of '{1}' has an invalid DefaultProvider".FormatWith(parameter.Name, parameter.Member.Name))
+ {
+ Parameter = parameter;
+ }
+
+ protected InvalidParameterDefaultProviderException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
}
View
2  CLAP/HelpGenerator.cs
@@ -53,7 +53,7 @@ private static ParserHelpInfo GetParserHelp(ParserRunner parser)
Required = p.Required,
Names = p.Names,
Type = p.ParameterInfo.ParameterType,
- Default = p.Default,
+ Default = p.DefaultProvider != null ? p.DefaultProvider.Description : p.Default,
Description = p.Description,
Validations = p.ParameterInfo.GetAttributes<ValidationAttribute>().Select(v => v.Description).ToList(),
}).ToList(),
View
2  CLAP/Interception/PostVerbExecutionContext.cs
@@ -6,7 +6,7 @@ namespace CLAP.Interception
/// <summary>
/// The context after a verb was executed
/// </summary>
- public sealed class PostVerbExecutionContext : VerbExecutionContext
+ public sealed class PostVerbExecutionContext : UserVerbExecutionContext
{
#region Properties
View
2  CLAP/Interception/PreVerbExecutionContext.cs
@@ -5,7 +5,7 @@ namespace CLAP.Interception
/// <summary>
/// The context before a verb is to be executed
/// </summary>
- public sealed class PreVerbExecutionContext : VerbExecutionContext
+ public sealed class PreVerbExecutionContext : UserVerbExecutionContext
{
#region Properties
View
17 CLAP/Interception/VerbExecutionContext.cs → CLAP/Interception/UserVerbExecutionContext.cs
@@ -2,10 +2,7 @@
namespace CLAP.Interception
{
- /// <summary>
- /// A verb execution context
- /// </summary>
- public abstract class VerbExecutionContext
+ public abstract class UserVerbExecutionContext
{
/// <summary>
/// The method that is executed
@@ -19,17 +16,17 @@ public abstract class VerbExecutionContext
public object Target { get; private set; }
/// <summary>
- /// The list of parameters and their values
- /// </summary>
- public ParameterAndValue[] Parameters { get; private set; }
-
- /// <summary>
/// A user-context that can be filled with custom keys and values.
/// Once filled in the pre-execution context - it is available in the post-execution context.
/// </summary>
public Dictionary<object, object> UserContext { get; private set; }
- protected VerbExecutionContext(
+ /// <summary>
+ /// The list of parameters and their values
+ /// </summary>
+ public ParameterAndValue[] Parameters { get; private set; }
+
+ protected UserVerbExecutionContext(
Method method,
object target,
ParameterAndValue[] parameters,
View
11 CLAP/Parameter.cs
@@ -19,6 +19,11 @@ public sealed class Parameter
public object Default { get; private set; }
/// <summary>
+ /// The default value provider
+ /// </summary>
+ public DefaultProvider DefaultProvider { get; private set; }
+
+ /// <summary>
/// Whether this parameter is required
/// </summary>
public Boolean Required { get; private set; }
@@ -63,6 +68,12 @@ internal Parameter(ParameterInfo parameter)
if (parameterAttribute != null)
{
Default = parameterAttribute.Default;
+
+ if (parameterAttribute.DefaultProvider != null)
+ {
+ DefaultProvider = (DefaultProvider)Activator.CreateInstance(parameterAttribute.DefaultProvider);
+ }
+
Required = parameterAttribute.Required;
Description = parameterAttribute.Description;
View
9 CLAP/ParameterAttribute.cs
@@ -15,6 +15,15 @@ public sealed class ParameterAttribute : Attribute
public object Default { get; set; }
/// <summary>
+ /// The default provider type
+ /// </summary>
+ /// <remarks>
+ /// The type must derive from CLAP.DefaultProvider.
+ /// A parameter cannot have both a Default and a DefaultProvider defined.
+ /// </remarks>
+ public Type DefaultProvider { get; set; }
+
+ /// <summary>
/// Whether this parameter is required
/// </summary>
public Boolean Required { get; set; }
View
38 CLAP/ParserRunner.cs
@@ -144,7 +144,7 @@ private void RunInternal(string[] args, object obj)
// a list of values, used when invoking the method
//
- var parameterValues = ValuesFactory.CreateParameterValues(verb, inputArgs, paremetersList);
+ var parameterValues = ValuesFactory.CreateParameterValues(method, obj, inputArgs, paremetersList);
ValidateVerbInput(method, parameterValues.Select(kvp => kvp.Value).ToList());
@@ -357,6 +357,39 @@ internal static void Validate(Type type)
//
ValidateDefinedPreInterceptors(type);
ValidateDefinedPostInterceptors(type);
+
+ // parameters can't have both Default and DefaultProvider
+ //
+ ValidateParameterDefaults(verbMethods);
+ }
+
+ private static void ValidateParameterDefaults(IEnumerable<Method> verbs)
+ {
+ var parameters = verbs.SelectMany(v => v.MethodInfo.GetParameters());
+ var dict = parameters.
+ Where(p => p.HasAttribute<ParameterAttribute>()).
+ ToDictionary(p => p.GetAttribute<ParameterAttribute>(), p => p);
+
+ // find one with both a Default and a DefaultProvider
+ //
+ var bad = dict.Where(kvp => kvp.Key.DefaultProvider != null && kvp.Key.Default != null);
+
+ if (bad.Any())
+ {
+ throw new AmbiguousParameterDefaultException(bad.First().Value);
+ }
+
+
+ // make sure all default providers are DefaultProvider
+ //
+ bad = dict.Where(kvp =>
+ kvp.Key.DefaultProvider != null &&
+ !typeof(DefaultProvider).IsAssignableFrom(kvp.Key.DefaultProvider));
+
+ if (bad.Any())
+ {
+ throw new InvalidParameterDefaultProviderException(bad.First().Value);
+ }
}
private static void ValidateDefinedEmptyHandlers(Type type)
@@ -737,7 +770,8 @@ internal void HandleEmptyArguments(object target)
// create an array of arguments that matches the method
//
var parameters = ValuesFactory.CreateParameterValues(
- defaultVerb.MethodInfo.Name,
+ defaultVerb,
+ target,
new Dictionary<string, string>(),
GetParameters(defaultVerb.MethodInfo));
View
23 CLAP/ValuesFactory.cs
@@ -156,12 +156,18 @@ private static TConvert[] ConvertToArray<TConvert>(string[] values)
}
internal static ParameterAndValue[] CreateParameterValues(
- string verb,
+ Method method,
+ object target,
Dictionary<string, string> inputArgs,
IEnumerable<Parameter> list)
{
var parameters = new List<ParameterAndValue>();
+ // inputArgs is getting emptied.
+ // create a copy for the VerbExecutionContext
+ //
+ var inputArgsCopy = inputArgs.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+
foreach (var p in list)
{
var parameterInfo = p.ParameterInfo;
@@ -186,12 +192,19 @@ private static TConvert[] ConvertToArray<TConvert>(string[] values)
{
if (p.Required)
{
- throw new MissingRequiredArgumentException(verb, parameterInfo.Name);
+ throw new MissingRequiredArgumentException(method, parameterInfo.Name);
}
- // the default is the value
- //
- value = p.Default;
+ if (p.DefaultProvider != null)
+ {
+ value = p.DefaultProvider.GetDefault(new VerbExecutionContext(method, target, inputArgsCopy));
+ }
+ else
+ {
+ // the default is the value
+ //
+ value = p.Default;
+ }
// convert the default value, if different from parameter's value (guid, for example)
//
View
36 CLAP/VerbExecutionContext.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+
+namespace CLAP
+{
+ /// <summary>
+ /// A verb execution context
+ /// </summary>
+ public class VerbExecutionContext
+ {
+ /// <summary>
+ /// The method that is executed
+ /// </summary>
+ public Method Method { get; private set; }
+
+ /// <summary>
+ /// The target object, if any, that the verb is executed on.
+ /// If the verb is static, this is null.
+ /// </summary>
+ public object Target { get; private set; }
+
+ /// <summary>
+ /// The input arguments
+ /// </summary>
+ public Dictionary<string, string> Input { get; set; }
+
+ internal VerbExecutionContext(
+ Method method,
+ object target,
+ Dictionary<string, string> input)
+ {
+ Method = method;
+ Target = target;
+ Input = input;
+ }
+ }
+}
View
49 Tests/Samples.cs
@@ -1152,4 +1152,53 @@ public class TypeWithProps
[MoreThan(10)]
public int Number { get; set; }
}
+
+ public class ParameterWithDefaults_1
+ {
+ public string P1 { get; set; }
+ public string P2 { get; set; }
+
+ [Verb]
+ public void Foo(
+ [Parameter(Default = "def1")] string p1,
+ [Parameter(DefaultProvider = typeof(MyDefault))] string p2)
+ {
+ P1 = p1;
+ P2 = p2;
+ }
+
+ public class MyDefault : DefaultProvider
+ {
+ public override object GetDefault(VerbExecutionContext context)
+ {
+ return "def2";
+ }
+ }
+ }
+
+ public class ParameterWithDefaults_2
+ {
+ [Verb]
+ public void Foo(
+ [Parameter(Default = "def1", DefaultProvider = typeof(MyDefault))] string p1)
+ {
+ }
+
+ public class MyDefault : DefaultProvider
+ {
+ public override object GetDefault(VerbExecutionContext context)
+ {
+ return "def2";
+ }
+ }
+ }
+
+ public class ParameterWithDefaults_3
+ {
+ [Verb]
+ public void Foo(
+ [Parameter(DefaultProvider = typeof(System.Text.StringBuilder))] string p1)
+ {
+ }
+ }
}
View
62 Tests/Tests.cs
@@ -2273,5 +2273,67 @@ public void Parameter_NullableInt()
Assert.AreEqual(null, s.P4);
Assert.AreEqual(null, s.P5);
}
+
+ [Test]
+ public void Parameter_WithDefault_1()
+ {
+ var t = new ParameterWithDefaults_1();
+
+ Parser.Run(new string[] { "foo" }, t);
+
+ Assert.AreEqual("def1", t.P1);
+ Assert.AreEqual("def2", t.P2);
+ }
+
+ [Test]
+ public void Parameter_WithDefault_2()
+ {
+ var t = new ParameterWithDefaults_1();
+
+ Parser.Run(new string[] { "foo", "-p2=bar" }, t);
+
+ Assert.AreEqual("def1", t.P1);
+ Assert.AreEqual("bar", t.P2);
+ }
+
+ [Test]
+ public void Parameter_WithDefault_3()
+ {
+ var t = new ParameterWithDefaults_1();
+
+ Parser.Run(new string[] { "foo", "-p1=zoo" }, t);
+
+ Assert.AreEqual("zoo", t.P1);
+ Assert.AreEqual("def2", t.P2);
+ }
+
+ [Test]
+ public void Parameter_WithDefault_4()
+ {
+ var t = new ParameterWithDefaults_1();
+
+ Parser.Run(new string[] { "foo", "-p1=zoo", "-p2=bar" }, t);
+
+ Assert.AreEqual("zoo", t.P1);
+ Assert.AreEqual("bar", t.P2);
+ }
+
+ [Test]
+ [ExpectedException(typeof(AmbiguousParameterDefaultException))]
+ public void Parameter_WithDefault_AmbiguousDefaults()
+ {
+ var t = new ParameterWithDefaults_2();
+
+ Parser.Run(new string[] { "foo" }, t);
+ }
+
+ [Test]
+ [ExpectedException(typeof(InvalidParameterDefaultProviderException))]
+ public void Parameter_WithDefault_BadDefaultProvider()
+ {
+ var t = new ParameterWithDefaults_3();
+
+ Parser.Run(new string[] { "foo" }, t);
+ }
}
}
View
1  Tests/Tests.csproj
@@ -103,6 +103,7 @@
<Compile Include="Samples.cs" />
<Compile Include="StaticSamples.cs" />
<Compile Include="Tests.cs" />
+ <Compile Include="UtilsTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CLAP\CLAP.csproj">
View
34 Tests/UtilsTests.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using CLAP;
+using NUnit.Framework;
+
+namespace Tests
+{
+ [TestFixture]
+ public class UtilsTests
+ {
+ private static Type GetUtils()
+ {
+ return typeof(Parser).Assembly.GetType("CLAP.Utils");
+ }
+
+ private static object InvokeUtils(string method, params object[] args)
+ {
+ var utils = GetUtils();
+
+ return utils.InvokeMember(
+ method,
+ BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
+ null, null, args);
+ }
+
+ [Test]
+ public void GetGenericTypeName_Test()
+ {
+ Assert.AreEqual("List<Int32>", InvokeUtils("GetGenericTypeName", typeof(List<Int32>)));
+ Assert.AreEqual("Dictionary<String,IEnumerable<Uri>>", InvokeUtils("GetGenericTypeName", typeof(Dictionary<string, IEnumerable<Uri>>)));
+ }
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.