using System; using System.Collections.Generic; using System.Text; using OpenSim.Region.Environment.Interfaces; namespace OpenSim.Region.Environment.Modules.ModuleFramework { /// /// A single function call encapsulated in a class which enforces arguments when passing around as Object[]'s. /// Used for console commands and script API generation /// public class Command : ICommand { private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private Action m_command; private string m_name; private string m_help; private List m_args = new List(); public Command(string name, Action command, string help) { m_name = name; m_command = command; m_help = help; } public void AddArgument(string name, string helptext, string type) { m_args.Add(new CommandArgument(name, helptext, type)); } public string Name { get { return m_name; } } public string Help { get { return m_help; } } public Dictionary Arguments { get { Dictionary tmp = new Dictionary(); foreach (CommandArgument arg in m_args) { tmp.Add(arg.Name, arg.ArgumentType); } return tmp; } } public void ShowConsoleHelp() { m_log.Info("== " + Name + " =="); m_log.Info(m_help); m_log.Info("= Parameters ="); foreach (CommandArgument arg in m_args) { m_log.Info("* " + arg.Name + " (" + arg.ArgumentType + ")"); m_log.Info("\t" + arg.HelpText); } } public void Run(Object[] args) { Object[] cleanArgs = new Object[m_args.Count]; if (args.Length < cleanArgs.Length) { m_log.Error("Missing " + (cleanArgs.Length - args.Length) + " argument(s)"); ShowConsoleHelp(); return; } if (args.Length > cleanArgs.Length) { m_log.Error("Too many arguments for this command. Type ' help' for help."); return; } int i = 0; foreach (Object arg in args) { if (string.IsNullOrEmpty(arg.ToString())) { m_log.Error("Empty arguments are not allowed"); return; } try { switch (m_args[i].ArgumentType) { case "String": m_args[i].ArgumentValue = arg.ToString(); break; case "Integer": m_args[i].ArgumentValue = Int32.Parse(arg.ToString()); break; case "Double": m_args[i].ArgumentValue = Double.Parse(arg.ToString()); break; case "Boolean": m_args[i].ArgumentValue = Boolean.Parse(arg.ToString()); break; default: m_log.Error("Unknown desired type for argument " + m_args[i].Name + " on command " + m_name); break; } } catch (FormatException) { m_log.Error("Argument number " + (i + 1) + " (" + m_args[i].Name + ") must be a valid " + m_args[i].ArgumentType.ToLower() + "."); } cleanArgs[i] = m_args[i].ArgumentValue; i++; } m_command.Invoke(cleanArgs); } } /// /// A single command argument, contains name, type and at runtime, value. /// public class CommandArgument { private string m_name; private string m_help; private string m_type; private Object m_val; public CommandArgument(string name, string help, string type) { m_name = name; m_help = help; m_type = type; } public string Name { get { return m_name; } } public string HelpText { get { return m_help; } } public string ArgumentType { get { return m_type; } } public Object ArgumentValue { get { return m_val; } set { m_val = value; } } } /// /// A class to enable modules to register console and script commands, which enforces typing and valid input. /// public class Commander : ICommander { private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private Dictionary m_commands = new Dictionary(); private string m_name; public Commander(string name) { m_name = name; } public void RegisterCommand(string commandName, ICommand command) { m_commands[commandName] = command; } void ShowConsoleHelp() { m_log.Info("===" + m_name + "==="); foreach (ICommand com in m_commands.Values) { m_log.Info("* " + com.Name + " - " + com.Help); } } string EscapeRuntimeAPICommand(string command) { command = command.Replace('-', '_'); StringBuilder tmp = new StringBuilder(command); tmp[0] = tmp[0].ToString().ToUpper().ToCharArray()[0]; return tmp.ToString(); } /// /// Generates a runtime C# class which can be compiled and inserted via reflection to enable modules to register new script commands /// /// Returns C# source code to create a binding public string GenerateRuntimeAPI() { string classSrc = "\n\tpublic class " + m_name + " {\n"; foreach (ICommand com in m_commands.Values) { classSrc += "\tpublic void " + EscapeRuntimeAPICommand(com.Name) + "( "; foreach (KeyValuePair arg in com.Arguments) { classSrc += arg.Value + " " + Framework.Util.Md5Hash(arg.Key) + ","; } classSrc = classSrc.Remove(classSrc.Length - 1); // Delete the last comma classSrc += " )\n\t{\n"; classSrc += "\t\tObject[] args = new Object[" + com.Arguments.Count.ToString() + "];\n"; int i = 0; foreach (KeyValuePair arg in com.Arguments) { classSrc += "\t\targs[" + i.ToString() + "] = " + Framework.Util.Md5Hash(arg.Key) + " " + ";\n"; i++; } classSrc += "\t\tGetCommander(\"" + m_name + "\").Run(\"" + com.Name + "\", args);\n"; classSrc += "\t}\n"; } classSrc += "}\n"; return classSrc; } /// /// Runs a specified function with attached arguments /// *** DO NOT CALL DIRECTLY. *** /// Call ProcessConsoleCommand instead if handling human input. /// /// The function name to call /// The function parameters public void Run(string function, object[] args) { m_commands[function].Run(args); } public void ProcessConsoleCommand(string function, string[] args) { if (m_commands.ContainsKey(function)) { if (args.Length > 0 && args[0] == "help") { m_commands[function].ShowConsoleHelp(); } else { m_commands[function].Run(args); } } else { if (function != "help") m_log.Error("Invalid command - No such command exists"); if (function == "api") m_log.Info(GenerateRuntimeAPI()); ShowConsoleHelp(); } } } }