private bool ParseArgument(string token, IDevCommand command, List <Argument> args, ref int argsFound) { if (argsFound < args.Count) { args[argsFound].IsPresent = true; Type t = args[argsFound].ArgType; object value = null; if (token.StringToValueOfType(t, ref value, true) == false) { return(false); } args[argsFound].Value = value; argsFound++; } else { Debug.Log("Error: Too many arguments (maximum " + args.Count + "), when \"" + token + "\" encountered\n"); return(false); } return(true); }
public void UnregisterDevCommand(IDevCommand devCmd) { var lowerCaseKey = devCmd.Name.ToLower( ); if (!DevCommands.ContainsKey(lowerCaseKey)) { throw new Exception("UnregisterDevCommand: Dev command " + lowerCaseKey + " not registered"); } DevCommands.Remove(lowerCaseKey); }
public void RegisterDevCommand(IDevCommand devCmd) { if (!GenerateArgSpec(devCmd)) { throw new Exception("Problem in dev command argument spec generation"); } if (!ValidateArgumentSpec(devCmd)) { throw new Exception("Problem in dev command argument spec validation"); } var lowerCaseKey = devCmd.Name.ToLower( ); if (DevCommands.ContainsKey(lowerCaseKey)) { throw new Exception("RegisterDevCommand: Dev command " + lowerCaseKey + " already registered"); } DevCommands[lowerCaseKey] = devCmd; }
private bool ValidateArgumentSpec(IDevCommand command) { bool optionalArgFound = false; foreach (var arg in command.Arguments) { if (!arg.IsRequired) { optionalArgFound = true; } else if (optionalArgFound) { Debug.Log("Error in argument specification for command \"" + command.Name + "\": Any optional arguments must come AFTER non-optional arguments\n"); return(false); } } foreach (var option in command.Options) { optionalArgFound = false; foreach (var arg in option.Arguments) { if (!arg.IsRequired) { optionalArgFound = true; } else if (optionalArgFound) { Debug.Log("Error in argument specification for command \"" + command.Name + "\": Any optional arguments must come AFTER non-optional arguments\n"); return(false); } } } return(true); }
private bool ParseDevCommandArguments(IDevCommand command, string rawArgsString) { foreach (var arg in command.Arguments) { arg.Value = null; arg.IsPresent = false; } foreach (var opt in command.Options) { opt.IsPresent = false; foreach (var optionArg in opt.Arguments) { optionArg.Value = null; optionArg.IsPresent = false; } } // Break the string into individual pieces (tokens), keeping quoted strings as one piece, and convert those preceded by "-" to lower case // Preserve spaces within quotes, when they appear any place within a space-delimited value, so: ABC"Max Value"DEF ...becomes... ABCMax ValueDEF var tokens = new List <Token>( ); var remainingString = rawArgsString; while (remainingString.Length > 0) { remainingString = remainingString.TrimStart( ); if (remainingString.Length > 0) { // log ABC"This is good" -count 5 // String now starts with a non-space; Look for first quote (if any), and first space (if any) var quoteIndex = remainingString.IndexOf('"'); var spaceIndex = remainingString.IndexOf(' '); if ((quoteIndex >= 0) && (spaceIndex < 0 || quoteIndex < spaceIndex)) { var endQuoteIndex = remainingString.IndexOf('"', quoteIndex + 1); if (endQuoteIndex < 0) { Debug.Log("Error: Mismatched quotes\n"); return(false); } var sansQuotes = remainingString.Substring(0, quoteIndex); // Any characters before the first quote mark sansQuotes += remainingString.Substring(quoteIndex + 1, (endQuoteIndex - quoteIndex) - 1); // Characters between the quotes (removing the quotes) var endIndex = remainingString.IndexOf(' ', endQuoteIndex); if (endIndex < 0) { endIndex = remainingString.Length; } sansQuotes += remainingString.Substring(endQuoteIndex + 1, (endIndex - endQuoteIndex) - 1); // Any characters after the second quote mark, up to a space or end of line tokens.Add(new Token(sansQuotes, true)); if (endIndex >= (remainingString.Length - 1)) { remainingString = string.Empty; } else { remainingString = remainingString.Substring(endIndex + 1, (remainingString.Length - endIndex) - 1); } continue; } if (spaceIndex < 0) { spaceIndex = remainingString.Length; } var nextWord = remainingString.Substring(0, spaceIndex); if (nextWord.StartsWith("-")) { nextWord = nextWord.ToLower( ); } tokens.Add(new Token(nextWord, false)); remainingString = remainingString.Substring(spaceIndex, (remainingString.Length - spaceIndex)); } } // Main arguments must be in spec order. Options can be in any order...but options must appear after all the main arguments. // Also, options can have their own arguments, which within themselves must appear in spec order. var mainArgsFound = 0; var optArgsFound = 0; Option curOption = null; string curOptionName = null; MutuallyExclusiveGroupsFound.Clear(); foreach (var tokenItem in tokens) { var token = tokenItem.token; if (token.StartsWith("-") && !tokenItem.wasQuoted) // Is it an option or an argument? { // Option: var optionText = token.Substring(1); // Strip hypen var matchList = command.Options.Where(e => e.Name.ToLower().Equals(optionText)).ToList(); if (matchList.Count > 0) { if (!AreAllRequiredOptionArgumentsPresent(curOption, curOptionName, optArgsFound)) // Error-check for the PREVIOUS option, if any { return(false); } curOption = matchList[0]; curOption.IsPresent = true; curOptionName = token; optArgsFound = 0; if (!String.IsNullOrEmpty(curOption.MutualGroupName)) { if (MutuallyExclusiveGroupsFound.ContainsKey(curOption.MutualGroupName)) { Debug.Log("Error: Option \"" + optionText + "\" cannot be used in the same command execution as option \"" + MutuallyExclusiveGroupsFound[curOption.MutualGroupName].Name + "\"; they are mutually exclusive"); return(false); } MutuallyExclusiveGroupsFound[curOption.MutualGroupName] = curOption; } } else { Debug.Log("Error: Option \"" + optionText + "\" not recognized\n"); return(false); } } else { if (curOption == null) { // Main argument: if (!ParseArgument(token, command, command.Arguments, ref mainArgsFound)) { return(false); } } else { // Option argument: if (!ParseArgument(token, command, curOption.Arguments, ref optArgsFound)) { return(false); } } } } if (!AreAllRequiredOptionArgumentsPresent(curOption, curOptionName, optArgsFound)) { return(false); } if (mainArgsFound < command.Arguments.Count) { if (command.Arguments[mainArgsFound].IsRequired) { Debug.Log("Error: Missing required main argument: " + ArgumentTextDescription(command.Arguments[mainArgsFound]) + "\n"); return(false); } } return(true); }
private bool GenerateArgSpec(IDevCommand command) { command.HelpTextFull = command.Name + " - " + command.HelpTextBrief + "\n"; command.Arguments.Clear(); command.Options.Clear(); // NOTE: Unfortunately, Microsoft says the order of members returned by "GetMembers" is indeterminate: https://msdn.microsoft.com/en-us/library/424c79hc(v=vs.110).aspx // Currently I depend on the order they appear in the source file for 'order of arguments', at least for making optional arguments appear after IsRequired arguments. // potential solution: Sort by 'IsRequired'. var argMembers = command.GetType() .GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(e => Attribute.IsDefined(e, typeof(DevCommandArgumentAttribute))); foreach (var m in argMembers) { if (m is PropertyInfo) { var propertyInfo = m as PropertyInfo; var argInstance = propertyInfo.GetValue(command, null) as Argument; command.Arguments.Add(argInstance); command.HelpTextFull += " " + (argInstance.IsRequired ? "" : "[") + ArgumentTextDescription(argInstance); command.HelpTextFull += (argInstance.IsRequired ? "" : "]") + "\n"; if (argInstance.HelpText.Length > 0) { command.HelpTextFull += " " + argInstance.HelpText + "\n"; } } else { Debug.Log("Member found, not Property but " + m.MemberType + ": " + m.Name); return(false); } } var optionMembers = command.GetType() .GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(e => Attribute.IsDefined(e, typeof(DevCommandOptionAttribute))); foreach (var m in optionMembers) { if (m is PropertyInfo) { var propertyInfo = m as PropertyInfo; var optionInstance = propertyInfo.GetValue(command, null) as Option; command.Options.Add(optionInstance); command.HelpTextFull += " -" + optionInstance.Name; foreach (var optionArg in optionInstance.Arguments) { command.HelpTextFull += " " + (optionArg.IsRequired ? "" : "[") + ArgumentTextDescription(optionArg); command.HelpTextFull += (optionArg.IsRequired ? "" : "]"); } command.HelpTextFull += "\n"; if (optionInstance.HelpText.Length > 0) { command.HelpTextFull += " " + optionInstance.HelpText + "\n"; } foreach (var optionArg in optionInstance.Arguments) { if (optionArg.HelpText.Length > 0) { command.HelpTextFull += " Note on " + optionArg.Name + ": " + optionArg.HelpText + "\n"; } } } else { Debug.Log("Member found, not Property but " + m.MemberType + ": " + m.Name); return(false); } } return(true); }