The Battle for Wesnoth  1.15.0-dev
map_command_handler.hpp
Go to the documentation of this file.
1 /*
2 Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3 wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12 
13 See the COPYING file for more details.
14 */
15 
16 #pragma once
17 #include "config.hpp"
18 #include "formula/string_utils.hpp"
19 #include "gettext.hpp"
20 
21 #include <boost/algorithm/string.hpp>
22 
23 namespace events {
24 
25 //simple command args parser, separated from command_handler for clarity.
26 //a word begins with a nonspace
27 //n-th arg is n-th word up to the next space
28 //n-th data is n-th word up to the end
29 //cmd is 0-th arg, begins at 0 always.
31 {
32 public:
34  str_(""),
35  args(1, 0),
36  args_end(false)
37  {
38  }
39 
40  explicit cmd_arg_parser(const std::string& str) :
41  str_(str),
42  args(1, 0),
43  args_end(false)
44  {
45  }
46 
47  void parse(const std::string& str)
48  {
49  str_ = str;
50  args.clear();
51  args.push_back(0);
52  args_end = false;
53  }
54 
55  const std::string& get_str() const
56  {
57  return str_;
58  }
59  std::string get_arg(unsigned n) const
60  {
61  advance_to_arg(n);
62  if (n < args.size()) {
63  return std::string(str_, args[n], str_.find(' ', args[n]) - args[n]);
64  }
65  else {
66  return "";
67  }
68  }
69  std::string get_data(unsigned n) const
70  {
71  advance_to_arg(n);
72  if (n < args.size()) {
73  std::string data(str_, args[n]);
74  boost::trim(data);
75  return data;
76  }
77  else {
78  return "";
79  }
80  }
81  std::string get_cmd() const
82  {
83  return get_arg(0);
84  }
85 private:
88  void advance_to_arg(unsigned n) const
89  {
90  while (n < args.size() && !args_end) {
91  std::size_t first_space = str_.find_first_of(' ', args.back());
92  std::size_t next_arg_begin = str_.find_first_not_of(' ', first_space);
93  if (next_arg_begin != std::string::npos) {
94  args.push_back(next_arg_begin);
95  }
96  else {
97  args_end = true;
98  }
99  }
100  }
101  std::string str_;
102  mutable std::vector<std::size_t> args;
103  mutable bool args_end;
104 };
105 
106 
107 //A helper class template with a slim public interface
108 //This represents a map of strings to void()-member-function-of-Worker-pointers
109 //with all the common functionality like general help, command help and aliases
110 //Usage (of a derived class): Derived(specific-arguments) d; d.dispatch(command);
111 //Derived classes should override virtual functions where noted.
112 //The template parameter currently must be the dervived class itself,
113 //i.e. class X : public map_command_handler<X>
114 //To add a new command in a derived class:
115 // * add a new private void function() to the derived class
116 // * add it to the function map in init_map there, setting flags like
117 // "D" for debug only (checking the flag is also done in the derived class)
118 // * remember to add some help and/or usage information in init_map()
119 template <class Worker>
121 {
122 public:
123  typedef void (Worker::*command_handler)();
124  struct command
125  {
126  command_handler handler;
127  std::string help; //long help text
128  std::string usage; //only args info
129  std::string flags;
130  explicit command(command_handler h, const std::string help = "",
131  const std::string& usage = "", const std::string flags = "")
132  : handler(h), help(help), usage(usage), flags(flags)
133  {
134  }
135  bool has_flag(const char f) const
136  {
137  return flags.find(f) != flags.npos;
138  }
139  command& add_flag(const char f)
140  {
141  flags += f;
142  return *this;
143  }
144  };
145  typedef std::map<std::string, command> command_map;
146  typedef std::map<std::string, std::string> command_alias_map;
147 
148  map_command_handler() : cap_("")
149  {
150  }
151 
152  virtual ~map_command_handler() {}
153 
154  bool empty() const
155  {
156  return command_map_.empty();
157  }
158  //actual work function
159  void dispatch(std::string cmd)
160  {
161  if (empty()) {
162  init_map_default();
163  init_map();
164  }
165 
166  // We recursively resolve alias (100 max to avoid infinite recursion)
167  for (int i = 0; i < 100; ++i) {
168  parse_cmd(cmd);
169  std::string actual_cmd = get_actual_cmd(get_cmd());
170  if (actual_cmd == get_cmd())
171  break;
172  std::string data = get_data(1);
173  // translate the command and add space + data if any
174  cmd = actual_cmd + (data.empty() ? "" : " ") + data;
175  }
176 
177  if (get_cmd().empty()) {
178  return;
179  }
180 
181  if (const command* c = get_command(get_cmd())) {
182  if (is_enabled(*c)) {
183  (static_cast<Worker*>(this)->*(c->handler))();
184  }
185  else {
186  print(get_cmd(), _("This command is currently unavailable."));
187  }
188  }
189  else if (help_on_unknown_) {
190  utils::string_map symbols;
191  symbols["command"] = get_cmd();
192  symbols["help_command"] = cmd_prefix_ + "help";
193  print("help", VGETTEXT("Unknown command '$command', try $help_command "
194  "for a list of available commands.", symbols));
195  }
196  }
197 
198  std::vector<std::string> get_commands_list() const
199  {
200  std::vector<std::string> res;
201  for (typename command_map::value_type i : command_map_) {
202  res.push_back(i.first);
203  }
204  return res;
205  }
206  //command error reporting shorthands
207  void command_failed(const std::string& message, bool = false)
208  {
209  print(get_cmd(), _("Error:") + std::string(" ") + message);
210  }
211 protected:
213  {
214  register_command("help", &map_command_handler<Worker>::help,
215  _("Available commands list and command-specific help. "
216  "Use \"help all\" to include currently unavailable commands."),
217  _("do not translate the 'all'^[all|<command>]"));
218  }
219  //derived classes initialize the map overriding this function
220  virtual void init_map() = 0;
221  //overridden in derived classes to actually print the messages somwehere
222  virtual void print(const std::string& title, const std::string& message) = 0;
223  //should be overridden in derived classes if the commands have flags
224  //this should return a string describing what all the flags mean
225  virtual std::string get_flags_description() const
226  {
227  return "";
228  }
229  //this should return a string describing the flags of the given command
230  virtual std::string get_command_flags_description(const command& /*c*/) const
231  {
232  return "";
233  }
234  //this should be overridden if e.g. flags are used to control command
235  //availability. Return false if the command should not be executed by dispatch()
236  virtual bool is_enabled(const command& /*c*/) const
237  {
238  return true;
239  }
240  virtual void parse_cmd(const std::string& cmd_string)
241  {
242  cap_.parse(cmd_string);
243  }
244  //safe n-th argunment getter
245  virtual std::string get_arg(unsigned argn) const
246  {
247  return cap_.get_arg(argn);
248  }
249  //"data" is n-th arg and everything after it
250  virtual std::string get_data(unsigned argn = 1) const
251  {
252  return cap_.get_data(argn);
253  }
254  virtual std::string get_cmd() const
255  {
256  return cap_.get_cmd();
257  }
258  void command_failed_need_arg(int argn)
259  {
260  utils::string_map symbols;
261  symbols["arg_id"] = std::to_string(argn);
262  command_failed(VGETTEXT("Missing argument $arg_id", symbols));
263  }
264  void print_usage()
265  {
266  help_command(get_cmd());
267  }
268  //take aliases into account
269  std::string get_actual_cmd(const std::string& cmd) const
270  {
271  command_alias_map::const_iterator i = command_alias_map_.find(cmd);
272  return i != command_alias_map_.end() ? i->second : cmd;
273  }
274  const command* get_command(const std::string& cmd) const
275  {
276  typename command_map::const_iterator i = command_map_.find(cmd);
277  return i != command_map_.end() ? &i->second : 0;
278  }
279  command* get_command(const std::string& cmd)
280  {
281  typename command_map::iterator i = command_map_.find(cmd);
282  return i != command_map_.end() ? &i->second : 0;
283  }
284  void help()
285  {
286  //print command-specific help if available, otherwise list commands
287  if (help_command(get_arg(1))) {
288  return;
289  }
290  std::stringstream ss;
291  bool show_unavail = show_unavailable_ || get_arg(1) == "all";
292  for (typename command_map::value_type i : command_map_) {
293  if (show_unavail || is_enabled(i.second)) {
294  ss << i.first;
295  //if (!i.second.usage.empty()) {
296  // ss << " " << i.second.usage;
297  //}
298  //uncomment the above to display usage information in command list
299  //which might clutter it somewhat
300  if (!i.second.flags.empty()) {
301  ss << " (" << i.second.flags << ") ";
302  }
303  ss << "; ";
304  }
305  }
306  utils::string_map symbols;
307  symbols["flags_description"] = get_flags_description();
308  symbols["list_of_commands"] = ss.str();
309  symbols["help_command"] = cmd_prefix_ + "help";
310  print(_("help"), VGETTEXT("Available commands $flags_description:\n$list_of_commands", symbols));
311  print(_("help"), VGETTEXT("Type $help_command <command> for more info.", symbols));
312  }
313  //returns true if the command exists.
314  bool help_command(const std::string& acmd)
315  {
316  std::string cmd = get_actual_cmd(acmd);
317  const command* c = get_command(cmd);
318  if (c) {
319  std::stringstream ss;
320  ss << cmd_prefix_ << cmd;
321  if (c->help.empty() && c->usage.empty()) {
322  ss << _(" No help available.");
323  }
324  else {
325  ss << " - " << c->help;
326  }
327  if (!c->usage.empty()) {
328  ss << " " << _("Usage:") << " " << cmd_prefix_ << cmd << " " << c->usage;
329  }
330  ss << get_command_flags_description(*c);
331  const std::vector<std::string> l = get_aliases(cmd);
332  if (!l.empty()) {
333  ss << " (" << _("aliases:") << " " << utils::join(l, " ") << ")";
334  }
335  print(_("help"), ss.str());
336  }
337  return c != 0;
338  }
340 protected:
341  //show a "try help" message on unknown command?
342  static void set_help_on_unknown(bool value)
343  {
344  help_on_unknown_ = value;
345  }
346  //this is display-only
347  static void set_cmd_prefix(std::string value)
348  {
349  cmd_prefix_ = value;
350  }
351  virtual void register_command(const std::string& cmd,
352  command_handler h, const std::string& help = "",
353  const std::string& usage = "", const std::string& flags = "")
354  {
355  command c = command(h, help, usage, flags);
356  std::pair<typename command_map::iterator, bool> r;
357  r = command_map_.insert(typename command_map::value_type(cmd, c));
358  if (!r.second) { //overwrite if exists
359  r.first->second = c;
360  }
361  }
362 
363  virtual void register_alias(const std::string& to_cmd,
364  const std::string& cmd)
365  {
366  command_alias_map_[cmd] = to_cmd;
367  }
368 
369  //get all aliases of a command.
370  static const std::vector<std::string> get_aliases(const std::string& cmd)
371  {
372  std::vector<std::string> aliases;
373  typedef command_alias_map::value_type p;
374  for (p i : command_alias_map_) {
375  if (i.second == cmd) {
376  aliases.push_back(i.first);
377  }
378  }
379  return aliases;
380  }
381 private:
382  static command_map command_map_;
383  static command_alias_map command_alias_map_;
384  static bool help_on_unknown_;
385  static bool show_unavailable_;
386  static std::string cmd_prefix_;
387 };
388 
389 //static member definitions
390 template <class Worker>
392 
393 template <class Worker>
395 
396 template <class Worker>
398 
399 template <class Worker>
401 
402 template <class Worker>
404 
405 }
void command_failed(const std::string &message, bool=false)
std::string get_arg(unsigned n) const
void parse(const std::string &str)
std::map< std::string, t_string > string_map
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
virtual std::string get_data(unsigned argn=1) const
std::vector< std::string > get_commands_list() const
cmd_arg_parser & operator=(const cmd_arg_parser &)
std::string get_data(unsigned n) const
command(command_handler h, const std::string help="", const std::string &usage="", const std::string flags="")
virtual std::string get_cmd() const
#define h
virtual std::string get_arg(unsigned argn) const
Definitions for the interface to Wesnoth Markup Language (WML).
const std::string & get_str() const
cmd_arg_parser(const std::string &str)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
void advance_to_arg(unsigned n) const
virtual void register_alias(const std::string &to_cmd, const std::string &cmd)
virtual bool is_enabled(const command &) const
const command * get_command(const std::string &cmd) const
virtual std::string get_flags_description() const
std::vector< std::size_t > args
virtual std::string get_command_flags_description(const command &) const
std::map< std::string, std::string > command_alias_map
static void print(std::stringstream &sstr, const std::string &queue, const std::string &id)
Definition: fire_event.cpp:29
std::size_t i
Definition: function.cpp:933
std::map< std::string, command > command_map
command * get_command(const std::string &cmd)
mock_party p
static void set_cmd_prefix(std::string value)
static const std::vector< std::string > get_aliases(const std::string &cmd)
#define VGETTEXT(msgid,...)
Handling of system events.
Definition: manager.hpp:41
static void set_help_on_unknown(bool value)
#define f
static command_alias_map command_alias_map_
bool help_command(const std::string &acmd)
virtual void parse_cmd(const std::string &cmd_string)
virtual void register_command(const std::string &cmd, command_handler h, const std::string &help="", const std::string &usage="", const std::string &flags="")
mock_char c
std::string get_cmd() const
static map_location::DIRECTION n
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
std::string get_actual_cmd(const std::string &cmd) const