Skip to content

Commit

Permalink
Merge pull request #25 from appakz/master
Browse files Browse the repository at this point in the history
Modifications for working with multiple targets
  • Loading branch information
adrianaisemberg committed Oct 29, 2013
2 parents ed10508 + d94f294 commit e8ab7ef
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 35 deletions.
1 change: 1 addition & 0 deletions CLAP/CLAP.csproj
Expand Up @@ -106,6 +106,7 @@
<Compile Include="Interception\ParameterAndValue.cs" />
<Compile Include="Interception\PreVerbExecutionAttribute.cs" />
<Compile Include="Interception\PreVerbExecutionContext.cs" />
<Compile Include="TargetResolver.cs" />
<Compile Include="VerbExecutionContext.cs" />
<Compile Include="Interception\VerbInterception.cs" />
<Compile Include="Method.cs" />
Expand Down
20 changes: 20 additions & 0 deletions CLAP/Exceptions.cs
Expand Up @@ -353,6 +353,26 @@ public MoreThanOneDefaultVerbException(IEnumerable<string> verbs)
: base(info, context) { }
}

[Serializable]
public class DuplicateTargetAliasException : CommandLineParserException
{
/// <summary>
/// The target alias that was determined to be duplicated.
/// </summary>
public string DuplicateTargetAlias { get; private set; }

public DuplicateTargetAliasException(string duplicateTargetAlias)
: base("TargetAlias value '{0}' has been specified more than once of the target types provided.".FormatWith(duplicateTargetAlias))
{
DuplicateTargetAlias = duplicateTargetAlias;
}

protected DuplicateTargetAliasException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}

[Serializable]
public class InvalidHelpHandlerException : CommandLineParserException
{
Expand Down
61 changes: 30 additions & 31 deletions CLAP/MultiParser.cs
Expand Up @@ -61,15 +61,15 @@ private void Init()
{
Debug.Assert(m_types.Any());

Register = new ParserRegistration(m_types, GetHelpString, ValuesFactory.GetValueForParameter);
Register = new ParserRegistration(m_types, GetHelpString);

foreach (var type in m_types)
{
ParserRunner.Validate(type, Register);
}
}

private void HandleEmptyArguments(object[] targets)
private void HandleEmptyArguments(TargetResolver targetResolver)
{
if (Register.RegisteredEmptyHandler != null)
{
Expand All @@ -79,13 +79,13 @@ private void HandleEmptyArguments(object[] targets)
{
var parser = new ParserRunner(m_types.First(), Register, HelpGenerator);

var target = targets == null ? null : targets[0];
var target = targetResolver == null ? null : targetResolver.Resolve(m_types[0]);

parser.HandleEmptyArguments(target);
}
}

private ParserRunner GetMultiTypesParser(string[] args, object obj, ParserRegistration registration)
private ParserRunner GetMultiTypesParser(string[] args, ParserRegistration registration)
{
Debug.Assert(args.Any());

Expand All @@ -110,21 +110,22 @@ private ParserRunner GetMultiTypesParser(string[] args, object obj, ParserRegist
throw new InvalidVerbException();
}

var typeName = parts[0];
var typeNameOrAlias = parts[0];

args[0] = args[0].Substring(typeName.Length + 1);
args[0] = args[0].Substring(typeNameOrAlias.Length + 1);

var type = m_types.FirstOrDefault(t => t.Name.Equals(typeName, StringComparison.InvariantCultureIgnoreCase));
var matchingType = registration.GetTargetType(typeNameOrAlias);

if (type == null)
if (matchingType == null)
{
throw new UnknownParserTypeException(typeName);
throw new UnknownParserTypeException(typeNameOrAlias);
}

return new ParserRunner(type, registration, HelpGenerator);
}
return new ParserRunner(matchingType, registration, HelpGenerator); }



private ParserRunner GetSingleTypeParser(string[] args, object obj, ParserRegistration registration)
private ParserRunner GetSingleTypeParser(string[] args, ParserRegistration registration)
{
Debug.Assert(m_types.Length == 1);

Expand Down Expand Up @@ -177,7 +178,7 @@ private ParserRunner GetSingleTypeParser(string[] args, object obj, ParserRegist
/// <param name="args">The user arguments</param>
public int RunStatic(string[] args)
{
return RunTargets(args, null);
return RunTargets(args, null as TargetResolver);
}

/// <summary>
Expand All @@ -187,37 +188,41 @@ public int RunStatic(string[] args)
/// <param name="targets">The instances of the verb classes</param>
public int RunTargets(string[] args, params object[] targets)
{
ParserRunner parser = null;
var targetResolver = new TargetResolver(targets);
return RunTargets(args, targetResolver);
}

public int RunTargets(string[] args, TargetResolver targetResolver)
{
ParserRunner parser;

try
{
// no args
//
if (args.None() || args.All(a => string.IsNullOrEmpty(a)))
{
HandleEmptyArguments(targets);

HandleEmptyArguments(targetResolver);
return SuccessCode;
}

if (m_types.Length == 1)
{
parser = GetSingleTypeParser(args, targets, Register);
parser = GetSingleTypeParser(args, Register);
}
else
{
Debug.Assert(m_types.Length > 1);

parser = GetMultiTypesParser(args, targets, Register);
parser = GetMultiTypesParser(args, Register);
}

Debug.Assert(parser != null);
}
catch (Exception ex)
{
// handle error using the first available error handler
//
// (if returns true - should rethrow)
//
if (TryHandlePrematureError(ex, targets))
if (TryHandlePrematureError(ex, targetResolver))
{
throw;
}
Expand All @@ -227,18 +232,12 @@ public int RunTargets(string[] args, params object[] targets)
}
}

Debug.Assert(parser != null);

var index = m_types.ToList().IndexOf(parser.Type);

Debug.Assert(index >= 0);

var target = targets.None() ? null : targets[index];
var target = (targetResolver == null || targetResolver.RegisteredTypes.None()) ? null : targetResolver.Resolve(parser.Type);

return parser.Run(args, target);
}

private bool TryHandlePrematureError(Exception ex, object[] targets)
private bool TryHandlePrematureError(Exception ex, TargetResolver targetResolver)
{
var context = new ExceptionContext(ex);

Expand All @@ -258,7 +257,7 @@ private bool TryHandlePrematureError(Exception ex, object[] targets)

if (errorHandler != null)
{
var target = targets == null ? null : targets[i];
var target = targetResolver == null ? null : targetResolver.Resolve(type);

errorHandler.Invoke(target, new[] { context });

Expand Down
9 changes: 9 additions & 0 deletions CLAP/Parser.cs
Expand Up @@ -36,6 +36,15 @@ public static int Run(string[] args, params object[] targets)
return ((MultiParser)p).RunTargets(args, targets);
}

public static int Run(string[] args, TargetResolver targetResolver)
{
Debug.Assert(targetResolver != null);

var p = new Parser(targetResolver.RegisteredTypes);

return p.RunTargets(args, targetResolver);
}

/// <summary>
/// Executes a generic static parser of a specified type
/// </summary>
Expand Down
54 changes: 50 additions & 4 deletions CLAP/ParserRegistration.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CLAP.Interception;
using System.Reflection;

Expand All @@ -13,6 +14,7 @@ public sealed class ParserRegistration
#region Fields

private readonly Type[] m_types;
private IDictionary<string, Type> m_registeredParsersByAlias;

#endregion Fields

Expand All @@ -28,22 +30,24 @@ public sealed class ParserRegistration
internal Func<string> HelpGetter { get; private set; }
internal Func<ParameterInfo, Type, string, string, object> ParameterValueGetter { get; private set; }



#endregion Properties

#region Constructors

internal ParserRegistration(
public ParserRegistration(
Type[] types,
Func<string> helpGetter,
Func<ParameterInfo, Type, string, string, object> parameterValueGetter)
Func<string> helpGetter)
{
m_types = types;

RegisterParserTypeAliases();
RegisteredGlobalHandlers = new Dictionary<string, GlobalParameterHandler>();
RegisteredHelpHandlers = new Dictionary<string, Action<string>>();

HelpGetter = helpGetter;
ParameterValueGetter = parameterValueGetter;
ParameterValueGetter = ValuesFactory.GetValueForParameter;
}

#endregion Constructors
Expand Down Expand Up @@ -256,10 +260,52 @@ public void ParameterHandler<TParameter>(string names, Action<TParameter> action
options);
}

/// <summary>
/// Attempts to get a registered type by the provided value. The value will first be checked against all of the
/// registered type names, and then if no match is found, by the 'TargetAlias' values applied to the types (if any).
/// </summary>
/// <param name="typeNameOrAlias">The name of the requested type or 'null'</param>
/// <returns>The matching type, or 'null' if no match is found.</returns>
public Type GetTargetType(string typeNameOrAlias)
{
var matchByTypeName = m_types.FirstOrDefault(t => t.Name.Equals(typeNameOrAlias, StringComparison.OrdinalIgnoreCase));

if (matchByTypeName != null)
return matchByTypeName;

return m_registeredParsersByAlias.ContainsKey(typeNameOrAlias)
? m_registeredParsersByAlias[typeNameOrAlias]
: null;
}

#endregion Public Methods

#region Private Methods

private void RegisterParserTypeAliases()
{
foreach (var type in m_types)
{
var parserTypeTargetAlias = GetTargetAliasAttributeValue(type);
if (string.IsNullOrEmpty(parserTypeTargetAlias))
continue;

if (m_registeredParsersByAlias == null)
m_registeredParsersByAlias = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

if (m_registeredParsersByAlias.ContainsKey(parserTypeTargetAlias))
throw new DuplicateTargetAliasException(parserTypeTargetAlias);

m_registeredParsersByAlias.Add(parserTypeTargetAlias, type);
}
}

private string GetTargetAliasAttributeValue(Type targetType)
{
var aliasAttribute = targetType.GetCustomAttributes(typeof(TargetAliasAttribute), false).FirstOrDefault() as TargetAliasAttribute;
return (aliasAttribute != null) ? aliasAttribute.Alias : string.Empty;
}

private void RegisterParameterHandlerInternal<TParameter>(string names, Action<TParameter> action, ParameterOptions options)
{
var objectAction = new Action<string>(str =>
Expand Down
51 changes: 51 additions & 0 deletions CLAP/TargetResolver.cs
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace CLAP
{
public class TargetResolver
{
private readonly Dictionary<Type, Func<object>> targetHash = new Dictionary<Type, Func<object>>();

public TargetResolver()
{
}

public TargetResolver(params object[] targets)
{
foreach (var target in targets)
{
var innerTarget = target;
RegisterTargetType(innerTarget.GetType(), () => innerTarget);
}
}

public void RegisterTargetType<T>(Func<T> resolver)
where T: class
{
RegisterTargetType(typeof (T), () => resolver());
}

private void RegisterTargetType(Type targetType, Func<object> resolver)
{
if (targetHash.ContainsKey(targetType))
throw new ArgumentException("The provided type is already registered.");

targetHash.Add(targetType, resolver);
}

internal Type[] RegisteredTypes { get { return targetHash.Keys.ToArray(); } }

internal object Resolve(Type targetType)
{

if (!targetHash.ContainsKey(targetType))
{
throw new ArgumentException("The requested type is not registered.");
}

return targetHash[targetType]();
}
}
}
12 changes: 12 additions & 0 deletions CLAP/VerbAttribute.cs
Expand Up @@ -24,4 +24,16 @@ public sealed class VerbAttribute : Attribute
/// </summary>
public bool IsDefault { get; set; }
}

[Serializable]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class TargetAliasAttribute : Attribute
{
public TargetAliasAttribute(string alias)
{
Alias = alias;
}

public string Alias { get; private set; }
}
}

0 comments on commit e8ab7ef

Please sign in to comment.