1 |
using System; |
2 |
using System.Collections.Generic; |
3 |
using System.Diagnostics; |
4 |
using System.Text; |
5 |
using System.Xml; |
6 |
|
7 |
namespace xmlTools |
8 |
{ |
9 |
/// <summary> |
10 |
/// This classes parses a .patch xml tools file and applies its content to the files which it specifies |
11 |
/// </summary> |
12 |
class XmlPatch |
13 |
{ |
14 |
String fileName; |
15 |
String forceFiles = ""; |
16 |
|
17 |
public XmlPatch(String file) |
18 |
{ |
19 |
fileName = file; |
20 |
} |
21 |
|
22 |
public XmlPatch(String file, String forceInFiles) |
23 |
{ |
24 |
fileName = file; |
25 |
forceFiles = forceInFiles; //We support apply the operation in diverse forced files (NameOfFile parameter will be ignored) |
26 |
} |
27 |
|
28 |
/// <summary> |
29 |
/// Applies the patch file. Returns true if successful otherwise returns false. |
30 |
/// </summary> |
31 |
/// <returns></returns> |
32 |
public bool startPatch() |
33 |
{ |
34 |
string line; |
35 |
|
36 |
// Read the file and display it line by line. |
37 |
System.IO.StreamReader file = new System.IO.StreamReader(fileName); |
38 |
|
39 |
while ((line = file.ReadLine()) != null) //read while we don't reach the end of the file |
40 |
{ |
41 |
if (line.StartsWith("@ADDTO ")) |
42 |
{ |
43 |
string operation = line; |
44 |
string xmlToInject = ""; |
45 |
|
46 |
file.ReadLine(); //ignore <xml> start header |
47 |
while ((line = file.ReadLine()) != "</xml>") |
48 |
{ |
49 |
xmlToInject += line + "\n"; //get all the xml that will be injected |
50 |
} |
51 |
if (!addOperation(operation, xmlToInject)) |
52 |
{ |
53 |
Program.printAppError(Program.appErrors.PATCH_ADDTO_PROCESS_ERROR, "Error while performing adding operation in patch file. Aborting..."); |
54 |
return false; |
55 |
} |
56 |
} |
57 |
else if (line.StartsWith("@REMOVE ")) |
58 |
{ |
59 |
if (!removeOperation(line)) |
60 |
{ |
61 |
Program.printAppError(Program.appErrors.PATCH_REMOVE_PROCESS_ERROR, "Error while performing remove operation in patch file. Aborting..."); |
62 |
return false; |
63 |
} |
64 |
} |
65 |
else if (line.StartsWith("@COMMAND ")) |
66 |
{ |
67 |
if (!executeCommand(line)) |
68 |
{ |
69 |
Program.printAppError(Program.appErrors.PATCH_COMMAND_PROCESS_ERROR, "Error while performing command operation in patch file. Aborting..."); |
70 |
return false; |
71 |
} |
72 |
} |
73 |
} |
74 |
|
75 |
file.Close(); |
76 |
|
77 |
return true; |
78 |
} |
79 |
|
80 |
/// <summary> |
81 |
/// Inserts xml in a desired Element. Returns true or false if it succeeds |
82 |
/// </summary> |
83 |
/// <param name="operation"></param> |
84 |
/// <param name="xmlToInject"></param> |
85 |
/// <returns></returns> |
86 |
private bool addOperation(string operation, string xmlToInject) |
87 |
{ |
88 |
//@ADDTO File "example.xml" ParentElement "Animation" Element "Lookup" |
89 |
|
90 |
string File = "", ParentElement = "", Element = ""; |
91 |
|
92 |
//---------------------------------------------------Parse Operation command (start) |
93 |
try |
94 |
{ |
95 |
if (forceFiles == null) |
96 |
{ |
97 |
File = getPatchParameter(operation, "File"); |
98 |
} |
99 |
else |
100 |
{ |
101 |
File = forceFiles; |
102 |
} |
103 |
|
104 |
ParentElement = getPatchParameter(operation, "ParentElement"); //Get the ParentElement |
105 |
|
106 |
Element = getPatchParameter(operation, "Element"); //Get the Element |
107 |
} |
108 |
catch (Exception e) |
109 |
{ |
110 |
Program.printAppError(Program.appErrors.PATCH_ADDTO_ERROR_PARSING_XML, "Error parsing addOperation in Patch file.\n" + e.ToString()); |
111 |
return false; |
112 |
} |
113 |
|
114 |
if (Element == "") |
115 |
{ |
116 |
return false; |
117 |
} |
118 |
|
119 |
//---------------------------------------------------Parse Operation command (end) |
120 |
List<String> filesToProcess = new List<String>(); |
121 |
if (File == "") |
122 |
{ |
123 |
filesToProcess = Util.getAllXmlFiles(); //no file specified, use all xml files found in same folder |
124 |
} |
125 |
else if (Util.containsWildcard(File)) |
126 |
{ |
127 |
filesToProcess = Util.getXmlFilesWildcard(File); |
128 |
} |
129 |
else |
130 |
{ |
131 |
filesToProcess.Add(File); |
132 |
} |
133 |
|
134 |
//---------------------------------------------------XML Injection (start) |
135 |
foreach (String currFile in filesToProcess) |
136 |
{ |
137 |
|
138 |
Util.backupFile(currFile); |
139 |
|
140 |
XmlDocument xdoc = new XmlDocument(); |
141 |
xdoc.Load(currFile); |
142 |
|
143 |
List<XmlNode> myElements = new List<XmlNode>(); |
144 |
Util.getAllSpecificElements(xdoc.DocumentElement, ref myElements, Element, ParentElement); //Returns all after "Oni" element |
145 |
|
146 |
if (myElements.Count == 0) |
147 |
{ |
148 |
Program.printAppError(Program.appErrors.PATCH_ELEMENT_NOT_FOUND, "Error in addOperation in Patch file: the element specified doesn't exist."); |
149 |
return false; |
150 |
} |
151 |
|
152 |
try |
153 |
{ |
154 |
XmlNode newXml = xdoc.ImportNode(Util.stringToXmlNode(xmlToInject), true); //necessary to import node or ArgumentException will be thrown when appending |
155 |
|
156 |
myElements[myElements.Count - 1].AppendChild(newXml); // Append the code after last element |
157 |
|
158 |
xdoc.Save(currFile); |
159 |
} |
160 |
catch (XmlException e) |
161 |
{ |
162 |
Program.printAppError(Program.appErrors.PATCH_ADDTO_ERROR_PARSING_XML, "Error parsing xml to addOperation in Patch file.\n" + e.ToString()); |
163 |
return false; |
164 |
} |
165 |
} |
166 |
//---------------------------------------------------XML Injection (end) |
167 |
|
168 |
return true; |
169 |
} |
170 |
|
171 |
/// <summary> |
172 |
/// Removes a xml element, right now it removes the first element it finds with matchs "Element" and "ParentElement" parameters |
173 |
/// </summary> |
174 |
/// <param name="operation"></param> |
175 |
/// <returns>true or false depending if succeed or not</returns> |
176 |
private bool removeOperation(string operation) |
177 |
{ |
178 |
//@REMOVE File "example.xml" ParentElement "Particles" Element "Particle" |
179 |
|
180 |
string File = "", ParentElement = "", Element = ""; |
181 |
|
182 |
//---------------------------------------------------Parse Operation command (start) |
183 |
try |
184 |
{ |
185 |
if (forceFiles == null) |
186 |
{ |
187 |
File = getPatchParameter(operation, "File"); |
188 |
} |
189 |
else |
190 |
{ |
191 |
File = forceFiles; |
192 |
} |
193 |
|
194 |
ParentElement = getPatchParameter(operation, "ParentElement"); //Get the ParentElement |
195 |
|
196 |
Element = getPatchParameter(operation, "Element"); //Get the Element |
197 |
} |
198 |
catch (Exception e) |
199 |
{ |
200 |
Program.printAppError(Program.appErrors.PATCH_REMOVE_PROCESS_ERROR, "Error parsing removeOperation in Patch file.\n" + e.ToString()); |
201 |
return false; |
202 |
} |
203 |
|
204 |
if (Element == "") |
205 |
{ |
206 |
return false; |
207 |
} |
208 |
|
209 |
//---------------------------------------------------Parse Operation command (end) |
210 |
|
211 |
List<String> filesToProcess = new List<String>(); |
212 |
if (File == "") |
213 |
{ |
214 |
filesToProcess = Util.getAllXmlFiles(); //no file specified, use all xml files found in same folder |
215 |
} |
216 |
else if (Util.containsWildcard(File)) |
217 |
{ |
218 |
filesToProcess = Util.getXmlFilesWildcard(File); |
219 |
} |
220 |
else |
221 |
{ |
222 |
filesToProcess.Add(File); |
223 |
} |
224 |
|
225 |
//---------------------------------------------------XML Remove (start) |
226 |
|
227 |
foreach (String currFile in filesToProcess) |
228 |
{ |
229 |
|
230 |
Util.backupFile(currFile); |
231 |
|
232 |
XmlDocument xdoc = new XmlDocument(); |
233 |
xdoc.Load(currFile); |
234 |
|
235 |
List<XmlNode> myElements = new List<XmlNode>(); |
236 |
Util.getAllSpecificElements(xdoc.DocumentElement, ref myElements, Element, ParentElement); //Returns all after "Oni" element |
237 |
|
238 |
if (myElements.Count == 0) |
239 |
{ |
240 |
Program.printAppError(Program.appErrors.PATCH_ELEMENT_NOT_FOUND, "Error in removeOperation in Patch file: the element specified doesn't exist."); |
241 |
return false; |
242 |
} |
243 |
|
244 |
myElements[0].ParentNode.RemoveChild(myElements[0]); // Removes the first occurrence which matches the "Element" and "ParentElement" given |
245 |
|
246 |
xdoc.Save(currFile); |
247 |
|
248 |
} |
249 |
//---------------------------------------------------XML Remove (end) |
250 |
|
251 |
|
252 |
return true; |
253 |
} |
254 |
|
255 |
/// <summary> |
256 |
/// |
257 |
/// </summary> |
258 |
/// <param name="command"></param> |
259 |
/// <returns>true or false depending if succeed or not</returns> |
260 |
private bool executeCommand(string command) |
261 |
{ |
262 |
//---------------------------------------------------Parse Operation command (start) |
263 |
|
264 |
command = command.Replace("@COMMAND ", ""); //get only the command to process |
265 |
|
266 |
if (command.Trim() == "") |
267 |
{ |
268 |
Program.printAppError(Program.appErrors.PATCH_COMMAND_NOT_FOUND, "Error parsing commandOperation in Patch file: Command is empty."); |
269 |
return false; |
270 |
} |
271 |
|
272 |
try |
273 |
{ |
274 |
if (forceFiles != null) |
275 |
{ |
276 |
string paramType = ""; |
277 |
|
278 |
// Filename already exists? |
279 |
if (command.IndexOf("filename:") != -1) |
280 |
{ |
281 |
paramType = "filename:"; |
282 |
} |
283 |
else if (command.IndexOf("filename=") != -1) |
284 |
{ |
285 |
paramType = "filename="; |
286 |
} |
287 |
// Add the filename if it doesn't exists |
288 |
else |
289 |
{ |
290 |
command = command.Insert(command.Length-1," -filename:" + this.forceFiles); // -2 to be inside quotes |
291 |
} |
292 |
|
293 |
if (paramType != "") |
294 |
{ |
295 |
int startIdx = command.IndexOf(paramType) + paramType.Length; |
296 |
int endIdx = command.IndexOf(" ", startIdx); // it may end with space |
297 |
if (endIdx == -1) |
298 |
{ |
299 |
endIdx = command.IndexOf("\"", startIdx); // or with quotes |
300 |
} |
301 |
string currFilename = command.Substring(startIdx, endIdx - startIdx); |
302 |
command = command.Replace(currFilename, this.forceFiles); |
303 |
} |
304 |
|
305 |
} |
306 |
|
307 |
command = command.Replace("\"", ""); // remove quotes |
308 |
|
309 |
ProcessStartInfo startInfo = new ProcessStartInfo(); |
310 |
if (!Util.IsRunningOnMono()) |
311 |
{ |
312 |
startInfo.FileName = Util.getExeFileName(); |
313 |
} |
314 |
else{ |
315 |
startInfo.FileName = "mono"; |
316 |
} |
317 |
if (!Util.IsRunningOnMono()) |
318 |
{ |
319 |
startInfo.Arguments = command; |
320 |
} |
321 |
else{ |
322 |
startInfo.Arguments = Util.getExeFileName() + " " + command; |
323 |
} |
324 |
startInfo.UseShellExecute = false; // necessary to redirect output |
325 |
startInfo.RedirectStandardOutput = true; |
326 |
startInfo.RedirectStandardError = true; |
327 |
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; // hide new process window |
328 |
|
329 |
Process p = System.Diagnostics.Process.Start(startInfo); |
330 |
p.OutputDataReceived += commandStdOutputReceived; |
331 |
p.ErrorDataReceived += commandStdErrorReceived; |
332 |
p.BeginOutputReadLine(); |
333 |
p.BeginErrorReadLine(); |
334 |
p.WaitForExit(); |
335 |
} |
336 |
catch (Exception e) |
337 |
{ |
338 |
Program.printAppError(Program.appErrors.PATCH_COMMAND_PROCESS_ERROR, "Error processing command in Patch file.\n" + e.ToString()); |
339 |
return false; |
340 |
} |
341 |
|
342 |
return true; |
343 |
} |
344 |
|
345 |
/// <summary> |
346 |
/// Reads asynchronously output from the new process where the command will be executed |
347 |
/// </summary> |
348 |
/// <param name="sender"></param> |
349 |
/// <param name="e"></param> |
350 |
private void commandStdOutputReceived(object sender, DataReceivedEventArgs e) |
351 |
{ |
352 |
|
353 |
string myData = e.Data; |
354 |
|
355 |
if (myData != null) |
356 |
{ |
357 |
if (myData.EndsWith("\n")) |
358 |
{ |
359 |
Console.Write(myData); |
360 |
} |
361 |
else |
362 |
{ |
363 |
Console.WriteLine(myData); |
364 |
} |
365 |
} |
366 |
|
367 |
} |
368 |
|
369 |
private void commandStdErrorReceived(object sender, DataReceivedEventArgs e) |
370 |
{ |
371 |
|
372 |
string myData = e.Data; |
373 |
|
374 |
if (myData != null) |
375 |
{ |
376 |
if (myData.EndsWith("\n")) |
377 |
{ |
378 |
Console.Error.Write(myData); |
379 |
} |
380 |
else |
381 |
{ |
382 |
Console.Error.WriteLine(myData); |
383 |
} |
384 |
} |
385 |
|
386 |
} |
387 |
|
388 |
private string getPatchParameter(string line, string parameterName) |
389 |
{ |
390 |
string result = ""; |
391 |
int startIdx = 0, endIdx = 0; |
392 |
|
393 |
string temp = parameterName + " \""; |
394 |
|
395 |
startIdx = line.IndexOf(temp); |
396 |
if (startIdx != -1) //we have Parameter specified |
397 |
{ |
398 |
startIdx += temp.Length; |
399 |
endIdx = line.IndexOf("\"", startIdx); |
400 |
result = line.Substring(startIdx, endIdx - startIdx); //Get the parameter value |
401 |
} |
402 |
|
403 |
return result; |
404 |
} |
405 |
} |
406 |
} |