| 1 | 
 #ifndef XMLCUSTOMCODE_H | 
 
 
 
 
 
 | 2 | 
 #define XMLCUSTOMCODE_H | 
 
 
 
 
 
 | 3 | 
  | 
 
 
 
 
 
 | 4 | 
 #include "utilxmltools.h" | 
 
 
 
 
 
 | 5 | 
  | 
 
 
 
 
 
 | 6 | 
 #include <ctime> // script execution time calculation | 
 
 
 
 
 
 | 7 | 
 #include <QScriptEngine> | 
 
 
 
 
 
 | 8 | 
 #include <QTextStream> | 
 
 
 
 
 
 | 9 | 
 #include <QCoreApplication> | 
 
 
 
 
 
 | 10 | 
  | 
 
 
 
 
 
 | 11 | 
 #include <QThreadPool> | 
 
 
 
 
 
 | 12 | 
 #include <QtConcurrent/QtConcurrent> | 
 
 
 
 
 
 | 13 | 
  | 
 
 
 
 
 
 | 14 | 
 static constexpr double SLOW_SCRIPT_TIME = 0.1; // if a user script takes more than 0.1 seconds to execute give a warning. | 
 
 
 
 
 
 | 15 | 
 static constexpr int CUSTOM_FILES_PER_THREAD = 4; | 
 
 
 
 
 
 | 16 | 
  | 
 
 
 
 
 
 | 17 | 
 // Uses a singleton implementation (based on here: http://www.yolinux.com/TUTORIALS/C++Singleton.html) | 
 
 
 
 
 
 | 18 | 
 // which allows each thread to keep one script engine without always create/destruct them | 
 
 
 
 
 
 | 19 | 
 class XmlCustomCode | 
 
 
 
 
 
 | 20 | 
 { | 
 
 
 
 
 
 | 21 | 
 public: | 
 
 
 
 
 
 | 22 | 
     static XmlCustomCode* getInstance(); | 
 
 
 
 
 
 | 23 | 
     void executeCustomCode(const QString &jsString, const QVector<QString> &filesToProcess, const bool backupsEnabled, const bool verboseEnabled); | 
 
 
 
 
 
 | 24 | 
 private: | 
 
 
 
 
 
 | 25 | 
     static XmlCustomCode* uniqueInstance; | 
 
 
 
 
 
 | 26 | 
  | 
 
 
 
 
 
 | 27 | 
     const int numThreads; | 
 
 
 
 
 
 | 28 | 
     QThreadPool myThreadPool; | 
 
 
 
 
 
 | 29 | 
     QMutex mutexIsAvailable; | 
 
 
 
 
 
 | 30 | 
  | 
 
 
 
 
 
 | 31 | 
     struct jsCustomCodeEngine{ | 
 
 
 
 
 
 | 32 | 
         QScriptEngine* scriptEngine; | 
 
 
 
 
 
 | 33 | 
         QScriptValue* jsFunction; | 
 
 
 
 
 
 | 34 | 
         QScriptValue* getXmlDataFunction; | 
 
 
 
 
 
 | 35 | 
         QScriptValue* setXmlDataFunction; | 
 
 
 
 
 
 | 36 | 
         bool isAvailable; | 
 
 
 
 
 
 | 37 | 
     }; | 
 
 
 
 
 
 | 38 | 
  | 
 
 
 
 
 
 | 39 | 
     QVector<jsCustomCodeEngine> jsScriptEngines; | 
 
 
 
 
 
 | 40 | 
  | 
 
 
 
 
 
 | 41 | 
     XmlCustomCode(); // constructor is private (use getInstance) | 
 
 
 
 
 
 | 42 | 
     XmlCustomCode(XmlCustomCode const&);             // copy constructor is private | 
 
 
 
 
 
 | 43 | 
     XmlCustomCode& operator=(XmlCustomCode const&);  // assignment operator is private | 
 
 
 
 
 
 | 44 | 
  | 
 
 
 
 
 
 | 45 | 
     void displayJsException(QScriptEngine &engine, QScriptValue &engineResult); | 
 
 
 
 
 
 | 46 | 
     jsCustomCodeEngine& getAvailableJsEngine(); | 
 
 
 
 
 
 | 47 | 
  | 
 
 
 
 
 
 | 48 | 
     __attribute__((always_inline)) inline void customCodeUnwinding(const QString &fileName, QString &currXmlFileString, | 
 
 
 
 
 
 | 49 | 
     QScriptEngine &engine, clock_t &begin, double elapsed_secs, QScriptValue &engineResult, QScriptValue &jsFunction, | 
 
 
 
 
 
 | 50 | 
     QScriptValue &getXmlDataFunction, QScriptValue &setXmlDataFunction, const bool &backupsEnabled, const bool &verboseEnabled){ | 
 
 
 
 
 
 | 51 | 
         if(backupsEnabled){ | 
 
 
 
 
 
 | 52 | 
             UtilXmlTools::backupFile(fileName, verboseEnabled); | 
 
 
 
 
 
 | 53 | 
         } | 
 
 
 
 
 
 | 54 | 
  | 
 
 
 
 
 
 | 55 | 
         QFile currXmlFile(fileName); | 
 
 
 
 
 
 | 56 | 
  | 
 
 
 
 
 
 | 57 | 
         if(!currXmlFile.open(QFile::ReadOnly | QFile::Text)){ | 
 
 
 
 
 
 | 58 | 
             UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Error loading '" + fileName + "' file for read operation."); | 
 
 
 
 
 
 | 59 | 
         } | 
 
 
 
 
 
 | 60 | 
  | 
 
 
 
 
 
 | 61 | 
         currXmlFileString=QTextStream(&currXmlFile).readAll(); | 
 
 
 
 
 
 | 62 | 
  | 
 
 
 
 
 
 | 63 | 
         currXmlFile.close(); // close reading | 
 
 
 
 
 
 | 64 | 
  | 
 
 
 
 
 
 | 65 | 
         setXmlDataFunction.call(setXmlDataFunction,QScriptValueList() << currXmlFileString); | 
 
 
 
 
 
 | 66 | 
  | 
 
 
 
 
 
 | 67 | 
         begin = clock(); | 
 
 
 
 
 
 | 68 | 
  | 
 
 
 
 
 
 | 69 | 
         engineResult=jsFunction.call(); // main funtion allows to use return to exit prematurely from user code | 
 
 
 
 
 
 | 70 | 
  | 
 
 
 
 
 
 | 71 | 
         if(verboseEnabled){ | 
 
 
 
 
 
 | 72 | 
             elapsed_secs = double(clock() - begin) / CLOCKS_PER_SEC; | 
 
 
 
 
 
 | 73 | 
  | 
 
 
 
 
 
 | 74 | 
             // Warn the user if the script took much time | 
 
 
 
 
 
 | 75 | 
             if(elapsed_secs>SLOW_SCRIPT_TIME){ | 
 
 
 
 
 
 | 76 | 
                 std::cout << "Warning: Slow javascript code detected.\n" << | 
 
 
 
 
 
 | 77 | 
                              "Warning: Script execution seconds: " << elapsed_secs | 
 
 
 
 
 
 | 78 | 
                           << std::endl; | 
 
 
 
 
 
 | 79 | 
             } | 
 
 
 
 
 
 | 80 | 
         } | 
 
 
 
 
 
 | 81 | 
  | 
 
 
 
 
 
 | 82 | 
         if (engine.hasUncaughtException()) { | 
 
 
 
 
 
 | 83 | 
             displayJsException(engine,engineResult); | 
 
 
 
 
 
 | 84 | 
         } | 
 
 
 
 
 
 | 85 | 
  | 
 
 
 
 
 
 | 86 | 
         if(!currXmlFile.open(QFile::WriteOnly | QFile::Text | QIODevice::Truncate)){ | 
 
 
 
 
 
 | 87 | 
             UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Error loading '" + fileName + "' file for @CUSTOM_CODE write operation."); | 
 
 
 
 
 
 | 88 | 
         } | 
 
 
 
 
 
 | 89 | 
  | 
 
 
 
 
 
 | 90 | 
         engineResult=getXmlDataFunction.call(); | 
 
 
 
 
 
 | 91 | 
  | 
 
 
 
 
 
 | 92 | 
         if (engine.hasUncaughtException()) { | 
 
 
 
 
 
 | 93 | 
             displayJsException(engine,engineResult); | 
 
 
 
 
 
 | 94 | 
         } | 
 
 
 
 
 
 | 95 | 
  | 
 
 
 
 
 
 | 96 | 
         QTextStream(&currXmlFile) << engineResult.toString(); // retreive the modified xml by javascript and save it to the file | 
 
 
 
 
 
 | 97 | 
  | 
 
 
 
 
 
 | 98 | 
         currXmlFile.close(); | 
 
 
 
 
 
 | 99 | 
     } | 
 
 
 
 
 
 | 100 | 
 }; | 
 
 
 
 
 
 | 101 | 
  | 
 
 
 
 
 
 | 102 | 
 #endif // XMLCUSTOMCODE_H |