// The `Type` must override `ToString` so the builder can actually build the command. public Command WithArgument(string name, Type type, bool trailing = false) { if (HasSubCommands) { throw new InvalidOperationException("A command cannot have both arguments and sub-commands"); } if (OrderedArgumentDefinitions == null) { OrderedArgumentDefinitions = new OrderedDictionary/*<string, ArgumentDefinition>*/ (); } if (_HasTrailingArg) { throw new InvalidOperationException("A command cannot have any arguments after a trailing argument"); } if (trailing) { _HasTrailingArg = true; } //// Construct a `ArgumentDefinition<T>` using the passed `type` as `T`. //Type genericArgumentDefinitionType = typeof(ArgumentDefinition<>).MakeGenericType(type); //object argDef = Activator.CreateInstance(genericArgumentDefinitionType, name, trailing); ArgumentDefinition definition = new ArgumentDefinition(type, name, trailing); OrderedArgumentDefinitions.Add(name, definition); NumArguments++; return(this); }
public BoundCommand BindArgument <T>(string argName, T argValue) { if (!HasArguments) { string message = string.Format("Command {0} does not accept any arguments", Command.Name); throw new InvalidOperationException(message); } if (!Command.AcceptsArgument(argName)) { string message = string.Format("Command {0} does not accept argument {1}", Command.Name, argName); throw new InvalidOperationException(message); } Type argValueType = typeof(T); ArgumentDefinition argDefinition = (ArgumentDefinition)Command.OrderedArgumentDefinitions[argName]; if (!argValueType.IsSameOrSubclass(argDefinition.Type)) { string message = string.Format("Command {0} accepts argument {1} with type {2}, but received type {3}", Command.Name, argName, argDefinition.Type.Name, typeof(T).Name); throw new InvalidOperationException(message); } ArgumentValues.Add(argName, argValue); IsDirty = true; return(this); }
public BoundCommand BindArguments(object[] argValues) { int numProvidedArgs = argValues != null ? argValues.Length : 0; int numRequiredArgs = Command.NumArguments; if (numProvidedArgs != numRequiredArgs) { throw new InvalidOperationException(string.Format( "Command {0} requires {1} arguments, but only {2} were provided", Command.Name, numRequiredArgs, numProvidedArgs)); } int i = 0; foreach (DictionaryEntry entry in Command.OrderedArgumentDefinitions) { ArgumentDefinition acceptedArgDefinition = (ArgumentDefinition)entry.Value; string acceptedArgName = (string)entry.Key; Type acceptedArgType = acceptedArgDefinition.Type; // The call below DOES NOT CORRECTLY validate argument types. BindArgument(acceptedArgName, argValues[i++]); } return(this); }
private static BoundCommand ParseMessage(Message msg, out string errorMessage, Command[] acceptedCommands, BoundCommand lastCommand = null) { string currentToken = msg.NextToken(); if (currentToken == null) { errorMessage = lastCommand == null ? "Invalid command" : null; return(lastCommand); } Command commandMatch = Array.Find(acceptedCommands, delegate(Command cmd) { return(currentToken.Equals(cmd.Name, StringComparison.InvariantCulture)); }); if (commandMatch == null) { errorMessage = string.Format("Unknown command: {0}", currentToken); return(null); } BoundCommand boundCommand = new BoundCommand(commandMatch); if (commandMatch.HasSubCommands) { // Find sub-command. boundCommand = ParseMessage(msg, out errorMessage, commandMatch.SubCommands.ToArray(), boundCommand); } else if (commandMatch.HasArguments) { LinkedList <string> argValues = new LinkedList <string>(); // Read and validate arguments. foreach (DictionaryEntry entry in commandMatch.OrderedArgumentDefinitions) { ArgumentDefinition definedArg = (ArgumentDefinition)entry.Value; string definedArgName = (string)entry.Key; Type definedArgType = definedArg.Type; string passedArgValue = definedArg.Trailing ? msg.RemainingTokens() : msg.NextToken(); try { bool isString = definedArgType.IsSameOrSubclass(typeof(string)); if (isString) { boundCommand.BindArgument(definedArgName, passedArgValue); } else { bool isComplex = definedArgType.IsSameOrSubclass(typeof(ComplexArgument)); if (isComplex) { dynamic deserializedValue = JsonConvert.DeserializeObject(passedArgValue, definedArgType); boundCommand.BindArgument(definedArgName, deserializedValue); } else { dynamic convertedArgValue = Convert.ChangeType(passedArgValue, definedArgType); boundCommand.BindArgument(definedArgName, convertedArgValue); } } } catch (Exception ex) { errorMessage = ex.Message; return(null); } if (definedArg.Trailing) { break; // There shouldn't be any arguments after this one. } } } errorMessage = null; return(boundCommand); }
protected void Build() { Stack <Command> commandStack = new Stack <Command>(); // Stack commands until there is no parent. // For example: 1 RUN PLAN CONTROL for (Command cmd = this.Command; cmd != null; cmd = cmd.Parent) { commandStack.Push(cmd); } int numCommands = commandStack.Count; if (numCommands == 0) { BuiltCommand = null; IsDirty = false; return; } // Then unstack them and add to a list. // For example: CONTROL PLAN RUN 1 List <string> commandList = new List <string>(numCommands); for (int i = 0; i < numCommands; i++) { Command cmd = commandStack.Pop(); commandList.Add(cmd.Name); if (cmd.HasArguments) { foreach (DictionaryEntry acceptedArg in cmd.OrderedArgumentDefinitions) { ArgumentDefinition acceptedArgDefinition = (ArgumentDefinition)acceptedArg.Value; string acceptedArgName = (string)acceptedArg.Key; Type acceptedArgType = acceptedArgDefinition.Type; bool found = ArgumentValues.ContainsKey(acceptedArgName); if (!found) { throw new InvalidOperationException(string.Format( "Command {0} requires argument {1}", cmd.Name, acceptedArgName)); } object passedArgValue = ArgumentValues[acceptedArgName]; Type passedArgType = passedArgValue.GetType(); if (!passedArgType.IsSameOrSubclass(acceptedArgType)) { throw new InvalidOperationException(string.Format( "Command {0} requires argument {1} of type {2}", cmd.Name, acceptedArgName, acceptedArgType.ToString())); } bool isComplex = passedArgType.IsSameOrSubclass(typeof(ComplexArgument)); if (isComplex) { string serializedValue = NotNullJsonSerializer.SerializeObject(passedArgValue); // Needs strong typing? commandList.Add(serializedValue); } else { commandList.Add(passedArgValue.ToString()); } } break; // Ignore remaining commands. } } // Then transform it into a valid command string. BuiltCommand = string.Join(" ", commandList); IsDirty = false; }