File indexing completed on 2024-05-12 16:23:28

0001 /***************************************************************************
0002  *   Copyright (C) 2013 by Linuxstopmotion contributors;                   *
0003  *   see the AUTHORS file for details.                                     *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 
0021 #include <stdarg.h>
0022 
0023 #include "executor.h"
0024 #include "command.h"
0025 #include "random.h"
0026 #include "commandlogger.h"
0027 #include "src/foundation/stringwriter.h"
0028 #include "src/domain/animation/errorhandler.h"
0029 
0030 #include <map>
0031 #include <vector>
0032 #include <string>
0033 #include <memory>
0034 
0035 #include <stdint.h>
0036 #include <string.h>
0037 
0038 // help out Eclipse's C++ parsing
0039 #ifndef INT32_MAX
0040 #define INT32_MAX 0x7FFFFFFF
0041 #endif
0042 
0043 class UndoRedoObserver;
0044 
0045 /**
0046  * Reads a space-separated list of strings and numbers. Strings are quoted,
0047  * quotes within strings can be escaped with backslashes.
0048  */
0049 class StringReader {
0050     const char* p;
0051     const char* end;
0052 public:
0053     /**
0054      * Indicates whether the end of the input buffer has been reached.
0055      */
0056     enum IsEof {
0057         isNotEof = 0,
0058         isEof = 1
0059     };
0060     /**
0061      * Indicates whether the end of a line has been reached.
0062      */
0063     enum IsEol {
0064         isNotEol = 0,
0065         isEol = 1
0066     };
0067     /**
0068      * Indicates whether the parse succeeded.
0069      */
0070     enum ParseSucceeded {
0071         parseFailed = 0,
0072         parseSucceeded = 1
0073     };
0074     /**
0075      * Constructs a @c StringReader without a buffer. The buffer must be set with
0076      * @c SetBuffer before use.
0077      */
0078     StringReader() : p(0), end(0) {
0079     }
0080     /**
0081      * Constructs a @c StringReader.
0082      * @param input The null-terminated buffer to read.
0083      */
0084     StringReader(const char* input) {
0085         p = input;
0086         end = p + strlen(input);
0087     }
0088     /**
0089      * Constructs a @c StringReader.
0090      * @param input The start of the buffer to read.
0091      * @param inputEnd The end of the buffer to read.
0092      */
0093     StringReader(const char* input, const char* endInput)
0094         : p(input), end(endInput) {
0095     }
0096     const char* getPos() const {
0097         return p;
0098     }
0099     void setPos(const char* pos) {
0100         p = pos;
0101     }
0102     /**
0103      * Sets the buffer to be read.
0104      * @param input The start of the buffer to read.
0105      * @param inputEnd The end of the buffer to read.
0106      */
0107     void setBuffer(const char* input, const char* endInput) {
0108         p = input;
0109         end = endInput;
0110     }
0111     /**
0112      * Sets the buffer to be read.
0113      * @param input The null-terminated string to read.
0114      */
0115     void setBuffer(const char* input) {
0116         p = input;
0117         end = input + strlen(input);
0118     }
0119     /**
0120      * Gets the next character from the input buffer, as long as it isn't an
0121      * end-of-line character.
0122      * @param out Stores the character output, only if @c isNotEol is returned.
0123      * @return @c isNotEol on success; in this case @a out will be set.
0124      * @c isEol if we are at the end of the buffer or the next character is a
0125      * line-termination character, in which case @a out will not be set.
0126      */
0127     IsEol getCharFromLine(char& out) {
0128         if (p == end)
0129             return isEol;
0130         if (*p == '\n' || *p == '\r')
0131             return isEol;
0132         out = *p;
0133         ++p;
0134         return isNotEol;
0135     }
0136     /**
0137      * Consume an end-of-line marker (\r, \n or \r\n), if present at the
0138      * position we are reading from.
0139      * @return @c isEof if we have reached the end of the buffer, whether or
0140      * not we consumed an end-of-line marker. @c isNotEof otherwise.
0141      */
0142     IsEof chompEol() {
0143         if (p != end && *p == '\r') {
0144             ++p;
0145         }
0146         if (p != end && *p == '\n') {
0147             ++p;
0148         }
0149         return p == end? isEof : isNotEof;
0150     }
0151     /**
0152      * Consume whitespace.
0153      * @return @c isEof if we reached the end of the buffer, whether or not we
0154      * consumed any space. @c isNotEof otherwise.
0155      */
0156     IsEof chompSpace() {
0157         while (p != end) {
0158             if (*p != ' ')
0159                 return isNotEof;
0160             ++p;
0161         }
0162         return isEof;
0163     }
0164     /**
0165      * Consumes a quote, if present.
0166      * returns true if a quote was consumed, false otherwise.
0167      */
0168     bool getQuote() {
0169         if (p != end && *p == '"') {
0170             ++p;
0171             return true;
0172         }
0173         return false;
0174     }
0175     /**
0176      * Tests whether we are at the end of the current line.
0177      * @return true if we are at the end of the buffer or the next character is
0178      * an end-of-line marker.
0179      */
0180     bool isEndOfLine() {
0181         return p == end || *p == '\n' || *p == '\r';
0182     }
0183     /**
0184      * Tests whether we are at an argument-delimiting character.
0185      * @returns true if we are at a whitespace, the end of the line or the end
0186      * of the buffer. false otherwise.
0187      */
0188     bool isFinishedArgument() {
0189         return *p == ' ' || *p == '!' || *p == '.' || isEndOfLine();
0190     }
0191     /**
0192      * Return the end-of-command marker, which is zero or more dots followed by
0193      * zero or one exclamation mark followed by the end of the line.
0194      * @param dotCount Number of dots read, if parseSucceeded is returned.
0195      * @param exclamation True if there was an exclamation mark and
0196      * parseSceeded is returned.
0197      * @return parseSucceeded if this is an end-of-command marker, parseFailed
0198      * if not. If parseFailed, dotCount and exclamation may be set to arbitrary
0199      * values, and the parse will be returned to how it was before the call.
0200      */
0201     ParseSucceeded getEndOfCommand(int32_t& dotCount, bool& exclamation) {
0202         const char* old = p;
0203         dotCount = 0;
0204         exclamation = false;
0205         char c;
0206         while (isEol != getCharFromLine(c)) {
0207             if (!exclamation && c == '.') {
0208                 ++dotCount;
0209             } else if (!exclamation && c == '!') {
0210                 exclamation = true;
0211             } else {
0212                 p = old;
0213                 return parseFailed;
0214             }
0215         }
0216         return parseSucceeded;
0217     }
0218     /**
0219      * Consumes a decimal digit, if one is next in the buffer. Does not consume
0220      * non-digit characters.
0221      * @param digit The digit output, if @c parseSuceeded is returned.
0222      * @return @c parseSucceeded on success; in this case @a digit will be set.
0223      * @c parseFailed if the next character is not a digit. In this case
0224      * @a digit will not be set and no characters will have been consumed.
0225      */
0226     ParseSucceeded getDigit(int32_t& digit) {
0227         if (p == end)
0228             return parseFailed;
0229         if (*p < '0' || '9' < *p)
0230             return parseFailed;
0231         digit = *p - '0';
0232         ++p;
0233         return parseSucceeded;
0234     }
0235     /**
0236      * As for @a GetDigit but '8' and '9' are not considered digits and are
0237      * not consumed.
0238      */
0239     ParseSucceeded getOctalDigit(int32_t& digit) {
0240         if (p == end)
0241             return parseFailed;
0242         if (*p < '0' || '7' < *p)
0243             return parseFailed;
0244         digit = *p - '0';
0245         ++p;
0246         return parseSucceeded;
0247     }
0248     /**
0249      * Consume a number, possibly negative (although '+' is not consumed and
0250      * causes a parse failure).
0251      * @param @a out The parsed number; only if @c parseSucceeded is returned.
0252      * @return @c parseSucceeded if the next characters were possibly a '-',
0253      * a string of numbers then an argument delimiter. All except the
0254      * delimiter are consumed. @a out is set to the parsed number. In other
0255      * cases, no characters are consumed, @a out is not set and @c parseFailed
0256      * is returned.
0257      */
0258     ParseSucceeded getInteger(int32_t& out) {
0259         const char *old = p;
0260         if (isEof == chompSpace())
0261             return parseFailed;
0262         int32_t n = 0;
0263         int32_t sign = 1;
0264         if (getDigit(n) == parseFailed) {
0265             if (*p == '-')
0266                 sign = -1;
0267             else
0268                 return parseFailed;
0269             ++p;
0270         }
0271         int32_t soFar = n;
0272         while (getDigit(n) == parseSucceeded) {
0273             soFar = soFar * 10 + n;
0274         }
0275         if (isFinishedArgument()) {
0276             out = soFar * sign;
0277             return parseSucceeded;
0278         }
0279         p = old;
0280         return parseFailed;
0281     }
0282     /**
0283      * Gets a string. The string must be in double quotes and the final quote
0284      * must be followed by an argument delimiter. The string and its quotes are
0285      * consumed. Line terminations within the string are not permitted.
0286      * Double quotes and backslashes must be quoted with backslashes. '\r' and
0287      * '\n' must be used for carriage return and line-feed characters. \nnn can
0288      * be used for ASCII codes in octal-- these must not be followed by another
0289      * digit (if present, this should be quoted in octal as well).
0290      * @param out Returns the parsed and decoded string.
0291      * @return @c parseSuccessful on success. @a out is filled.
0292      * @c parseFailed on failure. Nothing is consumed. Some of @a out may
0293      * have been set.
0294      */
0295     ParseSucceeded getString(std::string& out) {
0296         out.clear();
0297         const char *old = p;
0298         chompSpace();
0299         if (!getQuote()) {
0300             p = old;
0301             return parseFailed;
0302         }
0303         char c;
0304         while (!getQuote()) {
0305             // normal state
0306             if (getCharFromLine(c) == isEol) {
0307                 p = old;
0308                 return parseFailed;
0309             }
0310             if (c != '\\') {
0311                 out.append(1, c);
0312             } else {
0313                 // backslash state
0314                 if (getCharFromLine(c) == isEol)
0315                     // line continuation
0316                     chompEol();
0317                 if (c == 'n')
0318                     out.append(1, '\n');
0319                 else if (c == 'r')
0320                     out.append(1, '\r');
0321                 else if ('0' <= c && c <= '7') {
0322                     // octal
0323                     int32_t soFar = c - '0';
0324                     int32_t n;
0325                     while (parseSucceeded == getOctalDigit(n)) {
0326                         soFar = soFar * 8 + n;
0327                     }
0328                     out += static_cast<char>(soFar);
0329                 } else {
0330                     // Backslash followed by anything else is literally that
0331                     // anything else (the backslash is never output).
0332                     out += static_cast<char>(c);
0333                 }
0334             }
0335         }
0336         return parseSucceeded;
0337     }
0338     /**
0339      * Reads an identifier.
0340      * @param out Returns the parsed and decoded string.
0341      * @return @c parseSuccessful on success. @a out is filled.
0342      * @c parseFailed on failure. No non-whitespace characters will have
0343      * been consumed.
0344      */
0345     ParseSucceeded getIdentifier(std::string& out) {
0346         out.clear();
0347         chompSpace();
0348         if (isEndOfLine())
0349             return parseFailed;
0350         while (!isFinishedArgument()) {
0351             out.append(1, *p);
0352             ++p;
0353         }
0354         return parseSucceeded;
0355     }
0356     int getUndoCount() {
0357         int count = 0;
0358         for (; p != end; ++p) {
0359             switch (*p) {
0360             case '?':
0361                 ++count;
0362                 break;
0363             case '!':
0364                 --count;
0365                 break;
0366             default:
0367                 return count;
0368             }
0369         }
0370         return count;
0371     }
0372 };
0373 
0374 class StringReaderParameters : public Parameters {
0375     StringReader& reader;
0376 public:
0377     StringReaderParameters(StringReader& r) : reader(r) {
0378     }
0379     ~StringReaderParameters() {
0380     }
0381     int32_t getInteger(int32_t, int32_t) {
0382         int32_t r;
0383         if (StringReader::parseFailed == reader.getInteger(r))
0384             throw IncorrectParameterException();
0385         return r;
0386     }
0387     void getString(std::string& out, const char*) {
0388         if (StringReader::parseFailed == reader.getString(out))
0389             throw IncorrectParameterException();
0390     }
0391 };
0392 
0393 
0394 Executor::~Executor() {
0395 }
0396 
0397 /**
0398  * Write to the log as the parameters are read from some other Parameters.
0399  * Also checks that the values produced are within the ranges specified,
0400  * throwing a {@ref ParametersOutOfRangeException} if a value is out-of-range.
0401  */
0402 class WriterParametersWrapper : public Parameters {
0403     Parameters& delegate;
0404     StringWriter writer;
0405 public:
0406     /**
0407      * Construct a Parameters wrapper around another Parameters. Provides the
0408      * same parameters as this delegate, but writes out the command as it goes.
0409      * @param p The delegate.
0410      * @param name The name of the command (to write to the logger).
0411      */
0412     WriterParametersWrapper(Parameters& p, const char* name)
0413             : delegate(p) {
0414         writer.writeIdentifier(name);
0415     }
0416     ~WriterParametersWrapper() {
0417     }
0418     int32_t getInteger(int32_t min, int32_t max) {
0419         int32_t r = delegate.getInteger(min, max);
0420         if(r < min || max < r)
0421             throw ParametersOutOfRangeException();
0422         writer.writeInteger(r);
0423         return r;
0424     }
0425     int32_t getHowMany() {
0426         int32_t r = delegate.getHowMany();
0427         if (r < 0)
0428             throw ParametersOutOfRangeException();
0429         writer.writeInteger(r);
0430         return r;
0431     }
0432     void getString(std::string& out, const char* pattern) {
0433         delegate.getString(out, pattern);
0434         writer.writeString(out.c_str());
0435     }
0436     void writeCommand(CommandLogger* logger) {
0437         if (logger)
0438             logger->writePendingCommand(writer.result());
0439         writer.reset();
0440     }
0441 };
0442 
0443 class VaListParameters : public Parameters {
0444     va_list& args;
0445 public:
0446     /**
0447      * Construct a Parameters facade over a variable argument list.
0448      * @param a The va_list, which must have had va_start called on it.
0449      * va_end should not be called after use.
0450      * @param name The name of the command being constructed (i.e.
0451      * the @a name parameter of {@ref CommandExecutor::execute})
0452      * @param commandLogger The logger that will receive a string
0453      * representation of the command and its parameters. Ownership is
0454      * not passed.
0455      */
0456     VaListParameters(va_list& a)
0457             : args(a) {
0458     }
0459     ~VaListParameters() {
0460     }
0461     int32_t getInteger(int32_t, int32_t) {
0462         return va_arg(args, int32_t);
0463     }
0464     void getString(std::string& out, const char*) {
0465         const char* s = va_arg(args, const char*);
0466         out.assign(s);
0467     }
0468 };
0469 
0470 class RandomParameters : public Parameters {
0471     RandomSource& rs;
0472 public:
0473     RandomParameters(RandomSource& rng)
0474             : rs(rng) {
0475     }
0476     int32_t getInteger(int32_t min, int32_t max) {
0477         return rs.getUniform(min, max);
0478     }
0479     int32_t getHowMany() {
0480         return 1 + rs.getLogInt(60);
0481     }
0482     void getString(std::string& out, const char* pattern) {
0483         if (!pattern)
0484             pattern = "?*";
0485         out.clear();
0486         for (; *pattern; ++pattern) {
0487             char c = *pattern;
0488             if (c == '?') {
0489                 out.append(1, rs.getCharacter());
0490             } else if (c == '*') {
0491                 rs.appendAlphanumeric(out);
0492             } else {
0493                 out.append(1, c);
0494             }
0495         }
0496     }
0497 };
0498 
0499 class ConcreteExecutor : public Executor {
0500     CommandHistory history;
0501     CommandLogger* logger;
0502     typedef std::map<std::string, CommandFactory*> FactoryMap;
0503     FactoryMap factories;
0504     // does not own its factories
0505     std::vector<std::string> constructiveCommands;
0506     CommandFactory* Factory(const char* name) {
0507         std::string n(name);
0508         FactoryMap::iterator found = factories.find(n);
0509         if (found == factories.end())
0510             throw UnknownCommandException();
0511         return found->second;
0512     }
0513 public:
0514     ConcreteExecutor() : logger(0) {
0515     }
0516     ~ConcreteExecutor() {
0517         for (FactoryMap::iterator i = factories.begin();
0518                 i != factories.end(); ++i) {
0519             delete i->second;
0520         }
0521     }
0522     void execute(const char* name, ...) {
0523         CommandFactory* f = Factory(name);
0524         va_list args;
0525         va_start(args, name);
0526         VaListParameters vpsd(args);
0527         try {
0528             WriterParametersWrapper vps(vpsd, name);
0529             Command* c = f->create(vps, *ErrorHandler::getThrower());
0530             if (c) {
0531                 vps.writeCommand(logger);
0532                 history.execute(*c);
0533                 logger->commit();
0534             }
0535         } catch(...) {
0536             // Unfortunately we can't put this in VaListParameters's destructor
0537             // because va_end must be called in the same function as va_start,
0538             // according to the standard.
0539             va_end(args);
0540             throw;
0541         }
0542         va_end(args);
0543     }
0544     void execute(const char* name, Parameters& params) {
0545         WriterParametersWrapper pw(params, name);
0546         CommandFactory* f = Factory(name);
0547         Command* c = f->create(pw, *ErrorHandler::getThrower());
0548         if (c) {
0549             pw.writeCommand(logger);
0550             history.execute(*c);
0551             logger->commit();
0552         }
0553     }
0554     bool executeFromLog(const char* line, ErrorHandler& e) {
0555         StringReader reader;
0556         reader.setBuffer(line);
0557         std::string id;
0558         int undoCount = reader.getUndoCount();
0559         for (; undoCount < 0; ++undoCount)
0560             history.redo();
0561         for (; undoCount != 0; --undoCount)
0562             history.undo();
0563         if (StringReader::parseSucceeded == reader.getIdentifier(id)) {
0564             const char* commandName = id.c_str();
0565             CommandFactory* f = Factory(commandName);
0566             StringReaderParameters sps(reader);
0567             Command* c = f->create(sps, e);
0568             // It is an error if a command executed from a log is invalid.
0569             // This would mean that the log has become out-of-sync with the
0570             // model.
0571             if (!c)
0572                 throw ParametersOutOfRangeException();
0573             int32_t subcommands;
0574             bool finished;
0575             if (StringReader::parseFailed
0576                     == reader.getEndOfCommand(subcommands, finished))
0577                 throw MalformedLineException();
0578             if (finished)
0579                 history.execute(*c);
0580             return true;
0581         }
0582         if (StringReader::parseSucceeded == reader.isEndOfLine())
0583             return false;
0584         throw MalformedLineException();
0585     }
0586     void executeRandomCommands(int& commandCount, RandomSource& rng,
0587             int minCount, int maxCount) {
0588         commandCount = 0;
0589         int n = factories.size();
0590         if (n == 0)
0591             throw UnknownCommandException();
0592         std::vector<std::string> factoryNames;
0593         for (FactoryMap::iterator i = factories.begin();
0594                 i != factories.end(); ++i) {
0595             factoryNames.push_back(i->first);
0596         }
0597         bool dontEnd = false;
0598         while (commandCount < maxCount) {
0599             int r = rng.getUniform(dontEnd || commandCount < minCount?
0600                     n - 1: n);
0601             if (r == n)
0602                 return;
0603             std::string& name(factoryNames[r]);
0604             RandomParameters rpsd(rng);
0605             const char* commandName = name.c_str();
0606             WriterParametersWrapper rps(rpsd, commandName);
0607             Command* c = factories[name]->create(rps, *ErrorHandler::getThrower());
0608             if (c) {
0609                 if (logger)
0610                     rps.writeCommand(logger);
0611                 history.execute(*c);
0612                 if (logger)
0613                     logger->commit();
0614                 ++commandCount;
0615             }
0616         }
0617     }
0618     virtual void executeRandomConstructiveCommands(RandomSource& rng) {
0619         int n = constructiveCommands.size();
0620         if (n == 0)
0621             throw UnknownCommandException();
0622         while (true) {
0623             int r = rng.getUniform(n);
0624             if (r == n)
0625                 return;
0626             std::string& name(constructiveCommands[r]);
0627             RandomParameters rpsd(rng);
0628             WriterParametersWrapper rps(rpsd, name.c_str());
0629             Command* c = factories[name]->create(rps, *ErrorHandler::getThrower());
0630             if (c) {
0631                 if (logger)
0632                     rps.writeCommand(logger);
0633                 history.execute(*c);
0634                 if (logger)
0635                     logger->commit();
0636             }
0637         }
0638     }
0639     void setCommandLogger(CommandLogger* log) {
0640         logger = log;
0641     }
0642     void addCommand(const char* name,
0643             std::unique_ptr<CommandFactory> factory, bool constructive) {
0644         std::string n(name);
0645         std::pair<std::string, CommandFactory*> p(n, factory.get());
0646         if (constructive)
0647             constructiveCommands.reserve(constructiveCommands.size() + 1);
0648         factories.insert(p);
0649         if (constructive)
0650             constructiveCommands.push_back(n);
0651         factory.release();
0652     }
0653     int commandCount() const {
0654         return factories.size();
0655     }
0656     void clearHistory() {
0657         history.clear();
0658     }
0659     bool undo() {
0660         if (!history.canUndo())
0661             return false;
0662         if (logger)
0663             logger->writePendingUndo();
0664         history.undo();
0665         if (logger)
0666             logger->commit();
0667         return true;
0668     }
0669     bool redo() {
0670         if (!history.canRedo())
0671             return false;
0672         if (logger)
0673             logger->writePendingRedo();
0674         history.redo();
0675         if (logger)
0676             logger->commit();
0677         return true;
0678     }
0679     bool canUndo() const {
0680         return history.canUndo();
0681     }
0682     bool canRedo() const {
0683         return history.canRedo();
0684     }
0685     void setUndoRedoObserver(UndoRedoObserver* observer) {
0686         history.setUndoRedoObserver(observer);
0687     }
0688 };
0689 
0690 Executor* makeExecutor() {
0691     return new ConcreteExecutor();
0692 }
0693 
0694 const char* IncorrectParameterException::what() const throw() {
0695     return "Command log corrupt (incorrect parameter type)!";
0696 }
0697 
0698 const char* MalformedLineException::what() const throw() {
0699     return "Command log corrupt (malformed line)!";
0700 }
0701 
0702 const char* ParametersOutOfRangeException::what() const throw() {
0703     return "Internal error or command log corrupt:\n"
0704             "Command parameters out of range.";
0705 }
0706 
0707 const char* UnknownCommandException::what() const throw() {
0708     return "Internal error or command log corrupt:\n"
0709             "Unknown command.";
0710 }
0711 
0712 const char* CommandNameAlreadyUsedException::what() const
0713         throw() {
0714     return "Internal error: Command registered twice!";
0715 }