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