Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8729 was 7401, checked in by landauf, 14 years ago

merged doc branch back to trunk

  • Property svn:eol-style set to native
File size: 11.0 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 "ConsoleCommand.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        @return Returns the error-code (see @ref CommandExecutorErrorCodes "error codes")
70    */
71    /* static */ int CommandExecutor::execute(const std::string& command, bool useTcl)
72    {
73        int error;
74        CommandExecutor::queryMT(command, &error, useTcl);
75        return error;
76    }
77
78    /**
79        @brief Executes a command and returns its return-value.
80        @param command A string containing the command
81        @param error A pointer to a value (or NULL) where the error-code should be written to (see @ref CommandExecutorErrorCodes "error codes")
82        @param useTcl If true, the command is passed to tcl (see TclBind)
83        @return Returns the return-value of the command (if any - MT_Type::Null otherwise)
84    */
85    /* static */ MultiType CommandExecutor::queryMT(const std::string& command, int* error, bool useTcl)
86    {
87        if (useTcl)
88        {
89            // pass the command to tcl
90            return TclBind::eval(command, error);
91        }
92        else
93        {
94            CommandEvaluation evaluation;
95
96            // try to get the command evaluation from the cache
97            if (!CommandExecutor::getInstance().getCached(command, evaluation))
98            {
99                // it wasn't in the cache - evaluate the command
100                evaluation = CommandExecutor::evaluate(command);
101
102                // evaluate its arguments
103                evaluation.evaluateArguments();
104
105                // write the evaluation to the cache
106                CommandExecutor::getInstance().cache(command, evaluation);
107            }
108
109            // query the command and return its return-value
110            return evaluation.query(error);
111        }
112    }
113
114    /**
115        @brief Executes a command and returns its return-value as string.
116        @param command A string containing the command
117        @param error A pointer to a value (or NULL) where the error-code should be written to (see @ref CommandExecutorErrorCodes "error codes")
118        @param useTcl If true, the command is passed to tcl (see TclBind)
119        @return Returns the return-value of the command converted to a string (or "" if there's no return value)
120    */
121    /* static */ std::string CommandExecutor::query(const std::string& command, int* error, bool useTcl)
122    {
123        return CommandExecutor::queryMT(command, error, useTcl).getString();
124    }
125
126    /**
127        @brief Evaluates the given command.
128        @param command A string containing the command
129        @return Returns an instance of CommandEvaluation, which contains the evaluated ConsoleCommand, if the command is valid.
130
131        Evaluates the given command string and returns an instance of CommandEvaluation.
132        If the command is valid, this contains the evaluated ConsoleCommand. Otherwise it
133        can still be used to print hints or complete the command.
134
135        @note Tcl commands can not be evaluated. You have to pass them to execute() or to
136        TclBind directly.
137    */
138    /* static */ CommandEvaluation CommandExecutor::evaluate(const std::string& command)
139    {
140        // initialize the command evaluation
141        CommandEvaluation evaluation;
142        evaluation.initialize(command);
143
144        // assign the fallback-command to get hints about the possible commands and groups
145        evaluation.hintCommand_ = ConsoleCommand::getCommand(__CC_CommandExecutor_name, __CC_autocomplete_name);
146
147        // check if there's at least one argument
148        if (evaluation.getNumberOfArguments() >= 1)
149        {
150            // try to get a command from the first token
151            evaluation.execCommand_ = ConsoleCommand::getCommandLC(evaluation.getToken(0));
152            if (evaluation.execCommand_)
153                evaluation.execArgumentsOffset_ = 1;
154            else if (evaluation.getNumberOfArguments() >= 2)
155            {
156                // try to get a command from the first two tokens
157                evaluation.execCommand_ = ConsoleCommand::getCommandLC(evaluation.getToken(0), evaluation.getToken(1));
158                if (evaluation.execCommand_)
159                    evaluation.execArgumentsOffset_ = 2;
160            }
161        }
162
163        // if a valid command was found and the user is already entering arguments, overwrite hintCommand_ with execCommand_
164        if (evaluation.execCommand_ && evaluation.getNumberOfArguments() > evaluation.execArgumentsOffset_)
165        {
166            evaluation.hintCommand_ = evaluation.execCommand_;
167            evaluation.hintArgumentsOffset_ = evaluation.execArgumentsOffset_;
168        }
169
170        return evaluation;
171    }
172
173    /**
174        @brief Gets an evaluated command from the cache.
175        @param command The command that should be looked up in the cache
176        @param evaluation Reference to a CommandEvaluation that will be used to return the cached evaluation.
177        @return Returns true if the command was found in the cache
178    */
179    bool CommandExecutor::getCached(const std::string& command, CommandEvaluation& evaluation)
180    {
181        if (Shell::getCacheSize() == 0)
182            return false;
183
184        // check if the command is in the cache
185        std::map<std::string, CacheEntry>::iterator it = this->cache_.find(command);
186        if (it != this->cache_.end())
187        {
188            // update ordered list of cached commands (move it to the front)
189            this->cachelist_.erase(it->second.iterator_);
190            this->cachelist_.push_front(command);
191            it->second.iterator_ = this->cachelist_.begin();
192
193            // assign the cached evaluation
194            evaluation = it->second.evaluation_;
195            return true;
196        }
197        return false;
198    }
199
200    /**
201        @brief Writes a command evaluation for a given command to the cache.
202    */
203    void CommandExecutor::cache(const std::string& command, const CommandEvaluation& evaluation)
204    {
205        if (Shell::getCacheSize() == 0)
206            return;
207
208        // push command to the front of the ordered list
209        this->cachelist_.push_front(command);
210
211        // create a cache entry and store it in the cache
212        CacheEntry entry;
213        entry.evaluation_ = evaluation;
214        entry.iterator_ = this->cachelist_.begin();
215        this->cache_[command] = entry;
216
217        // remove the last command in the ordered list from the cache if it exceeds the maximum size of the cache
218        if (this->cachelist_.size() > Shell::getCacheSize())
219        {
220            this->cache_.erase(this->cachelist_.back());
221            this->cachelist_.pop_back();
222        }
223    }
224
225    /**
226        @brief This function is used as console command which executes commands that are usually hidden.
227
228        The argument completion function of this console commands is used to find
229        and enter hidden commands and their arguments.
230    */
231    /* static */ MultiType CommandExecutor::unhide(const std::string& command)
232    {
233        return CommandExecutor::queryMT(command);
234    }
235
236    /**
237        @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.
238        @param alias The name of the new command alias
239        @param command The existing command and (optionally) its arguments
240    */
241    /* static */ void CommandExecutor::alias(const std::string& alias, const std::string& command)
242    {
243        // first check if the given command is valid, else print an error
244        CommandEvaluation evaluation = CommandExecutor::evaluate(command);
245        if (evaluation.isValid())
246        {
247            // it is valid - copy the executor of this command
248            ExecutorPtr executor = new Executor(*evaluation.getConsoleCommand()->getExecutor().get());
249
250            // evaluate the arguments and if this returns no error, store them as default values
251            if (!evaluation.evaluateArguments())
252            {
253                for (size_t i = 0; i < MAX_FUNCTOR_ARGUMENTS; ++i)
254                    executor->setDefaultValue(i, evaluation.getEvaluatedArgument(i));
255            }
256
257            // split the alias in tokens
258            SubString tokens(alias, " ");
259
260            // check if the alias already exists - print an error and return if it does
261            if ((tokens.size() == 1 && ConsoleCommand::getCommand(tokens[0])) || (tokens.size() == 2 && ConsoleCommand::getCommand(tokens[0], tokens[1])))
262            {
263                COUT(1) << "Error: A command with name \"" << alias << "\" already exists." << std::endl;
264                return;
265            }
266
267            // create a new console command with the given alias as its name
268            if (tokens.size() == 1)
269                createConsoleCommand(tokens[0], executor);
270            else if (tokens.size() == 2)
271                createConsoleCommand(tokens[0], tokens[1], executor);
272            else
273                COUT(1) << "Error: \"" << alias << "\" is not a valid alias name (must have one or two words)." << std::endl;
274        }
275        else
276            COUT(1) << "Error: \"" << command << "\" is not a valid command (did you mean \"" << evaluation.getCommandSuggestion() << "\"?)." << std::endl;
277    }
278}
Note: See TracBrowser for help on using the repository browser.