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 = ""; public XmlPatch(String file) { fileName = file; } public XmlPatch(String file, String forceInFiles) { fileName = file; forceFiles = forceInFiles; //We support apply the operation in diverse forced files (NameOfFile parameter will be ignored) } /// /// 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 (forceFiles == null) { 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 (Element == "") { return false; } //---------------------------------------------------Parse Operation command (end) List filesToProcess = new List(); if (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) { 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 (forceFiles == null) { 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 (Element == "") { return false; } //---------------------------------------------------Parse Operation command (end) List filesToProcess = new List(); if (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) { 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 (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 (command.IndexOf("filename:") != -1) { paramType = "filename:"; } else if (command.IndexOf("filename=") != -1) { paramType = "filename="; } // Add the filename if it doesn't exists else { command = command.Insert(command.Length-1," -filename:" + this.forceFiles); // -2 to be inside quotes } if (paramType != "") { int startIdx = command.IndexOf(paramType) + paramType.Length; int endIdx = command.IndexOf(" ", startIdx); // it may end with space if (endIdx == -1) { endIdx = command.IndexOf("\"", startIdx); // or with quotes } string currFilename = command.Substring(startIdx, endIdx - startIdx); command = command.Replace(currFilename, this.forceFiles); } } command = command.Replace("\"", ""); // remove quotes ProcessStartInfo startInfo = new ProcessStartInfo(); if (!Util.IsRunningOnMono()) { startInfo.FileName = Util.getExeFileName(); } else{ startInfo.FileName = "mono"; } if (!Util.IsRunningOnMono()) { startInfo.Arguments = command; } else{ startInfo.Arguments = Util.getExeFileName() + " " + command; } startInfo.UseShellExecute = false; // necessary to redirect output startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; // hide new process window Process p = System.Diagnostics.Process.Start(startInfo); p.OutputDataReceived += commandStdOutputReceived; p.ErrorDataReceived += commandStdErrorReceived; p.BeginOutputReadLine(); p.BeginErrorReadLine(); p.WaitForExit(); } catch (Exception e) { Program.printAppError(Program.appErrors.PATCH_COMMAND_PROCESS_ERROR, "Error processing command in Patch file.\n" + e.ToString()); return false; } return true; } /// /// Reads asynchronously output from the new process where the command will be executed /// /// /// private void commandStdOutputReceived(object sender, DataReceivedEventArgs e) { string myData = e.Data; if (myData != null) { if (myData.EndsWith("\n")) { Console.Write(myData); } else { Console.WriteLine(myData); } } } private void commandStdErrorReceived(object sender, DataReceivedEventArgs e) { string myData = e.Data; if (myData != null) { if (myData.EndsWith("\n")) { Console.Error.Write(myData); } else { Console.Error.WriteLine(myData); } } } 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; } } }