Exemple #1
0
        private PropertyInfo FindSpecifiedAction(Type t, ref string[] args)
        {
            var actionProperty = ArgAction.GetActionProperty(t);

            if (actionProperty == null)
            {
                return(null);
            }

            var specifiedAction = args.Length > 0 ? args[0] : null;

            if (actionProperty.Attr <ArgRequired>().PromptIfMissing&& args.Length == 0)
            {
                actionProperty.Attr <ArgRequired>().ValidateAlways(actionProperty, ref specifiedAction);
                args = new string[] { specifiedAction };
            }

            if (specifiedAction == null)
            {
                return(null);
            }

            var actionArgProperty = (from p in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                     where p.MatchesSpecifiedAction(specifiedAction)
                                     select p).SingleOrDefault();

            if (actionArgProperty == null)
            {
                throw new UnknownActionArgException("Unknown Action: " + specifiedAction);
            }

            return(actionArgProperty);
        }
        internal static CommandLineAction Create(PropertyInfo actionProperty, List <string> knownAliases)
        {
            var ret = PropertyInitializer.CreateInstance <CommandLineAction>();

            ret.ActionMethod = ArgAction.ResolveMethod(actionProperty.DeclaringType, actionProperty);
            ret.Source       = actionProperty;
            ret.Arguments.AddRange(new CommandLineArgumentsDefinition(actionProperty.PropertyType).Arguments);
            ret.IgnoreCase = true;

            if (actionProperty.DeclaringType.HasAttr <ArgIgnoreCase>() && actionProperty.DeclaringType.Attr <ArgIgnoreCase>().IgnoreCase == false)
            {
                ret.IgnoreCase = false;
            }

            if (actionProperty.HasAttr <ArgIgnoreCase>() && actionProperty.Attr <ArgIgnoreCase>().IgnoreCase == false)
            {
                ret.IgnoreCase = false;
            }

            ret.Metadata.AddRange(actionProperty.Attrs <IArgMetadata>().AssertAreAllInstanceOf <ICommandLineActionMetadata>());

            // This line only calls into CommandLineArgument because the code to strip 'Args' off the end of the
            // action property name lives here.  This is a pre 2.0 hack that's only left in place to support apps that
            // use the 'Args' suffix pattern.
            ret.Aliases.AddRange(CommandLineArgument.FindDefaultShortcuts(actionProperty, knownAliases, ret.IgnoreCase));

            return(ret);
        }
Exemple #3
0
        private void PopulateProperties(ArgHook.HookContext context)
        {
            context.Args.GetType().RunBeforePopulateProperties(context);
            foreach (PropertyInfo prop in context.Args.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                var match = from k in context.ParserData.ExplicitParameters.Keys
                            where prop.MatchesSpecifiedArg(k)
                            select k;

                if (match.Count() > 1)
                {
                    throw new DuplicateArgException("Argument specified more than once: " + prop.GetArgumentName());
                }

                if (match.Count() == 1)
                {
                    var key = match.First();
                    context.ArgumentValue = context.ParserData.ExplicitParameters[key];
                    context.ParserData.ExplicitParameters.Remove(key);
                }
                else
                {
                    if (prop.HasAttr <ArgPosition>() && context.ParserData.ImplicitParameters.ContainsKey(prop.Attr <ArgPosition>().Position))
                    {
                        var position = prop.Attr <ArgPosition>().Position;
                        context.ArgumentValue = context.ParserData.ImplicitParameters[position];
                        context.ParserData.ImplicitParameters.Remove(position);
                    }
                    else
                    {
                        context.ArgumentValue = null;
                    }
                }


                context.Property = prop;

                prop.RunBeforePopulateProperty(context);

                bool shouldValidateAndRevive = true;
                if (prop.Attr <ArgIgnoreAttribute>() != null)
                {
                    shouldValidateAndRevive = false;
                }
                if (prop.IsActionArgProperty() && ArgAction.GetActionProperty(context.Args.GetType()) != null)
                {
                    shouldValidateAndRevive = false;
                }

                if (shouldValidateAndRevive)
                {
                    prop.Validate(context);
                    prop.Revive(context.Args, context);
                }

                prop.RunAfterPopulateProperty(context);
            }
            context.Args.GetType().RunAfterPopulateProperties(context);
        }
Exemple #4
0
 /// <summary>
 /// Parses the given arguments using a command line arguments definition. The values will be populated within
 /// the definition.
 /// </summary>
 /// <param name="definition">The definition that defines a set of command line arguments and/or actions.</param>
 /// <param name="args">The command line arguments to parse</param>
 public static ArgAction Parse(CommandLineArgumentsDefinition definition, params string[] args)
 {
     ArgAction ret = Execute(() =>
     {
         return ParseAction(definition, args);
     });
     return ret;
 }
Exemple #5
0
 /// <summary>
 /// Creates a new instance of T and populates it's properties based on the given arguments.
 /// If T correctly implements the heuristics for Actions (or sub commands) then the complex property
 /// that represents the options of a sub command are also populated.
 /// </summary>
 /// <typeparam name="T">The argument scaffold type.</typeparam>
 /// <param name="args">The command line arguments to parse</param>
 /// <returns>The raw result of the parse with metadata about the specified action.</returns>
 public static ArgAction<T> ParseAction<T>(params string[] args)
 {
     ArgAction<T> ret = Execute<ArgAction<T>>(() =>
     {
     Args instance = new Args();
     return instance.ParseInternal<T>(args);
     });
     return ret;
 }
Exemple #6
0
        /// <summary>
        /// Creates a new instance of the given type and populates it's properties based on the given arguments.
        /// If the type correctly implements the heuristics for Actions (or sub commands) then the complex property
        /// that represents the options of a sub command are also populated.
        /// </summary>
        /// <param name="t">The argument scaffold type.</param>
        /// <param name="args">The command line arguments to parse</param>
        /// <returns>The raw result of the parse with metadata about the specified action.</returns>
        public static ArgAction ParseAction(Type t, params string[] args)
        {
            ArgAction ret = Execute <ArgAction>(() =>
            {
                Args instance = new Args();
                return(instance.ParseInternal(t, args));
            });

            return(ret);
        }
Exemple #7
0
        internal static List <PropertyInfo> GetActionArgProperties(this Type t)
        {
            if (ArgAction.GetActionProperty(t) == null)
            {
                return(new List <PropertyInfo>());
            }

            return((from prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                    where prop.IsActionArgProperty() select prop).ToList());
        }
Exemple #8
0
        /// <summary>
        /// Parses the given arguments using a command line arguments definition.
        /// </summary>
        /// <param name="definition">The definition that defines a set of command line arguments and/or actions.</param>
        /// <param name="args">The command line arguments to parse</param>
        /// <returns></returns>
        public static ArgAction ParseAction(CommandLineArgumentsDefinition definition, params string[] args)
        {
            ArgAction ret = Execute(() =>
            {
                Args instance = new Args();
                return(instance.ParseInternal(definition, args));
            });

            return(ret);
        }
Exemple #9
0
 private static ArgAction <T> Strongify <T>(ArgAction weak)
 {
     return(new ArgAction <T>()
     {
         Args = (T)weak.Value,
         ActionArgs = weak.ActionArgs,
         ActionArgsProperty = weak.ActionArgsProperty,
         ActionParameters = weak.ActionParameters,
         ActionArgsMethod = weak.ActionArgsMethod,
         HandledException = weak.HandledException,
         Definition = weak.Definition,
         Context = weak.Context,
         Cancelled = weak.Cancelled,
     });
 }
        private static string GetShortcutInternal(PropertyInfo info, List <string> knownShortcuts)
        {
            var actionProperty = ArgAction.GetActionProperty(info.DeclaringType);

            if (actionProperty != null && actionProperty.Name == info.Name)
            {
                return(null);
            }

            var attr = info.Attr <ArgShortcut>();

            if (attr == null)
            {
                string shortcutVal = "";
                foreach (char c in info.GetArgumentName())
                {
                    shortcutVal += c;
                    if (knownShortcuts.Contains(shortcutVal) == false)
                    {
                        return(shortcutVal);
                    }
                }
                return(shortcutVal);
            }
            else
            {
                if (attr.policy.HasValue && attr.policy.Value == ArgShortcutPolicy.NoShortcut && attr.Shortcut != null)
                {
                    throw new InvalidArgDefinitionException("You cannot specify a shortcut value and an ArgShortcutPolicy of NoShortcut");
                }

                if (attr.Shortcut == null)
                {
                    return(null);
                }
                if (attr.Shortcut.StartsWith("-"))
                {
                    attr.Shortcut = attr.Shortcut.Substring(1);
                }
                else if (attr.Shortcut.StartsWith("/"))
                {
                    attr.Shortcut = attr.Shortcut.Substring(1);
                }
                return(attr.Shortcut);
            }
        }
        internal static void RegisterShortcuts(Type t, List <string> shortcuts = null)
        {
            RegisteredTypes.Add(t);
            bool isNested = shortcuts != null;

            shortcuts = isNested ? shortcuts : new List <string>();
            var actionProp = ArgAction.GetActionProperty(t);

            foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (prop.Attr <ArgIgnoreAttribute>() != null)
                {
                    continue;
                }
                if (prop.IsActionArgProperty() && actionProp != null)
                {
                    continue;
                }

                var shortcut = ArgShortcut.GetShortcutInternal(prop, shortcuts);
                if (shortcut != null)
                {
                    shortcuts.Add(shortcut);
                    if (KnownShortcuts.ContainsKey(prop) == false)
                    {
                        KnownShortcuts.Add(prop, shortcut);
                    }
                    else
                    {
                        KnownShortcuts[prop] = shortcut;
                    }
                }
            }

            if (actionProp != null)
            {
                foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (prop.IsActionArgProperty())
                    {
                        RegisterShortcuts(prop.PropertyType, shortcuts);
                    }
                }
            }
        }
Exemple #12
0
 /// <summary>
 /// Parses the given arguments using a command line arguments definition.  Then, invokes the action
 /// that was specified.  
 /// </summary>
 /// <param name="definition">The definition that defines a set of command line arguments and actions.</param>
 /// <param name="args"></param>
 /// <returns>The raw result of the parse with metadata about the specified action.  The action is executed before returning.</returns>
 public static ArgAction InvokeAction(CommandLineArgumentsDefinition definition, params string[] args)
 {
     ArgAction ret = Execute(() =>
     {
         return REPL.DriveREPL<ArgAction>(definition.Hooks.Where(h => h is TabCompletion).Select(h => h as TabCompletion).SingleOrDefault(), (a) =>
         {
         var result = ParseAction(definition, a);
             if (result.HandledException == null)
             {
                 result.Context.RunBeforeInvoke();
                 result.Invoke();
                 result.Context.RunAfterInvoke();
             }
         return result;
     }
     , args);
     });
     return ret;
 }
Exemple #13
0
 /// <summary>
 /// Creates a new instance of T and populates it's properties based on the given arguments. T must correctly
 /// implement the heuristics for Actions (or sub commands) because this method will not only detect the action
 /// specified on the command line, but will also find and execute the method that implements the action.
 /// </summary>
 /// <typeparam name="T">The argument scaffold type that must properly implement at least one action.</typeparam>
 /// <param name="args">The command line arguments to parse</param>
 /// <returns>The raw result of the parse with metadata about the specified action.  The action is executed before returning.</returns>
 public static ArgAction<T> InvokeAction<T>(params string[] args)
 {
     ArgAction<T> ret = Execute<ArgAction<T>>(() =>
     {
         return REPL.DriveREPL<ArgAction<T>>(typeof(T).Attr<TabCompletion>(), (a) =>
         {
         var result = ParseAction<T>(a);
             if (result.HandledException == null)
             {
                 result.Context.RunBeforeInvoke();
                 result.Invoke();
                 result.Context.RunAfterInvoke();
             }
         return result;
     }
     , args);
     });
     return ret;
 }
Exemple #14
0
        /// <summary>
        /// Parses the args for the given scaffold type and then calls the Main() method defined by the type.
        /// </summary>
        /// <param name="t">The argument scaffold type.</param>
        /// <param name="args">The command line arguments to parse</param>
        /// <returns>The raw result of the parse with metadata about the specified action.</returns>
        public static ArgAction InvokeMain(Type t, params string[] args)
        {
            ArgAction ret = Execute(() =>
            {
                return REPL.DriveREPL<ArgAction>(t.Attr<TabCompletion>(), (a) =>
                {
                var result = ParseAction(t, a);
                    if (result.HandledException == null)
                    {
                        result.Context.RunBeforeInvoke();
                        result.Value.InvokeMainMethod();
                        result.Context.RunAfterInvoke();
                    }
                return result;
            }
            , args);
            });

            return ret;
        }
Exemple #15
0
        private static T CreateEmptyResult <T>(ArgHook.HookContext context, ArgException ex = null, bool cancelled = false)
        {
            ArgAction ret = new ArgAction();

            if (typeof(T) == typeof(ArgAction))
            {
                ret = new ArgAction();
            }
            else if (typeof(T).IsSubclassOf(typeof(ArgAction)))
            {
                ret = Activator.CreateInstance(typeof(T)) as ArgAction;
            }
            else
            {
                return(default(T));
            }

            ret.HandledException = ex;
            ret.Definition       = context.Definition;
            ret.Context          = context;
            ret.Cancelled        = cancelled;
            return((T)((object)ret));
        }
Exemple #16
0
        private void ValidateArgScaffold(Type t, List <string> shortcuts = null, Type parentType = null)
        {
            if (parentType != null)
            {
                if (parentType.HasAttr <ArgIgnoreCase>() ^ t.HasAttr <ArgIgnoreCase>())
                {
                    throw new InvalidArgDefinitionException("If you specify the " + typeof(ArgIgnoreCase).Name + " attribute on your base type then you must also specify it on each action type.");
                }
                else if (parentType.HasAttr <ArgIgnoreCase>() && parentType.Attr <ArgIgnoreCase>().IgnoreCase != t.Attr <ArgIgnoreCase>().IgnoreCase)
                {
                    throw new InvalidArgDefinitionException("If you specify the " + typeof(ArgIgnoreCase).Name + " attribute on your base and acton types then they must be configured to use the same value for IgnoreCase.");
                }
            }

            if (t.Attrs <ArgIgnoreCase>().Count > 1)
            {
                throw new InvalidArgDefinitionException("An attribute that is or derives from " + typeof(ArgIgnoreCase).Name + " was specified on your type more than once");
            }


            var actionProp = ArgAction.GetActionProperty(t);

            shortcuts = shortcuts ?? new List <string>();
            bool ignoreCase = true;

            if (t.HasAttr <ArgIgnoreCase>() && t.Attr <ArgIgnoreCase>().IgnoreCase == false)
            {
                ignoreCase = false;
            }

            foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (prop.Attr <ArgIgnoreAttribute>() != null)
                {
                    continue;
                }
                if (prop.IsActionArgProperty() && actionProp != null)
                {
                    continue;
                }

                if (ArgRevivers.CanRevive(prop.PropertyType) == false)
                {
                    throw new InvalidArgDefinitionException("There is no reviver for type " + prop.PropertyType.Name + ". Offending Property: " + prop.DeclaringType.Name + "." + prop.GetArgumentName());
                }

                var shortcut = ArgShortcut.GetShortcut(prop);

                if (ignoreCase && shortcut != null)
                {
                    shortcut = shortcut.ToLower();
                }

                if (shortcut != null && shortcuts.Contains(shortcut))
                {
                    throw new InvalidArgDefinitionException("Duplicate arg options with shortcut '" + ArgShortcut.GetShortcut(prop) + "'.  Keep in mind that shortcuts are not case sensitive unless you use the [ArgIgnoreCase(false)] attribute.  For example, Without this attribute the shortcuts '-a' and '-A' would cause this exception.");
                }
                else if (shortcut != null)
                {
                    shortcuts.Add(shortcut);
                }
            }

            if (actionProp != null)
            {
                foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (prop.IsActionArgProperty())
                    {
                        ArgAction.ResolveMethod(t, prop);
                        ValidateArgScaffold(prop.PropertyType, shortcuts.ToArray().ToList(), t);
                    }
                }
            }
        }
Exemple #17
0
        /// <summary>
        /// Generates color styled usage documentation for the given argument scaffold type.
        /// </summary>
        /// <typeparam name="T">Your custom argument scaffold type</typeparam>
        /// <param name="exeName">The name of your program or null if you want PowerArgs to automatically detect it.</param>
        /// <param name="options">Specify custom usage options</param>
        /// <returns></returns>
        public static ConsoleString GetStyledUsage <T>(string exeName = null, ArgUsageOptions options = null)
        {
            options = options ?? new ArgUsageOptions();
            if (exeName == null)
            {
                var assembly = Assembly.GetEntryAssembly();
                if (assembly == null)
                {
                    throw new ArgException("PowerArgs could not determine the name of your executable automatically.  This may happen if you run GetUsage<T>() from within unit tests.  Use GetUsageT>(string exeName) in unit tests to avoid this exception.");
                }
                exeName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
            }

            ConsoleString ret = new ConsoleString();

            ret += new ConsoleString("Usage: " + exeName, ConsoleColor.Cyan);

            var actionProperty = ArgAction.GetActionProperty <T>();

            if (actionProperty != null)
            {
                ret.AppendUsingCurrentFormat(" <action> options\n\n");

                foreach (var example in typeof(T).Attrs <ArgExample>())
                {
                    ret += new ConsoleString("EXAMPLE: " + example.Example + "\n" + example.Description + "\n\n", ConsoleColor.DarkGreen);
                }

                var global = GetOptionsUsage(typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public), true, options);

                if (string.IsNullOrEmpty(global.ToString()) == false)
                {
                    ret += new ConsoleString("Global options:\n\n", ConsoleColor.Cyan) + global + "\n";
                }

                ret += "Actions:";

                foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (prop.IsActionArgProperty() == false)
                    {
                        continue;
                    }

                    var actionDescription = prop.HasAttr <ArgDescription>() ? " - " + prop.Attr <ArgDescription>().Description : "";

                    ret += "\n\n" + prop.GetArgumentName().Substring(0, prop.GetArgumentName().Length - Constants.ActionArgConventionSuffix.Length) + actionDescription + "\n\n";

                    foreach (var example in prop.Attrs <ArgExample>())
                    {
                        ret += new ConsoleString() + "   EXAMPLE: " + new ConsoleString(example.Example + "\n", ConsoleColor.Green) +
                               new ConsoleString("   " + example.Description + "\n\n", ConsoleColor.DarkGreen);
                    }

                    ret += GetOptionsUsage(prop.PropertyType.GetProperties(BindingFlags.Instance | BindingFlags.Public), false, options);
                }
            }
            else
            {
                ret.AppendUsingCurrentFormat(" options\n\n");

                ret += GetOptionsUsage(typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public), false, options);

                ret += "\n";

                foreach (var example in typeof(T).Attrs <ArgExample>())
                {
                    ret += new ConsoleString() + "   EXAMPLE: " + new ConsoleString(example.Example + "\n", ConsoleColor.Green) +
                           new ConsoleString("   " + example.Description + "\n\n", ConsoleColor.DarkGreen);
                }
            }

            return(ret);
        }
Exemple #18
0
 /// <summary>
 /// Given an action property, finds the method that implements the action.
 /// </summary>
 /// <param name="actionProperty">The property to resolve</param>
 /// <returns></returns>
 public static MethodInfo ResolveMethod(PropertyInfo actionProperty)
 {
     return(ArgAction.ResolveMethod(typeof(T), actionProperty));
 }
Exemple #19
0
        private ArgAction ParseInternal(CommandLineArgumentsDefinition definition, string[] input)
        {
            // TODO - Validation should be consistently done against the definition, not against the raw type
            if (definition.ArgumentScaffoldType != null)
            {
                ValidateArgScaffold(definition.ArgumentScaffoldType);
            }

            var context = ArgHook.HookContext.Current;

            context.Definition = definition;
            _ambientDefinition = definition;

            definition.Validate(context);

            if (definition.ArgumentScaffoldType != null)
            {
                context.Args = Activator.CreateInstance(definition.ArgumentScaffoldType);
            }
            context.CmdLineArgs = input;

            context.RunBeforeParse();
            context.ParserData = ArgParser.Parse(definition, context.CmdLineArgs);

            var actionToken = context.CmdLineArgs.FirstOrDefault();
            var actionQuery = context.Definition.Actions.Where(a => a.IsMatch(actionToken));

            if (actionQuery.Count() == 1)
            {
                context.SpecifiedAction = actionQuery.First();
            }
            else if (actionQuery.Count() > 1)
            {
                throw new InvalidArgDefinitionException("There are multiple actions that match argument '" + actionToken + "'");
            }

            context.RunBeforePopulateProperties();
            CommandLineArgument.PopulateArguments(context.Definition.Arguments, context);
            context.Definition.SetPropertyValues(context.Args);

            object actionArgs = null;

            object[] actionParameters = null;

            if (context.SpecifiedAction == null && context.Definition.Actions.Count > 0)
            {
                if (context.CmdLineArgs.FirstOrDefault() == null)
                {
                    throw new MissingArgException("No action was specified");
                }
                else
                {
                    throw new UnknownActionArgException(string.Format("Unknown action: '{0}'", context.CmdLineArgs.FirstOrDefault()));
                }
            }
            else if (context.SpecifiedAction != null)
            {
                PropertyInfo actionProp = null;
                if (context.Definition.ArgumentScaffoldType != null)
                {
                    actionProp = ArgAction.GetActionProperty(context.Definition.ArgumentScaffoldType);
                }

                if (actionProp != null)
                {
                    actionProp.SetValue(context.Args, context.SpecifiedAction.Aliases.First(), null);
                }

                context.ParserData.ImplicitParameters.Remove(0);
                CommandLineArgument.PopulateArguments(context.SpecifiedAction.Arguments, context);
            }

            context.RunAfterPopulateProperties();

            if (context.SpecifiedAction != null)
            {
                actionArgs = context.SpecifiedAction.PopulateArguments(context.Args, ref actionParameters);
            }

            if (context.Definition.Metadata.HasMeta <AllowUnexpectedArgs>() == false)
            {
                if (context.ParserData.ImplicitParameters.Count > 0)
                {
                    throw new UnexpectedArgException("Unexpected unnamed argument: " + context.ParserData.ImplicitParameters.First().Value);
                }

                if (context.ParserData.ExplicitParameters.Count > 0)
                {
                    throw new UnexpectedArgException("Unexpected named argument: " + context.ParserData.ExplicitParameters.First().Key);
                }
            }
            else
            {
                definition.UnexpectedExplicitArguments = context.ParserData.ExplicitParameters;
                definition.UnexpectedImplicitArguments = context.ParserData.ImplicitParameters;
            }


            if (definition.ArgumentScaffoldType != null)
            {
                if (AmbientArgs.ContainsKey(definition.ArgumentScaffoldType))
                {
                    AmbientArgs[definition.ArgumentScaffoldType] = context.Args;
                }
                else
                {
                    AmbientArgs.Add(definition.ArgumentScaffoldType, context.Args);
                }
            }

            PropertyInfo actionArgsPropertyInfo = null;

            if (context.SpecifiedAction != null)
            {
                if (context.SpecifiedAction.Source is PropertyInfo)
                {
                    actionArgsPropertyInfo = context.SpecifiedAction.Source as PropertyInfo;
                }
                else if (context.SpecifiedAction.Source is MethodInfo)
                {
                    actionArgsPropertyInfo = new ArgActionMethodVirtualProperty(context.SpecifiedAction.Source as MethodInfo);
                }
            }

            return(new ArgAction()
            {
                Value = context.Args,
                ActionArgs = actionArgs,
                ActionParameters = actionParameters,
                ActionArgsProperty = actionArgsPropertyInfo,
                ActionArgsMethod = context.SpecifiedAction != null ? context.SpecifiedAction.ActionMethod : null,
                Definition = context.Definition,
                Context = context,
            });
        }
Exemple #20
0
        private void ValidateArgScaffold(Type t, List <string> shortcuts = null, Type parentType = null)
        {
            /*
             * Today, this validates the following:
             *
             *     - IgnoreCase can't be different on parent and child scaffolds.
             *     - No collisions on shortcut values for properties and enum values
             *     - No reviver for type
             *
             */

            if (parentType != null)
            {
                if (parentType.HasAttr <ArgIgnoreCase>() ^ t.HasAttr <ArgIgnoreCase>())
                {
                    throw new InvalidArgDefinitionException("If you specify the " + typeof(ArgIgnoreCase).Name + " attribute on your base type then you must also specify it on each action type.");
                }
                else if (parentType.HasAttr <ArgIgnoreCase>() && parentType.Attr <ArgIgnoreCase>().IgnoreCase != t.Attr <ArgIgnoreCase>().IgnoreCase)
                {
                    throw new InvalidArgDefinitionException("If you specify the " + typeof(ArgIgnoreCase).Name + " attribute on your base and acton types then they must be configured to use the same value for IgnoreCase.");
                }
            }

            if (t.Attrs <ArgIgnoreCase>().Count > 1)
            {
                throw new InvalidArgDefinitionException("An attribute that is or derives from " + typeof(ArgIgnoreCase).Name + " was specified on your type more than once");
            }


            var actionProp = ArgAction.GetActionProperty(t);

            shortcuts = shortcuts ?? new List <string>();
            bool ignoreCase = true;

            if (t.HasAttr <ArgIgnoreCase>() && t.Attr <ArgIgnoreCase>().IgnoreCase == false)
            {
                ignoreCase = false;
            }

            foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (prop.Attr <ArgIgnoreAttribute>() != null)
                {
                    continue;
                }
                if (CommandLineAction.IsActionImplementation(prop))
                {
                    continue;
                }

                // This check happens in the CommandLineArgumentsDefinition validation method and should not be repeated here.  Leaving the code commented while this bakes, but this code
                // should be removable in the future.
                //if (ArgRevivers.CanRevive(prop.PropertyType) == false)
                //{
                //    throw new InvalidArgDefinitionException("There is no reviver for type " + prop.PropertyType.Name + ". Offending Property: " + prop.DeclaringType.Name + "." + prop.Name);
                //}

                if (prop.PropertyType.IsEnum)
                {
                    prop.PropertyType.ValidateNoDuplicateEnumShortcuts(ignoreCase);
                }

                var attrs = prop.Attrs <ArgShortcut>();
                var noShortcutsAllowed   = attrs.Where(a => a.Policy == ArgShortcutPolicy.NoShortcut).Count() != 0;
                var shortcutsOnly        = attrs.Where(a => a.Policy == ArgShortcutPolicy.ShortcutsOnly).Count() != 0;
                var actualShortcutValues = attrs.Where(a => a.Policy == ArgShortcutPolicy.Default && a.Shortcut != null).Count() != 0;

                if (noShortcutsAllowed && shortcutsOnly)
                {
                    throw new InvalidArgDefinitionException("You cannot specify a policy of NoShortcut and another policy of ShortcutsOnly.");
                }
                if (noShortcutsAllowed && actualShortcutValues)
                {
                    throw new InvalidArgDefinitionException("You cannot specify a policy of NoShortcut and then also specify shortcut values via another attribute.");
                }
                if (shortcutsOnly && actualShortcutValues == false)
                {
                    throw new InvalidArgDefinitionException("You specified a policy of ShortcutsOnly, but did not specify any shortcuts by adding another ArgShortcut attrivute.");
                }
            }

            if (actionProp != null)
            {
                foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (CommandLineAction.IsActionImplementation(prop))
                    {
                        ArgAction.ResolveMethod(t, prop);
                        ValidateArgScaffold(prop.PropertyType, shortcuts.ToArray().ToList(), t);
                    }
                }
            }

            foreach (var actionMethod in t.GetActionMethods())
            {
                if (actionMethod.GetParameters().Length == 0)
                {
                    continue;
                }

                ValidateArgScaffold(actionMethod.GetParameters()[0].ParameterType, shortcuts.ToArray().ToList(), t);
            }
        }
 internal static bool IsActionImplementation(PropertyInfo property)
 {
     return(property.Name.EndsWith(Constants.ActionArgConventionSuffix) &&
            property.HasAttr <ArgIgnoreAttribute>() == false &&
            ArgAction.GetActionProperty(property.DeclaringType) != null);
 }