using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Xml; namespace xmlTools { /// /// This classes parses a .patch xml tools file and applies its content to the files which it specifies /// class XmlPatch { String fileName; String forceFiles = ""; bool globalNoBackups = false; public XmlPatch(String file, bool noBackups) { fileName = file; globalNoBackups = noBackups; } public XmlPatch(String file, String forceInFiles, bool noBackups) { fileName = file; forceFiles = forceInFiles; //We support apply the operation in diverse forced files (NameOfFile parameter will be ignored) globalNoBackups = noBackups; } /// /// Applies the patch file. Returns true if successful otherwise returns false. /// /// public bool startPatch() { string line; // Read the file and display it line by line. System.IO.StreamReader file = new System.IO.StreamReader(fileName); while ((line = file.ReadLine()) != null) //read while we don't reach the end of the file { if (line.StartsWith("@ADDTO ")) { string operation = line; string xmlToInject = ""; file.ReadLine(); //ignore start header while ((line = file.ReadLine()) != "") { xmlToInject += line + "\n"; //get all the xml that will be injected } if (!addOperation(operation, xmlToInject)) { Program.printAppError(Program.appErrors.PATCH_ADDTO_PROCESS_ERROR, "Error while performing adding operation in patch file. Aborting..."); return false; } } else if (line.StartsWith("@REMOVE ")) { if (!removeOperation(line)) { Program.printAppError(Program.appErrors.PATCH_REMOVE_PROCESS_ERROR, "Error while performing remove operation in patch file. Aborting..."); return false; } } else if (line.StartsWith("@COMMAND ")) { if (!executeCommand(line)) { Program.printAppError(Program.appErrors.PATCH_COMMAND_PROCESS_ERROR, "Error while performing command operation in patch file. Aborting..."); return false; } } } file.Close(); return true; } /// /// Inserts xml in a desired Element. Returns true or false if it succeeds /// /// /// /// private bool addOperation(string operation, string xmlToInject) { //@ADDTO File "example.xml" ParentElement "Animation" Element "Lookup" string File = "", ParentElement = "", Element = ""; //---------------------------------------------------Parse Operation command (start) try { if (String.IsNullOrEmpty(forceFiles)) { File = getPatchParameter(operation, "File"); } else { File = forceFiles; } ParentElement = getPatchParameter(operation, "ParentElement"); //Get the ParentElement Element = getPatchParameter(operation, "Element"); //Get the Element } catch (Exception e) { Program.printAppError(Program.appErrors.PATCH_ADDTO_ERROR_PARSING_XML, "Error parsing addOperation in Patch file.\n" + e.ToString()); return false; } if (String.IsNullOrEmpty(Element)) { return false; } //---------------------------------------------------Parse Operation command (end) List filesToProcess = new List(); if (String.IsNullOrEmpty(File)) { filesToProcess = Util.getAllXmlFiles(); //no file specified, use all xml files found in same folder } else if (Util.containsWildcard(File)) { filesToProcess = Util.getXmlFilesWildcard(File); } else { filesToProcess.Add(File); } //---------------------------------------------------XML Injection (start) foreach (String currFile in filesToProcess) { if (!this.globalNoBackups && !Util.ContainsIgnoreCase(operation, "NoBackups")) // only skip backup if specified via global parameter or in patch file { Util.backupFile(currFile); } XmlDocument xdoc = new XmlDocument(); xdoc.Load(currFile); List myElements = new List(); Util.getAllSpecificElements(xdoc.DocumentElement, ref myElements, Element, ParentElement); //Returns all after "Oni" element if (myElements.Count == 0) { Program.printAppError(Program.appErrors.PATCH_ELEMENT_NOT_FOUND, "Error in addOperation in Patch file: the element specified doesn't exist."); return false; } try { XmlNode newXml = xdoc.ImportNode(Util.stringToXmlNode(xmlToInject), true); //necessary to import node or ArgumentException will be thrown when appending myElements[myElements.Count - 1].AppendChild(newXml); // Append the code after last element xdoc.Save(currFile); } catch (XmlException e) { Program.printAppError(Program.appErrors.PATCH_ADDTO_ERROR_PARSING_XML, "Error parsing xml to addOperation in Patch file.\n" + e.ToString()); return false; } } //---------------------------------------------------XML Injection (end) return true; } /// /// Removes a xml element, right now it removes the first element it finds with matchs "Element" and "ParentElement" parameters /// /// /// true or false depending if succeed or not private bool removeOperation(string operation) { //@REMOVE File "example.xml" ParentElement "Particles" Element "Particle" string File = "", ParentElement = "", Element = ""; //---------------------------------------------------Parse Operation command (start) try { if (String.IsNullOrEmpty(forceFiles)) { File = getPatchParameter(operation, "File"); } else { File = forceFiles; } ParentElement = getPatchParameter(operation, "ParentElement"); //Get the ParentElement Element = getPatchParameter(operation, "Element"); //Get the Element } catch (Exception e) { Program.printAppError(Program.appErrors.PATCH_REMOVE_PROCESS_ERROR, "Error parsing removeOperation in Patch file.\n" + e.ToString()); return false; } if (String.IsNullOrEmpty(Element)) { return false; } //---------------------------------------------------Parse Operation command (end) List filesToProcess = new List(); if (String.IsNullOrEmpty(File)) { filesToProcess = Util.getAllXmlFiles(); //no file specified, use all xml files found in same folder } else if (Util.containsWildcard(File)) { filesToProcess = Util.getXmlFilesWildcard(File); } else { filesToProcess.Add(File); } //---------------------------------------------------XML Remove (start) foreach (String currFile in filesToProcess) { if (!this.globalNoBackups && !Util.ContainsIgnoreCase(operation, "NoBackups")) // only skip backup if specified via global parameter or in patch file { Util.backupFile(currFile); } XmlDocument xdoc = new XmlDocument(); xdoc.Load(currFile); List myElements = new List(); Util.getAllSpecificElements(xdoc.DocumentElement, ref myElements, Element, ParentElement); //Returns all after "Oni" element if (myElements.Count == 0) { Program.printAppError(Program.appErrors.PATCH_ELEMENT_NOT_FOUND, "Error in removeOperation in Patch file: the element specified doesn't exist."); return false; } myElements[0].ParentNode.RemoveChild(myElements[0]); // Removes the first occurrence which matches the "Element" and "ParentElement" given xdoc.Save(currFile); } //---------------------------------------------------XML Remove (end) return true; } /// /// /// /// /// true or false depending if succeed or not private bool executeCommand(string command) { //---------------------------------------------------Parse Operation command (start) command = command.Replace("@COMMAND ", ""); //get only the command to process if (String.IsNullOrEmpty(command.Trim())) { Program.printAppError(Program.appErrors.PATCH_COMMAND_NOT_FOUND, "Error parsing commandOperation in Patch file: Command is empty."); return false; } try { if (forceFiles != null) { string paramType = ""; // Filename already exists? if (Util.ContainsIgnoreCase(command,"filename:")) { paramType = "filename:"; } else if (Util.ContainsIgnoreCase(command, "filename=")) { paramType = "filename="; } // Add the filename if it doesn't exists else { command = command.Insert(command.Length," -filename:" + this.forceFiles); } if (!String.IsNullOrEmpty(paramType)) { int startIdx = command.IndexOf(paramType) + paramType.Length; int endIdx = command.IndexOf(" ", startIdx); // it may end with space if (endIdx == -1) { endIdx = command.IndexOf("\n", startIdx); // or with endline } string currFilename = command.Substring(startIdx, endIdx - startIdx); command = command.Replace(currFilename, this.forceFiles); } } if (this.globalNoBackups && !Util.ContainsIgnoreCase(command,"nobackups")) // add noBackup flag if provided as global parameter { command = command.Insert(command.Length, " -nobackups"); } Program.Main(Util.stringToArgsArray(command)); // use the current process is more efficient than start a new one } catch (Exception e) { Program.printAppError(Program.appErrors.PATCH_COMMAND_PROCESS_ERROR, "Error processing command in Patch file.\n" + e.ToString()); return false; } return true; } private string getPatchParameter(string line, string parameterName) { string result = ""; int startIdx = 0, endIdx = 0; string temp = parameterName + " \""; startIdx = line.IndexOf(temp); if (startIdx != -1) //we have Parameter specified { startIdx += temp.Length; endIdx = line.IndexOf("\"", startIdx); result = line.Substring(startIdx, endIdx - startIdx); //Get the parameter value } return result; } } }