Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7401 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
RevLine 
[1505]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
[7401]29/**
30    @file
31    @brief Implementation of CommandExecutor
32*/
33
[1505]34#include "CommandExecutor.h"
[3196]35
[1505]36#include "ConsoleCommand.h"
37#include "TclBind.h"
[7228]38#include "Shell.h"
[1505]39
40namespace orxonox
41{
[7228]42    static const std::string __CC_CommandExecutor_name = "CommandExecutor";
43    static const std::string __CC_autocomplete_name = "autocomplete";
44
[7236]45    SetConsoleCommand(__CC_CommandExecutor_name, __CC_autocomplete_name, &CommandExecutor::_autocomplete)
[7228]46        .hide()
47        .argumentCompleter(0, autocompletion::groupsandcommands())
48        .argumentCompleter(1, autocompletion::subcommands());
49
[7236]50    SetConsoleCommand("unhide", &CommandExecutor::unhide)
[7234]51        .argumentCompleter(0, autocompletion::hiddencommand());
[7233]52
[7277]53    SetConsoleCommand("alias", &CommandExecutor::alias)
54        .argumentCompleter(1, autocompletion::command());
55
[7401]56    /**
57        @brief Returns a reference to the only instance of CommandExecutor.
58    */
[7228]59    /* static */ CommandExecutor& CommandExecutor::getInstance()
[1505]60    {
61        static CommandExecutor instance;
62        return instance;
63    }
64
[7401]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    */
[7228]71    /* static */ int CommandExecutor::execute(const std::string& command, bool useTcl)
[1505]72    {
[7228]73        int error;
74        CommandExecutor::queryMT(command, &error, useTcl);
75        return error;
[1505]76    }
77
[7401]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    */
[7228]85    /* static */ MultiType CommandExecutor::queryMT(const std::string& command, int* error, bool useTcl)
[1505]86    {
87        if (useTcl)
[7401]88        {
89            // pass the command to tcl
[7228]90            return TclBind::eval(command, error);
[7401]91        }
[7189]92        else
[7229]93        {
94            CommandEvaluation evaluation;
[7401]95
96            // try to get the command evaluation from the cache
[7229]97            if (!CommandExecutor::getInstance().getCached(command, evaluation))
98            {
[7401]99                // it wasn't in the cache - evaluate the command
[7229]100                evaluation = CommandExecutor::evaluate(command);
[7401]101
102                // evaluate its arguments
103                evaluation.evaluateArguments();
104
105                // write the evaluation to the cache
[7229]106                CommandExecutor::getInstance().cache(command, evaluation);
107            }
108
[7401]109            // query the command and return its return-value
[7229]110            return evaluation.query(error);
111        }
[1505]112    }
113
[7401]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    */
[7228]121    /* static */ std::string CommandExecutor::query(const std::string& command, int* error, bool useTcl)
[3035]122    {
[7228]123        return CommandExecutor::queryMT(command, error, useTcl).getString();
[3035]124    }
125
[7401]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    */
[7228]138    /* static */ CommandEvaluation CommandExecutor::evaluate(const std::string& command)
[3035]139    {
[7401]140        // initialize the command evaluation
[7228]141        CommandEvaluation evaluation;
142        evaluation.initialize(command);
[3035]143
[7401]144        // assign the fallback-command to get hints about the possible commands and groups
[7236]145        evaluation.hintCommand_ = ConsoleCommand::getCommand(__CC_CommandExecutor_name, __CC_autocomplete_name);
[1505]146
[7401]147        // check if there's at least one argument
[7228]148        if (evaluation.getNumberOfArguments() >= 1)
[1505]149        {
[7401]150            // try to get a command from the first token
[7236]151            evaluation.execCommand_ = ConsoleCommand::getCommandLC(evaluation.getToken(0));
[7228]152            if (evaluation.execCommand_)
153                evaluation.execArgumentsOffset_ = 1;
154            else if (evaluation.getNumberOfArguments() >= 2)
[1505]155            {
[7401]156                // try to get a command from the first two tokens
[7236]157                evaluation.execCommand_ = ConsoleCommand::getCommandLC(evaluation.getToken(0), evaluation.getToken(1));
[7228]158                if (evaluation.execCommand_)
159                    evaluation.execArgumentsOffset_ = 2;
[1505]160            }
161        }
162
[7401]163        // if a valid command was found and the user is already entering arguments, overwrite hintCommand_ with execCommand_
[7228]164        if (evaluation.execCommand_ && evaluation.getNumberOfArguments() > evaluation.execArgumentsOffset_)
[1505]165        {
[7228]166            evaluation.hintCommand_ = evaluation.execCommand_;
167            evaluation.hintArgumentsOffset_ = evaluation.execArgumentsOffset_;
[1505]168        }
169
[7228]170        return evaluation;
[1505]171    }
[7229]172
[7401]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    */
[7229]179    bool CommandExecutor::getCached(const std::string& command, CommandEvaluation& evaluation)
180    {
181        if (Shell::getCacheSize() == 0)
182            return false;
183
[7401]184        // check if the command is in the cache
[7229]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
[7401]200    /**
201        @brief Writes a command evaluation for a given command to the cache.
202    */
[7229]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    }
[7233]224
[7401]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    */
[7234]231    /* static */ MultiType CommandExecutor::unhide(const std::string& command)
[7233]232    {
[7234]233        return CommandExecutor::queryMT(command);
[7233]234    }
[7277]235
[7401]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    */
[7277]241    /* static */ void CommandExecutor::alias(const std::string& alias, const std::string& command)
242    {
[7401]243        // first check if the given command is valid, else print an error
[7277]244        CommandEvaluation evaluation = CommandExecutor::evaluate(command);
245        if (evaluation.isValid())
246        {
[7401]247            // it is valid - copy the executor of this command
[7277]248            ExecutorPtr executor = new Executor(*evaluation.getConsoleCommand()->getExecutor().get());
249
[7401]250            // evaluate the arguments and if this returns no error, store them as default values
251            if (!evaluation.evaluateArguments())
[7277]252            {
253                for (size_t i = 0; i < MAX_FUNCTOR_ARGUMENTS; ++i)
[7401]254                    executor->setDefaultValue(i, evaluation.getEvaluatedArgument(i));
[7277]255            }
256
[7401]257            // split the alias in tokens
[7277]258            SubString tokens(alias, " ");
259
[7401]260            // check if the alias already exists - print an error and return if it does
[7277]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
[7401]267            // create a new console command with the given alias as its name
[7277]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    }
[1505]278}
Note: See TracBrowser for help on using the repository browser.