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); }
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); }
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()); }
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); } } } }
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); } }
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, }); }
internal static bool IsActionImplementation(PropertyInfo property) { return(property.Name.EndsWith(Constants.ActionArgConventionSuffix) && property.HasAttr <ArgIgnoreAttribute>() == false && ArgAction.GetActionProperty(property.DeclaringType) != null); }
/// <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); }
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); } } } }