1 | #include "xmlcustomcode.h"
|
---|
2 |
|
---|
3 | XmlCustomCode* XmlCustomCode::uniqueInstance = NULL;
|
---|
4 |
|
---|
5 | QScriptValue echo(QScriptContext *context, QScriptEngine*)
|
---|
6 | {
|
---|
7 | std::cout << context->argument(0).toString().toUtf8().constData() << std::endl;
|
---|
8 |
|
---|
9 | return "";
|
---|
10 | }
|
---|
11 |
|
---|
12 | XmlCustomCode::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 |
|
---|
52 | XmlCustomCode* XmlCustomCode::getInstance(){
|
---|
53 |
|
---|
54 | if (uniqueInstance==NULL){ // allow only one instance
|
---|
55 | uniqueInstance = new XmlCustomCode();
|
---|
56 | }
|
---|
57 |
|
---|
58 | return uniqueInstance;
|
---|
59 | }
|
---|
60 |
|
---|
61 | void 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 |
|
---|
144 | void 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 |
|
---|
150 | XmlCustomCode::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 | }
|
---|