Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/command/CommandExecutor.cc @ 12273

Last change on this file since 12273 was 11099, checked in by muemart, 9 years ago

Fix loads of doxygen warnings and other documentation issues

  • Property svn:eol-style set to native
File size: 12.5 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30    @file
31    @brief Implementation of CommandExecutor
32*/
33
34#include "CommandExecutor.h"
35
36#include "ConsoleCommandIncludes.h"
37#include "TclBind.h"
38#include "Shell.h"
39
40namespace orxonox
41{
42    static const std::string __CC_CommandExecutor_name = "CommandExecutor";
43    static const std::string __CC_autocomplete_name = "autocomplete";
44
45    SetConsoleCommand(__CC_CommandExecutor_name, __CC_autocomplete_name, &CommandExecutor::_autocomplete)
46        .hide()
47        .argumentCompleter(0, autocompletion::groupsandcommands())
48        .argumentCompleter(1, autocompletion::subcommands());
49
50    SetConsoleCommand("unhide", &CommandExecutor::unhide)
51        .argumentCompleter(0, autocompletion::hiddencommand());
52
53    SetConsoleCommand("alias", &CommandExecutor::alias)
54        .argumentCompleter(1, autocompletion::command());
55
56    /**
57        @brief Returns a reference to the only instance of CommandExecutor.
58    */
59    /* static */ CommandExecutor& CommandExecutor::getInstance()
60    {
61        static CommandExecutor instance;
62        return instance;
63    }
64
65    /**
66        @brief Executes a command.
67        @param command A string containing the command
68        @param useTcl If true, the command is passed to tcl (see TclBind)
69        @param printErrors If true, print an error when command failed
70        @return Returns the error-code (see @ref CommandExecutorErrorCodes "error codes")
71    */
72    /* static */ int CommandExecutor::execute(const std::string& command, bool useTcl, bool printErrors)
73    {
74        int error;
75        CommandExecutor::queryMT(command, &error, useTcl);
76        if (error && printErrors)
77            orxout(user_error) << "Can't execute \"" << command << "\", " << CommandExecutor::getErrorDescription(error) << ". (execute)" << endl;
78        return error;
79    }
80
81    /**
82        @brief Executes a command and returns its return-value.
83        @param command A string containing the command
84        @param error A pointer to a value (or nullptr) where the error-code should be written to (see @ref CommandExecutorErrorCodes "error codes")
85        @param useTcl If true, the command is passed to tcl (see TclBind)
86        @return Returns the return-value of the command (if any - MultiType::Null otherwise)
87    */
88    /* static */ MultiType CommandExecutor::queryMT(const std::string& command, int* error, bool useTcl)
89    {
90        MultiType result;
91        int error_internal;
92
93        if (useTcl)
94        {
95            // pass the command to tcl
96            result = TclBind::eval(command, &error_internal);
97        }
98        else
99        {
100            CommandEvaluation evaluation;
101
102            // try to get the command evaluation from the cache
103            if (!CommandExecutor::getInstance().getCached(command, evaluation))
104            {
105                // it wasn't in the cache - evaluate the command
106                evaluation = CommandExecutor::evaluate(command);
107
108                // evaluate its arguments
109                evaluation.evaluateArguments();
110
111                // write the evaluation to the cache
112                CommandExecutor::getInstance().cache(command, evaluation);
113            }
114
115            // query the command and return its return-value
116            result = evaluation.query(&error_internal);
117        }
118
119        if (error)
120            *error = error_internal;
121        else if (error_internal)
122            orxout(user_error) << "Can't execute \"" << command << "\", " << CommandExecutor::getErrorDescription(error_internal) << ". (query)" << endl;
123
124        return result;
125    }
126
127    /**
128        @brief Executes a command and returns its return-value as string.
129        @param command A string containing the command
130        @param error A pointer to a value (or nullptr) where the error-code should be written to (see @ref CommandExecutorErrorCodes "error codes")
131        @param useTcl If true, the command is passed to tcl (see TclBind)
132        @return Returns the return-value of the command converted to a string (or "" if there's no return value)
133    */
134    /* static */ std::string CommandExecutor::query(const std::string& command, int* error, bool useTcl)
135    {
136        return CommandExecutor::queryMT(command, error, useTcl).get<std::string>();
137    }
138
139    /**
140        @brief Evaluates the given command.
141        @param command A string containing the command
142        @return Returns an instance of CommandEvaluation, which contains the evaluated ConsoleCommand, if the command is valid.
143
144        Evaluates the given command string and returns an instance of CommandEvaluation.
145        If the command is valid, this contains the evaluated ConsoleCommand. Otherwise it
146        can still be used to print hints or complete the command.
147
148        @note Tcl commands can not be evaluated. You have to pass them to execute() or to
149        TclBind directly.
150    */
151    /* static */ CommandEvaluation CommandExecutor::evaluate(const std::string& command)
152    {
153        // initialize the command evaluation
154        CommandEvaluation evaluation;
155        evaluation.initialize(command);
156
157        // assign the fallback-command to get hints about the possible commands and groups
158        evaluation.hintCommand_ = ConsoleCommandManager::getInstance().getCommand(__CC_CommandExecutor_name, __CC_autocomplete_name);
159
160        // check if there's at least one argument
161        if (evaluation.getNumberOfArguments() >= 1)
162        {
163            // try to get a command from the first token
164            evaluation.execCommand_ = ConsoleCommandManager::getInstance().getCommandLC(evaluation.getToken(0));
165            if (evaluation.execCommand_)
166                evaluation.execArgumentsOffset_ = 1;
167            else if (evaluation.getNumberOfArguments() >= 2)
168            {
169                // try to get a command from the first two tokens
170                evaluation.execCommand_ = ConsoleCommandManager::getInstance().getCommandLC(evaluation.getToken(0), evaluation.getToken(1));
171                if (evaluation.execCommand_)
172                    evaluation.execArgumentsOffset_ = 2;
173            }
174        }
175
176        // if a valid command was found and the user is already entering arguments, overwrite hintCommand_ with execCommand_
177        if (evaluation.execCommand_ && evaluation.getNumberOfArguments() > evaluation.execArgumentsOffset_)
178        {
179            evaluation.hintCommand_ = evaluation.execCommand_;
180            evaluation.hintArgumentsOffset_ = evaluation.execArgumentsOffset_;
181        }
182
183        return evaluation;
184    }
185
186    /**
187        @brief Returns a description of the error code.
188        @param error The error code
189    */
190    /* static */ std::string CommandExecutor::getErrorDescription(int error)
191    {
192        switch (error)
193        {
194            case CommandExecutor::Inexistent:  return "command doesn't exist";
195            case CommandExecutor::Incomplete:  return "not enough arguments given";
196            case CommandExecutor::Deactivated: return "command is not active";
197            case CommandExecutor::Denied:      return "access denied";
198            case CommandExecutor::Error:       return "an error occurred";
199            default: return "";
200        }
201    }
202
203    /**
204        @brief Gets an evaluated command from the cache.
205        @param command The command that should be looked up in the cache
206        @param evaluation Reference to a CommandEvaluation that will be used to return the cached evaluation.
207        @return Returns true if the command was found in the cache
208    */
209    bool CommandExecutor::getCached(const std::string& command, CommandEvaluation& evaluation)
210    {
211        if (Shell::getCacheSize() == 0)
212            return false;
213
214        // check if the command is in the cache
215        std::map<std::string, CacheEntry>::iterator it = this->cache_.find(command);
216        if (it != this->cache_.end())
217        {
218            // update ordered list of cached commands (move it to the front)
219            this->cachelist_.erase(it->second.iterator_);
220            this->cachelist_.push_front(command);
221            it->second.iterator_ = this->cachelist_.begin();
222
223            // assign the cached evaluation
224            evaluation = it->second.evaluation_;
225            return true;
226        }
227        return false;
228    }
229
230    /**
231        @brief Writes a command evaluation for a given command to the cache.
232    */
233    void CommandExecutor::cache(const std::string& command, const CommandEvaluation& evaluation)
234    {
235        if (Shell::getCacheSize() == 0)
236            return;
237
238        // push command to the front of the ordered list
239        this->cachelist_.push_front(command);
240
241        // create a cache entry and store it in the cache
242        CacheEntry entry;
243        entry.evaluation_ = evaluation;
244        entry.iterator_ = this->cachelist_.begin();
245        this->cache_[command] = entry;
246
247        // remove the last command in the ordered list from the cache if it exceeds the maximum size of the cache
248        if (this->cachelist_.size() > Shell::getCacheSize())
249        {
250            this->cache_.erase(this->cachelist_.back());
251            this->cachelist_.pop_back();
252        }
253    }
254
255    /**
256        @brief This function is used as console command which executes commands that are usually hidden.
257
258        The argument completion function of this console commands is used to find
259        and enter hidden commands and their arguments.
260    */
261    /* static */ MultiType CommandExecutor::unhide(const std::string& command)
262    {
263        return CommandExecutor::queryMT(command);
264    }
265
266    /**
267        @brief This function is used as console command which saves a command and optionally also it's arguments as a new console command with a new name.
268        @param alias The name of the new command alias
269        @param command The existing command and (optionally) its arguments
270    */
271    /* static */ void CommandExecutor::alias(const std::string& alias, const std::string& command)
272    {
273        // first check if the given command is valid, else print an error
274        CommandEvaluation evaluation = CommandExecutor::evaluate(command);
275        if (evaluation.isValid())
276        {
277            // it is valid - copy the executor of this command
278            ExecutorPtr executor = std::make_shared<Executor>(*evaluation.getConsoleCommand()->getExecutor().get());
279
280            // evaluate the arguments and if this returns no error, store them as default values
281            if (!evaluation.evaluateArguments())
282            {
283                for (size_t i = 0; i < MAX_FUNCTOR_ARGUMENTS; ++i)
284                    executor->setDefaultValue(i, evaluation.getEvaluatedArgument(i));
285            }
286
287            // split the alias in tokens
288            SubString tokens(alias, " ");
289
290            // check if the alias already exists - print an error and return if it does
291            if ((tokens.size() == 1 && ConsoleCommandManager::getInstance().getCommand(tokens[0])) || (tokens.size() == 2 && ConsoleCommandManager::getInstance().getCommand(tokens[0], tokens[1])))
292            {
293                orxout(user_error) << "A command with name \"" << alias << "\" already exists." << endl;
294                return;
295            }
296
297            // create a new console command with the given alias as its name
298            if (tokens.size() == 1)
299                ConsoleCommandManager::getInstance().registerCommand(new ConsoleCommand(tokens[0], executor));
300            else if (tokens.size() == 2)
301                ConsoleCommandManager::getInstance().registerCommand(new ConsoleCommand(tokens[0], tokens[1], executor));
302            else
303                orxout(user_error) << "\"" << alias << "\" is not a valid alias name (must have one or two words)." << endl;
304        }
305        else
306            orxout(user_error) << "\"" << command << "\" is not a valid command (did you mean \"" << evaluation.getCommandSuggestion() << "\"?)." << endl;
307    }
308}
Note: See TracBrowser for help on using the repository browser.