source: s10k/XmlTools/xmlcustomcode.cpp @ 1073

Last change on this file since 1073 was 1073, checked in by s10k, 3 years ago

added XML Tools latest version (2.0d) and s10k's common libs

File size: 6.7 KB
Line 
1#include "xmlcustomcode.h"
2
3XmlCustomCode* XmlCustomCode::uniqueInstance = NULL;
4
5QScriptValue echo(QScriptContext *context, QScriptEngine*)
6{
7    std::cout << context->argument(0).toString().toUtf8().constData() << std::endl;
8
9    return "";
10}
11
12XmlCustomCode::XmlCustomCode(): numThreads(QThread::idealThreadCount())
13{
14    myThreadPool.setMaxThreadCount(numThreads);
15    myThreadPool.setExpiryTimeout(-1); // don't let threads expire
16
17    // create individual thread script engines
18    this->jsScriptEngines.reserve(this->numThreads);
19
20    QString jsxmlString;
21    QFile jsxmlfile(":/resources/libs/jsxml.js");
22
23    jsxmlfile.open(QFile::ReadOnly | QFile::Text);
24
25    jsxmlString=QTextStream(&jsxmlfile).readAll();
26
27    for(int i=0; i<this->numThreads; i++){
28
29        jsCustomCodeEngine e;
30
31        e.scriptEngine = new QScriptEngine();
32        e.jsFunction = new QScriptValue();
33
34        // main needs to be called so the user code is evaluated
35        // alternatively you can do: myFunc=engine.evaluate('(function main(){})'); myFunc.call();
36        // Note the () around the function
37        e.getXmlDataFunction = new QScriptValue(e.scriptEngine->evaluate("(function getXmlData() { return $xmlData; })"));
38        e.setXmlDataFunction = new QScriptValue(e.scriptEngine->evaluate("(function setXmlData(newXmlData) { $xmlData=newXmlData; })"));
39        e.isAvailable = true;
40
41        // Add echo function so user can debug the code
42        QScriptValue echoFunction = e.scriptEngine->newFunction(echo);
43        e.scriptEngine->globalObject().setProperty("echo", echoFunction);
44
45        // Add the js library for XmlEditing
46        e.scriptEngine->evaluate(jsxmlString);
47
48        this->jsScriptEngines.append(e);
49    }
50}
51
52XmlCustomCode* XmlCustomCode::getInstance(){
53
54    if (uniqueInstance==NULL){   // allow only one instance
55        uniqueInstance = new XmlCustomCode();
56    }
57
58    return uniqueInstance;
59}
60
61void XmlCustomCode::executeCustomCode(const QString &jsString, const QVector<QString> &filesToProcess, const bool backupsEnabled, const bool verboseEnabled){
62
63    // Reconstruct script functions
64    for(int i=0; i<this->numThreads; i++){
65        *(jsScriptEngines[i].jsFunction) = jsScriptEngines.at(i).scriptEngine->evaluate("(function main() {"+jsString+"})");
66    }
67
68    QString currXmlFileString;
69
70    QScriptValue engineResult; // variable to check for js_errors
71    double elapsed_secs=0; // elapsed seconds that a user script took
72    clock_t begin; // seconds that a script started
73
74    // Single tread if small number of files or number of threads = 1
75    if(filesToProcess.size()<=CUSTOM_FILES_PER_THREAD || numThreads == 1){
76
77        jsCustomCodeEngine &jsEngine = getAvailableJsEngine();
78
79        // Process all XmlFiles
80        for(int i=0; i<filesToProcess.size(); i++){
81            customCodeUnwinding(filesToProcess.at(i),currXmlFileString,*jsEngine.scriptEngine,begin,elapsed_secs,engineResult,
82                                *jsEngine.jsFunction,*jsEngine.getXmlDataFunction,*jsEngine.setXmlDataFunction,backupsEnabled,verboseEnabled);
83        }
84    }
85    else{ // Multithread if there are many files
86        // Process all XmlFiles
87
88        QVector<QFuture<void>> executingThreads;
89
90        for(int i=0; i<=filesToProcess.size()-CUSTOM_FILES_PER_THREAD; i+=CUSTOM_FILES_PER_THREAD){
91
92            executingThreads <<
93            QtConcurrent::run(&this->myThreadPool, [=]()
94            {
95                mutexIsAvailable.lock();
96                jsCustomCodeEngine &jsEngine = getAvailableJsEngine();
97                jsEngine.isAvailable = false;
98                mutexIsAvailable.unlock();
99
100                QString currXmlFileStringThread;
101
102                QScriptValue engineResultThread; // variable to check for js_errors
103                double elapsedSecsThread=0; // elapsed seconds that a user script took
104                clock_t beginThread; // seconds that a script started
105
106                customCodeUnwinding(filesToProcess.at(i),currXmlFileStringThread,*jsEngine.scriptEngine,beginThread,elapsedSecsThread,engineResultThread,
107                                    *jsEngine.jsFunction,*jsEngine.getXmlDataFunction,*jsEngine.setXmlDataFunction,backupsEnabled,verboseEnabled);
108
109                customCodeUnwinding(filesToProcess.at(i+1),currXmlFileStringThread,*jsEngine.scriptEngine,beginThread,elapsedSecsThread,engineResultThread,
110                                    *jsEngine.jsFunction,*jsEngine.getXmlDataFunction,*jsEngine.setXmlDataFunction,backupsEnabled,verboseEnabled);
111
112                customCodeUnwinding(filesToProcess.at(i+2),currXmlFileStringThread,*jsEngine.scriptEngine,beginThread,elapsedSecsThread,engineResultThread,
113                                    *jsEngine.jsFunction,*jsEngine.getXmlDataFunction,*jsEngine.setXmlDataFunction,backupsEnabled,verboseEnabled);
114
115                customCodeUnwinding(filesToProcess.at(i+3),currXmlFileStringThread,*jsEngine.scriptEngine,beginThread,elapsedSecsThread,engineResultThread,
116                                    *jsEngine.jsFunction,*jsEngine.getXmlDataFunction,*jsEngine.setXmlDataFunction,backupsEnabled,verboseEnabled);
117
118                mutexIsAvailable.lock();
119                jsEngine.isAvailable = true;
120                mutexIsAvailable.unlock();
121            });
122        }
123
124        // Wait for all threads to finish
125        for(QFuture<void> &f :executingThreads){
126            f.waitForFinished();
127        }
128
129        if(filesToProcess.size()%CUSTOM_FILES_PER_THREAD!=0){
130
131            int alreadyProcessedFiles=(filesToProcess.size()/CUSTOM_FILES_PER_THREAD)*CUSTOM_FILES_PER_THREAD;
132
133            jsCustomCodeEngine &jsEngine = getAvailableJsEngine();
134
135            for(int i=alreadyProcessedFiles; i<filesToProcess.size(); i++){
136
137                customCodeUnwinding(filesToProcess.at(i),currXmlFileString,*jsEngine.scriptEngine,begin,elapsed_secs,engineResult,
138                                    *jsEngine.jsFunction,*jsEngine.getXmlDataFunction,*jsEngine.setXmlDataFunction,backupsEnabled,verboseEnabled);
139            }
140        }
141    }
142}
143
144void XmlCustomCode::displayJsException(QScriptEngine &engine, QScriptValue &engineResult){
145    if (engine.hasUncaughtException()) {
146        UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Uncaught js exception (user code) at line " +QString::number(engine.uncaughtExceptionLineNumber()) + ":\n" + engineResult.toString());
147    }
148}
149
150XmlCustomCode::jsCustomCodeEngine& XmlCustomCode::getAvailableJsEngine(){
151    for(jsCustomCodeEngine &e : this->jsScriptEngines){
152        if(e.isAvailable){
153            return e;
154        }
155    }
156
157    throw std::runtime_error("Could't find an available js engine for custom command.");
158}
Note: See TracBrowser for help on using the repository browser.