| 1 | #include "utilxmltools.h" | 
 
 
 
 
 | 2 |  | 
 
 
 
 
 | 3 | namespace UtilXmlTools{ | 
 
 
 
 
 | 4 |  | 
 
 
 
 
 | 5 | // As this will not likely be modified we use QVector instead of QList since it is more cache friendly | 
 
 
 
 
 | 6 | QVector<QString> getAllXmlFilesByWildcard(const QString &wildcard){ | 
 
 
 
 
 | 7 | QStringList validFilesMatching; | 
 
 
 
 
 | 8 | QStringList filesMatching; | 
 
 
 
 
 | 9 |  | 
 
 
 
 
 | 10 | // Get all files matching the wildcard | 
 
 
 
 
 | 11 |  | 
 
 
 
 
 | 12 | filesMatching=UtilXmlTools::getAllFilesByWildcard(wildcard); | 
 
 
 
 
 | 13 |  | 
 
 
 
 
 | 14 | // Check if all are XmlFiles, only return valid XmlFiles | 
 
 
 
 
 | 15 |  | 
 
 
 
 
 | 16 | for(int i=0; i<filesMatching.size(); i++){ | 
 
 
 
 
 | 17 | if(filesMatching[i].endsWith(".xml",Qt::CaseInsensitive)){ | 
 
 
 
 
 | 18 | validFilesMatching << filesMatching[i]; | 
 
 
 
 
 | 19 | } | 
 
 
 
 
 | 20 | } | 
 
 
 
 
 | 21 |  | 
 
 
 
 
 | 22 | return validFilesMatching.toVector(); | 
 
 
 
 
 | 23 |  | 
 
 
 
 
 | 24 | } | 
 
 
 
 
 | 25 |  | 
 
 
 
 
 | 26 | QVector<QString> getAllPatchFilesByWildcard(const QString &wildcard){ | 
 
 
 
 
 | 27 | QStringList validFilesMatching; | 
 
 
 
 
 | 28 | QStringList filesMatching; | 
 
 
 
 
 | 29 |  | 
 
 
 
 
 | 30 | // Get all files matching the wildcard | 
 
 
 
 
 | 31 |  | 
 
 
 
 
 | 32 | filesMatching=UtilXmlTools::getAllFilesByWildcard(wildcard); | 
 
 
 
 
 | 33 |  | 
 
 
 
 
 | 34 | // Check if all are PatchFiles, only return valid PatchFiles | 
 
 
 
 
 | 35 |  | 
 
 
 
 
 | 36 | for(int i=0; i<filesMatching.size(); i++){ | 
 
 
 
 
 | 37 | if(filesMatching[i].endsWith(".patch",Qt::CaseInsensitive) || filesMatching[i].endsWith(".oni-patch",Qt::CaseInsensitive)){ | 
 
 
 
 
 | 38 | validFilesMatching << filesMatching[i]; | 
 
 
 
 
 | 39 | } | 
 
 
 
 
 | 40 | } | 
 
 
 
 
 | 41 |  | 
 
 
 
 
 | 42 | return validFilesMatching.toVector(); | 
 
 
 
 
 | 43 |  | 
 
 
 
 
 | 44 | } | 
 
 
 
 
 | 45 |  | 
 
 
 
 
 | 46 | void backupFile(const QString &file, bool verboseEnabled){ | 
 
 
 
 
 | 47 | if(!QFile::exists(file+".bak")){ | 
 
 
 
 
 | 48 | if(!Util::FileSystem::backupFile(file)){ | 
 
 
 
 
 | 49 | std::cerr << "Couldn't back up file '" << file.toUtf8().constData() << "'. Aborting." << std::endl; | 
 
 
 
 
 | 50 | exit(1); | 
 
 
 
 
 | 51 | } | 
 
 
 
 
 | 52 | } | 
 
 
 
 
 | 53 | else{ | 
 
 
 
 
 | 54 | if(verboseEnabled){ | 
 
 
 
 
 | 55 | std::cout << "Backup file '" << file.toUtf8().constData() << "'' already exists. Skipping..." << std::endl; | 
 
 
 
 
 | 56 | } | 
 
 
 
 
 | 57 | } | 
 
 
 
 
 | 58 | } | 
 
 
 
 
 | 59 |  | 
 
 
 
 
 | 60 | void getAllXpathElements(const QString &xPathExpression, pugi::xml_document &doc, QList<pugi::xml_node> &result){ | 
 
 
 
 
 | 61 |  | 
 
 
 
 
 | 62 | pugi::xpath_node_set selectedNodes; | 
 
 
 
 
 | 63 | pugi::xpath_node node; | 
 
 
 
 
 | 64 |  | 
 
 
 
 
 | 65 | try | 
 
 
 
 
 | 66 | { | 
 
 
 
 
 | 67 | selectedNodes = doc.select_nodes(xPathExpression.toUtf8().constData()); | 
 
 
 
 
 | 68 | } | 
 
 
 
 
 | 69 |  | 
 
 
 
 
 | 70 | catch (const pugi::xpath_exception& e) | 
 
 
 
 
 | 71 | { | 
 
 
 
 
 | 72 | displayErrorMessage("XPath element selection","Selection of elements using the XPathExpression: '" + xPathExpression + "' failed:\n" + e.what()); | 
 
 
 
 
 | 73 | } | 
 
 
 
 
 | 74 |  | 
 
 
 
 
 | 75 | for (pugi::xpath_node_set::const_iterator currNode = selectedNodes.begin(); currNode != selectedNodes.end(); ++currNode) | 
 
 
 
 
 | 76 | { | 
 
 
 
 
 | 77 | node = *currNode; | 
 
 
 
 
 | 78 | if(node){ // if node != null | 
 
 
 
 
 | 79 | result << node.node(); | 
 
 
 
 
 | 80 | } | 
 
 
 
 
 | 81 | } | 
 
 
 
 
 | 82 |  | 
 
 
 
 
 | 83 | if(result.isEmpty()){ | 
 
 
 
 
 | 84 | result << pugi::xml_node(); // add an empty node if none found | 
 
 
 
 
 | 85 | } | 
 
 
 
 
 | 86 |  | 
 
 
 
 
 | 87 | } | 
 
 
 
 
 | 88 |  | 
 
 
 
 
 | 89 | pugi::xml_node getFirstXpathElement(const QString &xPathExpression, pugi::xml_document &doc){ | 
 
 
 
 
 | 90 | pugi::xpath_node selectedNode; | 
 
 
 
 
 | 91 |  | 
 
 
 
 
 | 92 | try | 
 
 
 
 
 | 93 | { | 
 
 
 
 
 | 94 | selectedNode = doc.select_single_node(xPathExpression.toUtf8().constData()); | 
 
 
 
 
 | 95 | } | 
 
 
 
 
 | 96 |  | 
 
 
 
 
 | 97 | catch (const pugi::xpath_exception& e) | 
 
 
 
 
 | 98 | { | 
 
 
 
 
 | 99 | displayErrorMessage("XPath element selection","Selection of element using the XPathExpression: '" + xPathExpression + "' failed:\n" + e.what()); | 
 
 
 
 
 | 100 | } | 
 
 
 
 
 | 101 |  | 
 
 
 
 
 | 102 | return selectedNode.node(); | 
 
 
 
 
 | 103 | } | 
 
 
 
 
 | 104 |  | 
 
 
 
 
 | 105 | void getAllNamedElements(pugi::xml_node &node, QList<pugi::xml_node> &result, XmlFilter &filters){ | 
 
 
 
 
 | 106 | for (pugi::xml_node_iterator currNode = node.begin(); currNode != node.end(); ++currNode) | 
 
 
 
 
 | 107 | { | 
 
 
 
 
 | 108 |  | 
 
 
 
 
 | 109 | if ((*currNode).name() == filters.getElementName() && (filters.getParentElementName() == "" || filters.getParentElementName() == (*currNode).parent().name()) | 
 
 
 
 
 | 110 | && (filters.getAttributeName() == "" || | 
 
 
 
 
 | 111 | QString((*currNode).attribute(filters.getAttributeName().toUtf8().constData()).value()) == filters.getAttributeValue()) ){ // Seems node attribute must be converted to qtsring to remove \r\n needs to check in future! | 
 
 
 
 
 | 112 |  | 
 
 
 
 
 | 113 | result << *currNode; | 
 
 
 
 
 | 114 | continue; | 
 
 
 
 
 | 115 | } | 
 
 
 
 
 | 116 | getAllNamedElements(*currNode,result,filters); | 
 
 
 
 
 | 117 | } | 
 
 
 
 
 | 118 | } | 
 
 
 
 
 | 119 |  | 
 
 
 
 
 | 120 | pugi::xml_node getFirstNamedElement(pugi::xml_node &node, XmlFilter &filters){ | 
 
 
 
 
 | 121 |  | 
 
 
 
 
 | 122 | pugi::xml_node foundNode; | 
 
 
 
 
 | 123 |  | 
 
 
 
 
 | 124 | for (pugi::xml_node_iterator currNode = node.begin(); currNode != node.end(); ++currNode) | 
 
 
 
 
 | 125 | { | 
 
 
 
 
 | 126 | if ((*currNode).name() == filters.getElementName() && (filters.getParentElementName() == "" || filters.getParentElementName() == (*currNode).parent().name()) | 
 
 
 
 
 | 127 | && (filters.getAttributeName() == "" || | 
 
 
 
 
 | 128 | QString((*currNode).attribute(filters.getAttributeName().toUtf8().constData()).value()) == filters.getAttributeValue()) ){ | 
 
 
 
 
 | 129 | return *currNode; | 
 
 
 
 
 | 130 | } | 
 
 
 
 
 | 131 |  | 
 
 
 
 
 | 132 | foundNode=getFirstNamedElement(*currNode,filters); | 
 
 
 
 
 | 133 |  | 
 
 
 
 
 | 134 | if(foundNode.type()!=pugi::node_null){ | 
 
 
 
 
 | 135 | return foundNode; | 
 
 
 
 
 | 136 | } | 
 
 
 
 
 | 137 |  | 
 
 
 
 
 | 138 | } | 
 
 
 
 
 | 139 |  | 
 
 
 
 
 | 140 | return foundNode; | 
 
 
 
 
 | 141 | } | 
 
 
 
 
 | 142 |  | 
 
 
 
 
 | 143 | void displaySuccessMessage(const int numberOperations, const QString &operation){ | 
 
 
 
 
 | 144 | std::cout << "------------------------------------------------------------------------" << std::endl; | 
 
 
 
 
 | 145 | std::cout << numberOperations << " " << operation.toUtf8().constData() << " operation(s) completed with success!" << std::endl; | 
 
 
 
 
 | 146 | std::cout << "------------------------------------------------------------------------" << std::endl; | 
 
 
 
 
 | 147 | } | 
 
 
 
 
 | 148 |  | 
 
 
 
 
 | 149 | void displayErrorMessage(const QString& operation, const QString &message, bool exitProgram){ | 
 
 
 
 
 | 150 | std::cerr << "************************************************************************" << std::endl; | 
 
 
 
 
 | 151 | std::cerr << operation.toUtf8().constData() << " operation failed!" << std::endl << std::endl; | 
 
 
 
 
 | 152 | std::cerr << message.toUtf8().constData() << std::endl << std::endl; | 
 
 
 
 
 | 153 | if(exitProgram) std::cerr << "Aborting..." << std::endl; | 
 
 
 
 
 | 154 | std::cerr << "************************************************************************" << std::endl; | 
 
 
 
 
 | 155 | if(exitProgram) exit(1); | 
 
 
 
 
 | 156 | } | 
 
 
 
 
 | 157 |  | 
 
 
 
 
 | 158 | QStringList qStringListFromSpacedString(const QString &mySpacedString){ | 
 
 
 
 
 | 159 | return mySpacedString.split(" "); | 
 
 
 
 
 | 160 | } | 
 
 
 
 
 | 161 |  | 
 
 
 
 
 | 162 | QList<int> qListIntFromSpacedString(const QString &mySpacedString){ | 
 
 
 
 
 | 163 | QStringList stringList; | 
 
 
 
 
 | 164 | QList<int> intList; | 
 
 
 
 
 | 165 |  | 
 
 
 
 
 | 166 | stringList = mySpacedString.split(" "); | 
 
 
 
 
 | 167 |  | 
 
 
 
 
 | 168 | foreach(QString value, stringList){ | 
 
 
 
 
 | 169 | bool ok; | 
 
 
 
 
 | 170 |  | 
 
 
 
 
 | 171 | intList << value.toInt(&ok); | 
 
 
 
 
 | 172 |  | 
 
 
 
 
 | 173 | if(!ok){ | 
 
 
 
 
 | 174 | throw std::runtime_error(QString("Impossible to convert string '" + value + "' to int!").toUtf8().constData()); | 
 
 
 
 
 | 175 | } | 
 
 
 
 
 | 176 | } | 
 
 
 
 
 | 177 |  | 
 
 
 
 
 | 178 | return intList; | 
 
 
 
 
 | 179 | } | 
 
 
 
 
 | 180 |  | 
 
 
 
 
 | 181 | QList<double> qListDoubleFromSpacedString(const QString &mySpacedString){ | 
 
 
 
 
 | 182 | QStringList stringList; | 
 
 
 
 
 | 183 | QList<double> doubleList; | 
 
 
 
 
 | 184 |  | 
 
 
 
 
 | 185 | stringList = mySpacedString.split(" "); | 
 
 
 
 
 | 186 |  | 
 
 
 
 
 | 187 | foreach(QString value, stringList){ | 
 
 
 
 
 | 188 | bool ok; | 
 
 
 
 
 | 189 |  | 
 
 
 
 
 | 190 | doubleList << value.toDouble(&ok); | 
 
 
 
 
 | 191 |  | 
 
 
 
 
 | 192 | if(!ok){ | 
 
 
 
 
 | 193 | throw std::runtime_error(QString("Impossible to convert string '" + value + "' to double!").toUtf8().constData()); | 
 
 
 
 
 | 194 | } | 
 
 
 
 
 | 195 | } | 
 
 
 
 
 | 196 |  | 
 
 
 
 
 | 197 | return doubleList; | 
 
 
 
 
 | 198 | } | 
 
 
 
 
 | 199 |  | 
 
 
 
 
 | 200 | void loadXmlFile(const QString &file, pugi::xml_document &document, pugi::xml_node &rootNode, bool backupsEnabled, bool verboseEnabled, const QString &operationForErrorMessage){ | 
 
 
 
 
 | 201 |  | 
 
 
 
 
 | 202 | pugi::xml_parse_result result = document.load_file(file.toUtf8().constData()); | 
 
 
 
 
 | 203 | rootNode=document.root(); | 
 
 
 
 
 | 204 |  | 
 
 
 
 
 | 205 | if(result.status==pugi::status_ok){ | 
 
 
 
 
 | 206 | if(verboseEnabled){ | 
 
 
 
 
 | 207 | std::cout << "File '" << file.toUtf8().constData() << "' loaded with sucess." << std::endl; | 
 
 
 
 
 | 208 | } | 
 
 
 
 
 | 209 | } | 
 
 
 
 
 | 210 | else{ | 
 
 
 
 
 | 211 | UtilXmlTools::displayErrorMessage(operationForErrorMessage,"An error ocurred while loading '" +file + "' XML file\n" + result.description()); | 
 
 
 
 
 | 212 | } | 
 
 
 
 
 | 213 |  | 
 
 
 
 
 | 214 | if(backupsEnabled){ | 
 
 
 
 
 | 215 | UtilXmlTools::backupFile(file,verboseEnabled); // bake a backup of the file. | 
 
 
 
 
 | 216 | } | 
 
 
 
 
 | 217 |  | 
 
 
 
 
 | 218 | } | 
 
 
 
 
 | 219 |  | 
 
 
 
 
 | 220 | void saveXmlFile(const QString &file, pugi::xml_document &document, const QString &operationForErrorMessage){ | 
 
 
 
 
 | 221 | if(!document.save_file(file.toUtf8().constData(), "\t", pugi::format_indent | pugi::format_save_file_text | pugi::format_no_escapes)){ // output as the system new lines ending | 
 
 
 
 
 | 222 | UtilXmlTools::displayErrorMessage(operationForErrorMessage,"An error ocurred while saving '" + file + "' XML file"); | 
 
 
 
 
 | 223 | } | 
 
 
 
 
 | 224 | } | 
 
 
 
 
 | 225 |  | 
 
 
 
 
 | 226 | //TODO Needs optimization | 
 
 
 
 
 | 227 | QStringList QStringToArgsArray(const QString &args){ | 
 
 
 
 
 | 228 | QStringList result; | 
 
 
 
 
 | 229 | int startIdx=0, endIdx=0; | 
 
 
 
 
 | 230 |  | 
 
 
 
 
 | 231 | if(!args.isEmpty()){ // if non empty arguments | 
 
 
 
 
 | 232 |  | 
 
 
 
 
 | 233 | while(endIdx<args.length()){ | 
 
 
 
 
 | 234 |  | 
 
 
 
 
 | 235 | startIdx=endIdx; | 
 
 
 
 
 | 236 |  | 
 
 
 
 
 | 237 | if(args.at(startIdx)==' '){ // Ignore spaces until a different char is found | 
 
 
 
 
 | 238 | endIdx++; | 
 
 
 
 
 | 239 | continue; | 
 
 
 
 
 | 240 | } | 
 
 
 
 
 | 241 | else if(args.at(startIdx)!='"'){ // if first character is different from quote it ends with space | 
 
 
 
 
 | 242 | endIdx=args.indexOf(' ',startIdx+1); | 
 
 
 
 
 | 243 | } | 
 
 
 
 
 | 244 | else{ // If is a quote try to get the all the string until next quote | 
 
 
 
 
 | 245 | endIdx=args.indexOf('"',startIdx+1); | 
 
 
 
 
 | 246 | } | 
 
 
 
 
 | 247 |  | 
 
 
 
 
 | 248 | if(endIdx==-1) break; | 
 
 
 
 
 | 249 |  | 
 
 
 
 
 | 250 | endIdx++; | 
 
 
 
 
 | 251 |  | 
 
 
 
 
 | 252 | result << args.mid(startIdx,endIdx-startIdx).replace("\"","").trimmed(); // remove quotes | 
 
 
 
 
 | 253 | } | 
 
 
 
 
 | 254 |  | 
 
 
 
 
 | 255 | } | 
 
 
 
 
 | 256 |  | 
 
 
 
 
 | 257 | return result; | 
 
 
 
 
 | 258 | } | 
 
 
 
 
 | 259 |  | 
 
 
 
 
 | 260 | // TODO: Replace later with a more generic function and put in util library | 
 
 
 
 
 | 261 | // Supports wildcards, and directory with wildcard e.g.: | 
 
 
 
 
 | 262 | // *.xml | 
 
 
 
 
 | 263 | // C:/myXmls/*.xml | 
 
 
 
 
 | 264 | QStringList getAllFilesByWildcard(const QString &wildcard){ | 
 
 
 
 
 | 265 |  | 
 
 
 
 
 | 266 | QString pathNormalized; | 
 
 
 
 
 | 267 | QStringList nameWildCard; // entryList requires a QStringList | 
 
 
 
 
 | 268 | QStringList resultFiles; // result files with absolute path | 
 
 
 
 
 | 269 | int endOfPathIdx; | 
 
 
 
 
 | 270 | QDir directory; | 
 
 
 
 
 | 271 |  | 
 
 
 
 
 | 272 | if(wildcard==""){ | 
 
 
 
 
 | 273 | std::cout << "You need to specify a wildcard! Aborting..." << std::endl; | 
 
 
 
 
 | 274 | exit(1); | 
 
 
 
 
 | 275 | } | 
 
 
 
 
 | 276 |  | 
 
 
 
 
 | 277 | pathNormalized=Util::FileSystem::normalizePath(wildcard); // Convert slashes to work in both mac and windows | 
 
 
 
 
 | 278 |  | 
 
 
 
 
 | 279 | if(pathNormalized.contains("/")){ // If contains full path | 
 
 
 
 
 | 280 | endOfPathIdx=pathNormalized.lastIndexOf("/"); // get last slash | 
 
 
 
 
 | 281 |  | 
 
 
 
 
 | 282 | nameWildCard.append(pathNormalized.right(pathNormalized.size()-1-endOfPathIdx)); // get the names wildcard // -1 because starts with zeo | 
 
 
 
 
 | 283 |  | 
 
 
 
 
 | 284 | pathNormalized=pathNormalized.left(endOfPathIdx); // get the complete path | 
 
 
 
 
 | 285 |  | 
 
 
 
 
 | 286 | directory=QDir(pathNormalized); | 
 
 
 
 
 | 287 | } | 
 
 
 
 
 | 288 | else{ // if relative | 
 
 
 
 
 | 289 | nameWildCard << wildcard; | 
 
 
 
 
 | 290 | } | 
 
 
 
 
 | 291 |  | 
 
 
 
 
 | 292 | foreach (QFileInfo fileInfo, directory.entryInfoList(nameWildCard)){ | 
 
 
 
 
 | 293 | resultFiles << fileInfo.absoluteFilePath(); | 
 
 
 
 
 | 294 | } | 
 
 
 
 
 | 295 |  | 
 
 
 
 
 | 296 | return resultFiles; | 
 
 
 
 
 | 297 | } | 
 
 
 
 
 | 298 |  | 
 
 
 
 
 | 299 | } |