source: XmlTools2/trunk/xmlpatch.cpp@ 1060

Last change on this file since 1060 was 1055, checked in by s10k, 8 years ago

XmlTools 2.0c

File size: 18.2 KB
RevLine 
[906]1#include "xmlpatch.h"
2
[923]3XmlPatch::XmlPatch(QString patchFilesWildcard, QString forceTargetFilesWildcard, bool noBackups, bool noVerbose)
[906]4{
5 this->patchFilesToProcess=UtilXmlTools::getAllPatchFilesByWildcard(patchFilesWildcard);
6 this->forceTargetFilesWildcard=forceTargetFilesWildcard;
7 this->backupsEnabled=!noBackups;
[923]8 this->verboseEnabled=!noVerbose;
[906]9
10 if(forceTargetFilesWildcard!=""){
[935]11 std::cout << "User forced patch in the target file(s): " << forceTargetFilesWildcard.toUtf8().constData() << std::endl;
[906]12 }
13
[910]14 if(this->patchFilesToProcess.isEmpty()){
[906]15 UtilXmlTools::displayErrorMessage("Loading patch files","No .patch or .oni-patch files were found for the wildcard: "+patchFilesWildcard);
16 }
17
18}
19
20void XmlPatch::readAndProcessPatchFile(){
21
22 // Process all PatchFiles
23 for(int i=0; i<this->patchFilesToProcess.size(); i++){
24
25 QFile inputFile(this->patchFilesToProcess[i]);
26
27 if (inputFile.open(QIODevice::ReadOnly))
28 {
29
30 QTextStream fileStream(&inputFile);
31
32 checkPatchVersion(this->patchFilesToProcess[i], fileStream);
33 checkAndProcessValidCommands(fileStream);
34
35 inputFile.close();
36 }
37 else{
38 UtilXmlTools::displayErrorMessage("Read patch file", "Error opening patch file: '" + this->patchFilesToProcess[i] + "'.\n" + inputFile.errorString());
39 }
40
41 }
42
43 UtilXmlTools::displaySuccessMessage(this->patchFilesToProcess.size(),"Patch File(s)");
44
45}
46
[920]47void XmlPatch::insertNodesOperation(const QString &xmlString, XmlFilter &filters, const QString &xPathExpression, const QString &filesWildcard){
[906]48
[964]49 QVector<QString> filesToProcess;
[920]50 QList<pugi::xml_node> nodesToInsertion;
[906]51 pugi::xml_document newNode;
52 pugi::xml_parse_result result;
53
[1055]54 try{
[910]55
[1055]56 filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
[910]57
[1055]58 if(filesToProcess.isEmpty()){
59 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","No XML files were found for the wildcard: "+filesWildcard);
60 }
[906]61
[1055]62 result=newNode.load(xmlString.toUtf8().constData()); // load xml to insert
[906]63
[1055]64 if(result.status!=pugi::status_ok){
65 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", "The xml node to insert is invalid.\n" + Util::toQString(result.description()));
[920]66 }
[906]67
[1055]68 // Process all XmlFiles
69 for(int i=0; i<filesToProcess.size(); i++){
[906]70
[1055]71 UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@ADD_INSIDE_NODES");
[906]72
[1055]73 // Check how the element will be fetched via element name or xpath expression
[920]74 if(xPathExpression.isEmpty()){
[1055]75 UtilXmlTools::getAllNamedElements(this->rootNode,nodesToInsertion,filters);
[906]76 }
[920]77 else{
[1055]78 UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToInsertion);
[906]79 }
80
[1055]81 if(nodesToInsertion.isEmpty() || nodesToInsertion[0].type()==pugi::node_null){
[906]82
[1055]83 QString errMessage;
84
85 if(xPathExpression.isEmpty()){
86 errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
87 if(filters.getParentElementName()!=""){
88 errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
89 }
90 if(filters.getAttributeName()!=""){
91 errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
92 }
93 }
94 else{
95 errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
96 }
97
98 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES",errMessage);
[923]99 }
100
[1055]101 for(int j=0; j<nodesToInsertion.size(); j++){
102 for (pugi::xml_node currNodeToInsert = newNode.first_child(); currNodeToInsert; currNodeToInsert = currNodeToInsert.next_sibling())
103 {
104 nodesToInsertion[j].append_copy(currNodeToInsert); // append the new node
105 }
[906]106
[1055]107 }
[906]108
[1055]109 UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@ADD_INSIDE_NODES");
110
111 nodesToInsertion.clear();
112 }
[906]113 }
[1055]114 catch(const std::exception &e){
115 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", QString("An exception was caught: ") + e.what());
116 }
[906]117
[920]118 UtilXmlTools::displaySuccessMessage(filesToProcess.size(),"@ADD_INSIDE_NODES");
[906]119}
120
[920]121void XmlPatch::removeNodesOperation(XmlFilter &filters, const QString &xPathExpression, const QString &filesWildcard){
[906]122
[964]123 QVector<QString> filesToProcess;
[906]124
[920]125 QList<pugi::xml_node> nodesToDeletion;
[910]126
[1055]127 try{
[906]128
[1055]129 filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
[906]130
[1055]131 if(filesToProcess.isEmpty()){
132 UtilXmlTools::displayErrorMessage("@REMOVE_NODES","No XML files were found for the wildcard: "+filesWildcard);
[920]133 }
[906]134
[1055]135 // Process all XmlFiles
136 for(int i=0; i<filesToProcess.size(); i++){
[920]137
[1055]138 UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@REMOVE_NODES");
[906]139
[1055]140 // Check how the element will be fetched via element name or xpath expression
[920]141 if(xPathExpression.isEmpty()){
[1055]142 UtilXmlTools::getAllNamedElements(this->rootNode,nodesToDeletion,filters);
[906]143 }
[920]144 else{
[1055]145 UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToDeletion);
[906]146 }
147
[1055]148 if(nodesToDeletion.isEmpty() || nodesToDeletion[0].type()==pugi::node_null){
[906]149
[1055]150 QString errMessage;
[906]151
[920]152 if(xPathExpression.isEmpty()){
[1055]153 errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
[920]154 if(filters.getParentElementName()!=""){
[1055]155 errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
[920]156 }
[1055]157 if(filters.getAttributeName()!=""){
158 errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
159 }
[920]160 }
161 else{
[1055]162 errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
[920]163 }
[906]164
[920]165 UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
[906]166 }
167
[1055]168 // Delete all the specified nodes
169 for(int j=0; j<nodesToDeletion.size(); j++){
170 if(!nodesToDeletion[j].parent().remove_child(nodesToDeletion[j])){ // remove the node
[923]171
[1055]172 QString errMessage;
173 if(xPathExpression.isEmpty()){
174 errMessage = "Couldn't remove the node with Element '" + filters.getElementName() + "'";
175
176 if(filters.getParentElementName()!=""){
177 errMessage += " and a ParentElement: '" + filters.getParentElementName() + "'";
178 }
179 }
180 else{
181 errMessage = "Couldn't remove the node with the XPathExpression '" + xPathExpression + "'";
182 }
183
184 UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
185 }
186 }
187
188 UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@REMOVE_NODES");
189
190 nodesToDeletion.clear();
191 }
[906]192 }
[1055]193 catch(const std::exception &e){
194 UtilXmlTools::displayErrorMessage("@REMOVE_NODES", QString("An exception was caught: ") + e.what());
195 }
[906]196
[920]197 UtilXmlTools::displaySuccessMessage(filesToProcess.size(), "@REMOVE_NODES");
[906]198}
199
200void XmlPatch::executeCommandOperation(const QString &commandString){
201
202 // Avoid infinite fork loops
[926]203 if(commandString.contains("-p ") || commandString.contains("--patch-files ")){
[906]204 UtilXmlTools::displayErrorMessage("@COMMAND","Use of --patch-files option is not allowed inside a patch file");
205 }
206
[926]207 // Reserved to AEI
208 if(commandString.contains("--aei-patch-files-list ")){
209 UtilXmlTools::displayErrorMessage("@COMMAND","Use of --aei-patch-files-list option is not allowed inside a patch file");
[906]210 }
211
[920]212 std::cout << "@COMMAND patch operation output:\n"
[953]213 << "########################################################################"
214 << std::endl;
[906]215
[926]216 OptionsParser myParser(Util::QStringToArgsArray(commandString));
217 myParser.parse();
218
219 std::cout
[953]220 << "########################################################################"
221 << std::endl;
[926]222
[906]223 UtilXmlTools::displaySuccessMessage(1,"@COMMAND");
224}
225
226void XmlPatch::executeCustomCommandOperation(const QString &jsString, const QString &filesWildcard){
227
[964]228 QVector<QString> filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
[906]229
[1055]230 try{
231 if(filesToProcess.isEmpty()){
232 UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","No XML files were found for the wildcard: "+filesWildcard);
233 }
234
235 XmlCustomCode::getInstance()->executeCustomCode(jsString,filesToProcess,this->backupsEnabled,this->verboseEnabled);
[953]236 }
[1055]237 catch(const std::exception &e){
238 UtilXmlTools::displayErrorMessage("@CUSTOM_CODE", QString("An exception was caught: ") + e.what());
239 }
[906]240
241 UtilXmlTools::displaySuccessMessage(filesToProcess.size(), "@CUSTOM_CODE");
242}
243
244void XmlPatch::checkPatchVersion(const QString &file, QTextStream &fileStream){
245
246 QString line, patchVersion="";
247
[967]248 // First get the patch version and check its validity
[906]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
[910]279 QString line, filesWildcard;
[906]280 QString xmlToInsert, command, jsCode;
[920]281 QString xPathExpression;
[906]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 }
[920]291 else if(line.startsWith("@ADD_INSIDE_NODES")){
292 xPathExpression=getPatchParameterValue(line,"XPathExpression");
[906]293 filters.setElementName(getPatchParameterValue(line,"ElementName"));
294 filters.setParentElementName(getPatchParameterValue(line,"ParentElementName"));
295 filters.setAttributeName(getPatchParameterValue(line,"AttributeName"));
296 filters.setAttributeValue(getPatchParameterValue(line,"AttributeValue"));
[910]297
298 if(this->forceTargetFilesWildcard!=""){
299 filesWildcard=this->forceTargetFilesWildcard;
[906]300 }
301 else{
[910]302 filesWildcard=getPatchParameterValue(line,"Files");
[906]303 }
304
[920]305 // Check options
306 if(xPathExpression.isEmpty() && filters.getElementName().isEmpty()){
307 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","ElementName option or XPathExpression option is required.");
308 }
309 else if(!xPathExpression.isEmpty() && !filters.getElementName().isEmpty()){
[955]310 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","ElementName option and XPathExpression option cannot be used simultaneously.");
[920]311 }
[906]312 if(filters.getAttributeName()!="" && filters.getAttributeValue()==""){
[920]313 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","AttributeValue option is required if using AttributeName option.");
[906]314 }
315
316 if(filters.getAttributeValue()!="" && filters.getAttributeName()==""){
[920]317 UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","AttributeName option is required if using AttributeValue option.");
[906]318 }
319
320 while ( !fileStream.atEnd() && !line.startsWith("</xml>")){
321 line = fileStream.readLine();
322
323 if(!line.startsWith("<xml>") && !line.startsWith("</xml>")){
324 xmlToInsert += line + "\n";
325 }
326 }
327
[920]328 insertNodesOperation(xmlToInsert,filters,xPathExpression,filesWildcard);
[906]329
[920]330 xmlToInsert.clear();
331 filters.clear();
332 xPathExpression.clear();
333 filesWildcard.clear();
[906]334 }
[920]335 else if(line.startsWith("@REMOVE_NODES")){
[906]336
[920]337 xPathExpression=getPatchParameterValue(line,"XPathExpression");
[906]338 filters.setElementName(getPatchParameterValue(line,"ElementName"));
339 filters.setParentElementName(getPatchParameterValue(line,"ParentElementName"));
340 filters.setAttributeName(getPatchParameterValue(line,"AttributeName"));
341 filters.setAttributeValue(getPatchParameterValue(line,"AttributeValue"));
[910]342
343 if(this->forceTargetFilesWildcard!=""){
344 filesWildcard=this->forceTargetFilesWildcard;
[906]345 }
346 else{
[910]347 filesWildcard=getPatchParameterValue(line,"Files");
[906]348 }
349
[920]350 // Check options
351 if(xPathExpression.isEmpty() && filters.getElementName().isEmpty()){
352 UtilXmlTools::displayErrorMessage("@REMOVE_NODES","ElementName option or XPathExpression option is required.");
353 }
354 else if(!xPathExpression.isEmpty() && !filters.getElementName().isEmpty()){
[955]355 UtilXmlTools::displayErrorMessage("@REMOVE_NODES","ElementName option and XPathExpression option cannot be used simultaneously.");
[920]356 }
357
[906]358 if(filters.getAttributeName()!="" && filters.getAttributeValue()==""){
[920]359 UtilXmlTools::displayErrorMessage("@REMOVE_NODES","AttributeValue option is required if using AttributeName option.");
[906]360 }
361
362 if(filters.getAttributeValue()!="" && filters.getAttributeName()==""){
[920]363 UtilXmlTools::displayErrorMessage("@REMOVE_NODES","AttributeName option is required if using AttributeValue option.");
[906]364 }
365
[920]366 removeNodesOperation(filters,xPathExpression,filesWildcard);
[906]367
[920]368 filters.clear();
369 xPathExpression.clear();
370 filesWildcard.clear();
[906]371 }
372 else if(line.startsWith("@COMMAND")){
373
[926]374 command=GlobalVars::AppExecutable;
[906]375
[926]376 // Append files if forced to
377 if(!this->forceTargetFilesWildcard.isEmpty()){
378 command+=" --files '"+this->forceTargetFilesWildcard+"' ";
379 }
380
381 command+=" "+getPatchParameterValue(line,"Options");
382
383 // Add --no-backups and --no-verbose if patch was called with that arguments
384 if(!this->backupsEnabled){
[958]385 command.append(" --no-backups ");
[926]386 }
387
388 if(!this->verboseEnabled){
[958]389 command.append(" --no-verbose ");
[926]390 }
391
[920]392 command.replace("'","\""); //replace apostrophe by quotes, to avoid problems
[960]393 command.replace("\"\"","'"); // this allow to use '' as ' ('' is transformed in "" and then in ')
[920]394
[906]395 executeCommandOperation(command);
[920]396
397 command.clear();
[926]398 filesWildcard.clear();
[906]399 }
400 else if(line.startsWith("@CUSTOM_CODE")){
401
[910]402 if(this->forceTargetFilesWildcard!=""){
403 filesWildcard=this->forceTargetFilesWildcard;
[906]404 }
405 else{
[910]406 filesWildcard=getPatchParameterValue(line,"Files");
[906]407 }
408
409 while ( !fileStream.atEnd() && !line.startsWith("</code>")){
410
411 line = fileStream.readLine();
412
413 if(!line.startsWith("<code>") && !line.startsWith("</code>")){
414 jsCode += line + "\n";
415 }
416 }
417
418 executeCustomCommandOperation(jsCode,filesWildcard);
[920]419
420 jsCode.clear();
421 filesWildcard.clear();
[906]422 }
423 }
424}
425
426QString XmlPatch::getPatchParameterValue(const QString& line, QString parameter){
427
428 int startValueIdx, endValueIdx;
429
[920]430 parameter=" "+parameter+" "; // Parameters include a space before and after it's value.
[906]431
432 if(!line.contains(parameter)){
[920]433 if(parameter==" ParentElementName " || parameter==" AttributeName " || parameter==" AttributeValue "
434 || parameter==" ElementName " || parameter==" XPathExpression "){
[906]435 return ""; // not mandatory
436 }
437 parameter.remove(" "); // just remove the space added so it doesn't look weird when outputted to the user
438
[955]439 UtilXmlTools::displayErrorMessage("Read patch file parameter","Couldn't retrieve '" + parameter + "' parameter.");
[906]440 }
441
442 startValueIdx=line.indexOf(parameter); // get the position where parameter is defined
443 endValueIdx=line.indexOf("\"",startValueIdx)+1; // get the position where parameter value begins (+1 to ignore mandotory quote)
444
445 startValueIdx=endValueIdx;
446 endValueIdx=line.indexOf("\"",startValueIdx); // get the last mandatory quote of the value
447
448 return line.mid(startValueIdx,endValueIdx-startValueIdx); // return the value of the parameter (is in the middle of the mandatory quotes)
449}
Note: See TracBrowser for help on using the repository browser.