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