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;
}
}
}