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 }