source: XmlTools2/trunk/XmlTools/xmlpatch.cpp@ 906

Last change on this file since 906 was 905, checked in by s10k, 11 years ago

XmlTools v2

File size: 15.2 KB
Line 
1#include "xmlpatch.h"
2
3XmlPatch::XmlPatch(QString patchFilesWildcard, QString forceTargetFilesWildcard, bool noBackups)
4{
5 this->patchFilesToProcess=UtilXmlTools::getAllPatchFilesByWildcard(patchFilesWildcard);
6 this->forceTargetFilesWildcard=forceTargetFilesWildcard;
7 this->backupsEnabled=!noBackups;
8
9 if(forceTargetFilesWildcard!=""){
10 std::cout << "User forced patch in the target file(s): " << this->forceTargetFilesWildcard.toLatin1().constData() << std::endl;
11 }
12
13 if(this->patchFilesToProcess.size()==0){
14 UtilXmlTools::displayErrorMessage("Loading patch files","No .patch or .oni-patch files were found for the wildcard: "+patchFilesWildcard);
15 }
16
17}
18
19void XmlPatch::readAndProcessPatchFile(){
20
21 // Process all PatchFiles
22 for(int i=0; i<this->patchFilesToProcess.size(); i++){
23
24 QFile inputFile(this->patchFilesToProcess[i]);
25
26 if (inputFile.open(QIODevice::ReadOnly))
27 {
28
29 QTextStream fileStream(&inputFile);
30
31 checkPatchVersion(this->patchFilesToProcess[i], fileStream);
32 checkAndProcessValidCommands(fileStream);
33
34 inputFile.close();
35 }
36 else{
37 UtilXmlTools::displayErrorMessage("Read patch file", "Error opening patch file: '" + this->patchFilesToProcess[i] + "'.\n" + inputFile.errorString());
38 }
39
40 }
41
42 UtilXmlTools::displaySuccessMessage(this->patchFilesToProcess.size(),"Patch File(s)");
43
44}
45
46void XmlPatch::addToOperation(const QString &xmlString, XmlFilter &filters, const QString &filesWildcard){
47
48 QStringList filesToProcess;
49
50 filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
51
52 pugi::xml_node nodeToInsertion;
53 pugi::xml_document newNode;
54 pugi::xml_parse_result result;
55
56 result=newNode.load(xmlString.toLatin1().constData()); // load xml to insert
57
58 if(result.status!=pugi::status_ok){
59 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODE", "The xml node to insert is invalid.\n" + Util::toQString(result.description()));
60 }
61
62 // Process all XmlFiles
63 for(int i=0; i<filesToProcess.size(); i++){
64
65 UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,"@ADD_INSIDE_NODE");
66
67 nodeToInsertion=UtilXmlTools::getFirstNamedElements(this->rootNode,filters);
68
69 if(nodeToInsertion.type()==pugi::node_null){
70
71 QString errMessage;
72
73 errMessage = "It wasn't found a node with a ElementName: '" + filters.getElementName() + "'";
74 if(filters.getParentElementName()!=""){
75 errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
76 }
77 if(filters.getAttributeName()!=""){
78 errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
79 }
80
81 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODE",errMessage);
82 }
83
84 nodeToInsertion.prepend_copy(newNode.first_child()); // append the new node
85
86
87 UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@ADD_INSIDE_NODE");
88 }
89
90 UtilXmlTools::displaySuccessMessage(filesToProcess.size(),"@ADD_INSIDE_NODE");
91}
92
93void XmlPatch::removeNodeOperation(XmlFilter &filters, const QString &filesWildcard){
94
95 QStringList filesToProcess;
96
97 filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
98
99 pugi::xml_node nodeToDeletion;
100 pugi::xml_parse_result result;
101
102 // Process all XmlFiles
103 for(int i=0; i<filesToProcess.size(); i++){
104
105 UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,"@REMOVE_NODE");
106
107 nodeToDeletion=UtilXmlTools::getFirstNamedElements(this->rootNode,filters);
108
109 if(nodeToDeletion.type()==pugi::node_null){
110 QString errMessage;
111
112 errMessage = "It wasn't found a node with a ElementName: '" + filters.getElementName() + "'";
113 if(filters.getParentElementName()!=""){
114 errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
115 }
116 if(filters.getAttributeName()!=""){
117 errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
118 }
119
120 UtilXmlTools::displayErrorMessage("@REMOVE_NODE",errMessage);
121 }
122
123 if(!nodeToDeletion.parent().remove_child(nodeToDeletion)){ // remove the node
124
125 QString errMessage;
126
127 errMessage = "Couldn't remove the node with Element '" + filters.getElementName() + "'";
128
129 if(filters.getParentElementName()!=""){
130 errMessage += " and a ParentElement: '" + filters.getParentElementName() + "'";
131 }
132
133 UtilXmlTools::displayErrorMessage("@REMOVE_NODE",errMessage);
134 }
135
136 UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@REMOVE_NODE");
137 }
138
139 UtilXmlTools::displaySuccessMessage(filesToProcess.size(), "@REMOVE_NODE");
140}
141
142void XmlPatch::executeCommandOperation(const QString &commandString){
143
144 QProcess newXmlToolsInstance;
145 QString resultOutput;
146
147 // Avoid infinite fork loops
148 if(commandString.contains("-p") || commandString.contains("--patch-files")){
149 UtilXmlTools::displayErrorMessage("@COMMAND","Use of --patch-files option is not allowed inside a patch file");
150 }
151
152 newXmlToolsInstance.start(GlobalVars::AppName + " " + commandString);
153 newXmlToolsInstance.waitForFinished(-1); // wait for new instance to finish
154
155 resultOutput=newXmlToolsInstance.readAll();
156
157 if(newXmlToolsInstance.exitCode()!=0){
158 UtilXmlTools::displayErrorMessage("@COMMAND", "An error ocurred:\n" + resultOutput);
159 exit(1);
160 }
161
162 std::cout << "@COMMAND patch operation output:\n" << resultOutput.toLatin1().constData() << std::endl;
163
164 UtilXmlTools::displaySuccessMessage(1,"@COMMAND");
165}
166
167void XmlPatch::executeCustomCommandOperation(const QString &jsString, const QString &filesWildcard){
168
169 QStringList filesToProcess;
170#ifdef _USE_OLD_JS_ENGINE
171 QScriptEngine engine;
172 QScriptValue engineResult; // variable to check for js_errors
173#else
174 QJSEngine engine;
175 QJSValue engineResult; // variable to check for js_errors
176#endif
177
178 QString rexmlString, jsxmlString, currXmlFileString;
179
180
181 QFile rexmlfile(":/resources/libs/rexml.js");
182 QFile jsxmlfile(":/resources/libs/jsxml.js");
183
184 filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
185
186 rexmlfile.open(QFile::ReadOnly | QFile::Text);
187 jsxmlfile.open(QFile::ReadOnly | QFile::Text);
188
189 rexmlString=QTextStream(&rexmlfile).readAll();
190 jsxmlString=QTextStream(&jsxmlfile).readAll();
191
192 engine.evaluate(rexmlString); // load js libraries
193 engine.evaluate(jsxmlString);
194
195 // Process all XmlFiles
196 for(int i=0; i<filesToProcess.size(); i++){
197
198 if(this->backupsEnabled){
199 UtilXmlTools::backupFile(filesToProcess[i]);
200 }
201
202 QFile currXmlFile(filesToProcess[i]);
203
204 if(!currXmlFile.open(QFile::ReadOnly | QFile::Text)){
205 UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Error loading '" + filesToProcess[i] + "' file for read operation.");
206 }
207
208 currXmlFileString=QTextStream(&currXmlFile).readAll();
209
210 currXmlFile.close(); // close reading
211
212 engine.globalObject().setProperty("$xmlData",currXmlFileString);
213
214 engineResult=engine.evaluate(jsString);
215
216#ifdef _USE_OLD_JS_ENGINE
217 if (engine.hasUncaughtException()) {
218 displayJsException(engine,engineResult);
219 }
220#else
221 checkForJsException(engineResult);
222#endif
223
224 if(!currXmlFile.open(QFile::WriteOnly | QFile::Text | QIODevice::Truncate)){
225 UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Error loading '" + filesToProcess[i] + "' file for @CUSTOM_CODE write operation.");
226 }
227
228 engineResult=engine.globalObject().property("$xmlData");
229
230#ifdef _USE_OLD_JS_ENGINE
231 if (engine.hasUncaughtException()) {
232 displayJsException(engine,engineResult);
233 }
234#else
235 checkForJsException(engineResult);
236#endif
237
238 QTextStream(&currXmlFile) << engineResult.toString(); // retreive the modified xml by javascript and save it to the file
239 }
240
241 UtilXmlTools::displaySuccessMessage(filesToProcess.size(), "@CUSTOM_CODE");
242}
243
244void XmlPatch::checkPatchVersion(const QString &file, QTextStream &fileStream){
245
246 QString line, patchVersion="";
247
248 // First get the patch version and check it validity
249 while ( !fileStream.atEnd() ){
250 line = fileStream.readLine();
251
252 if(line.startsWith('#')){ // Ignore comments
253 continue;
254 }
255 else if(line.startsWith("@XML_TOOLS")){
256
257 patchVersion=getPatchParameterValue(line,"Version");
258
259 if(!patchVersion.startsWith("2.0")){
260 QString errMessage;
261
262 errMessage = "The current patch version is incompatible with this XmlTools version:\n";
263 errMessage += "Patch file name: '" + file + "'\n";
264 errMessage += "XmlTools version: " + GlobalVars::AppVersion + "\n" + "CurrPatch version: " + patchVersion + "";
265 UtilXmlTools::displayErrorMessage("@XML_TOOLS",errMessage);
266 }
267 break; // We have got what we wanted
268 }
269 }
270
271 if(patchVersion==""){
272 UtilXmlTools::displayErrorMessage("@XML_TOOLS","Patch version not found.");
273 }
274
275}
276
277void XmlPatch::checkAndProcessValidCommands(QTextStream &fileStream){
278
279 QString line;
280 QString filesWildcard;
281 QString xmlToInsert, command, jsCode;
282 XmlFilter filters;
283
284 // Process the rest of the commands in patch file
285 while ( !fileStream.atEnd() ){
286 line = fileStream.readLine();
287
288 if(line.startsWith('#')){ // Ignore comments
289 continue;
290 }
291 else if(line.startsWith("@ADD_INSIDE_NODE")){
292 filters.setElementName(getPatchParameterValue(line,"ElementName"));
293 filters.setParentElementName(getPatchParameterValue(line,"ParentElementName"));
294 filters.setAttributeName(getPatchParameterValue(line,"AttributeName"));
295 filters.setAttributeValue(getPatchParameterValue(line,"AttributeValue"));
296 if(this->forceTargetFilesWildcard==""){
297 filesWildcard=getPatchParameterValue(line,"Files");
298 }
299 else{
300 filesWildcard=this->forceTargetFilesWildcard;
301 }
302
303 // Check attribute filters
304 if(filters.getAttributeName()!="" && filters.getAttributeValue()==""){
305 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODE","attribute-value option is required if using attribute-name option.");
306 }
307
308 if(filters.getAttributeValue()!="" && filters.getAttributeName()==""){
309 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODE","attribute-name option is required if using attribute-value option.");
310 }
311
312 while ( !fileStream.atEnd() && !line.startsWith("</xml>")){
313 line = fileStream.readLine();
314
315 if(!line.startsWith("<xml>") && !line.startsWith("</xml>")){
316 xmlToInsert += line + "\n";
317 }
318 }
319
320 addToOperation(xmlToInsert,filters,filesWildcard);
321
322 }
323 else if(line.startsWith("@REMOVE_NODE")){
324
325 filters.setElementName(getPatchParameterValue(line,"ElementName"));
326 filters.setParentElementName(getPatchParameterValue(line,"ParentElementName"));
327 filters.setAttributeName(getPatchParameterValue(line,"AttributeName"));
328 filters.setAttributeValue(getPatchParameterValue(line,"AttributeValue"));
329 if(this->forceTargetFilesWildcard==""){
330 filesWildcard=getPatchParameterValue(line,"Files");
331 }
332 else{
333 filesWildcard=this->forceTargetFilesWildcard;
334 }
335
336 // Check attribute filters
337 if(filters.getAttributeName()!="" && filters.getAttributeValue()==""){
338 UtilXmlTools::displayErrorMessage("@REMOVE_NODE","attribute-value option is required if using attribute-name option.");
339 }
340
341 if(filters.getAttributeValue()!="" && filters.getAttributeName()==""){
342 UtilXmlTools::displayErrorMessage("@REMOVE_NODE","attribute-name option is required if using attribute-value option.");
343 }
344
345 removeNodeOperation(filters,filesWildcard);
346
347 }
348 else if(line.startsWith("@COMMAND")){
349
350 command=getPatchParameterValue(line,"");
351
352 executeCommandOperation(command);
353 }
354 else if(line.startsWith("@CUSTOM_CODE")){
355
356 if(this->forceTargetFilesWildcard==""){
357 filesWildcard=getPatchParameterValue(line,"Files");
358 }
359 else{
360 filesWildcard=this->forceTargetFilesWildcard;
361 }
362
363 while ( !fileStream.atEnd() && !line.startsWith("</code>")){
364
365 line = fileStream.readLine();
366
367 if(!line.startsWith("<code>") && !line.startsWith("</code>")){
368 jsCode += line + "\n";
369 }
370 }
371
372 executeCustomCommandOperation(jsCode,filesWildcard);
373 }
374 }
375}
376
377QString XmlPatch::getPatchParameterValue(const QString& line, QString parameter){
378
379 int startValueIdx, endValueIdx;
380
381 parameter+=" "; // Parameters include a space before it's value.
382
383 if(!line.contains(parameter)){
384 if(parameter=="ParentElementName " || parameter=="AttributeName " || parameter=="AttributeValue "){
385 return ""; // not mandatory
386 }
387 parameter.remove(" "); // just remove the space added so it doesn't look weird when outputted to the user
388
389 UtilXmlTools::displayErrorMessage("Read patch file parameter","Couldn't retreive '" + parameter + "' parameter.");
390 }
391
392 startValueIdx=line.indexOf(parameter); // get the position where parameter is defined
393 endValueIdx=line.indexOf("\"",startValueIdx)+1; // get the position where parameter value begins (+1 to ignore mandotory quote)
394
395 startValueIdx=endValueIdx;
396 endValueIdx=line.indexOf("\"",startValueIdx); // get the last mandatory quote of the value
397
398 return line.mid(startValueIdx,endValueIdx-startValueIdx); // return the value of the parameter (is in the middle of the mandatory quotes)
399}
400
401#ifdef _USE_OLD_JS_ENGINE
402void XmlPatch::displayJsException(QScriptEngine &engine, QScriptValue &engineResult){
403 if (engine.hasUncaughtException()) {
404 UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Uncaught js exception (user code) at line " +QString::number(engine.uncaughtExceptionLineNumber()) + ":\n" + engineResult.toString());
405 }
406}
407#else
408void XmlPatch::checkForJsException(QJSValue &engineResult){
409 if (engineResult.isError()) {
410 UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","Uncaught js exception (user code):\n" + engineResult.toString());
411 }
412}
413#endif
Note: See TracBrowser for help on using the repository browser.