aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Prebuild/src/Core/Parse/Preprocessor.cs
diff options
context:
space:
mode:
authorMW2007-05-26 13:40:19 +0000
committerMW2007-05-26 13:40:19 +0000
commit3436961bb5c01d659d09be134368f4f69460cef9 (patch)
tree3753ba4d7818df2a6bce0bbe863ff033cdfd568a /Prebuild/src/Core/Parse/Preprocessor.cs
downloadopensim-SC_OLD-3436961bb5c01d659d09be134368f4f69460cef9.zip
opensim-SC_OLD-3436961bb5c01d659d09be134368f4f69460cef9.tar.gz
opensim-SC_OLD-3436961bb5c01d659d09be134368f4f69460cef9.tar.bz2
opensim-SC_OLD-3436961bb5c01d659d09be134368f4f69460cef9.tar.xz
Start of rewrite 5279!
Diffstat (limited to 'Prebuild/src/Core/Parse/Preprocessor.cs')
-rw-r--r--Prebuild/src/Core/Parse/Preprocessor.cs519
1 files changed, 519 insertions, 0 deletions
diff --git a/Prebuild/src/Core/Parse/Preprocessor.cs b/Prebuild/src/Core/Parse/Preprocessor.cs
new file mode 100644
index 0000000..85e92c3
--- /dev/null
+++ b/Prebuild/src/Core/Parse/Preprocessor.cs
@@ -0,0 +1,519 @@
1#region BSD License
2/*
3Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com)
4
5Redistribution and use in source and binary forms, with or without modification, are permitted
6provided that the following conditions are met:
7
8* Redistributions of source code must retain the above copyright notice, this list of conditions
9 and the following disclaimer.
10* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
11 and the following disclaimer in the documentation and/or other materials provided with the
12 distribution.
13* The name of the author may not be used to endorse or promote products derived from this software
14 without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
17BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*/
24#endregion
25
26#region CVS Information
27/*
28 * $Source$
29 * $Author: jendave $
30 * $Date: 2006-09-01 19:55:06 +0200 (fr, 01 sep 2006) $
31 * $Revision: 147 $
32 */
33#endregion
34
35using System;
36using System.Collections;
37using System.IO;
38using System.Xml;
39
40namespace Prebuild.Core.Parse
41{
42 /// <summary>
43 ///
44 /// </summary>
45 public enum OperatorSymbol
46 {
47 /// <summary>
48 ///
49 /// </summary>
50 None,
51 /// <summary>
52 ///
53 /// </summary>
54 Equal,
55 /// <summary>
56 ///
57 /// </summary>
58 NotEqual,
59 /// <summary>
60 ///
61 /// </summary>
62 LessThan,
63 /// <summary>
64 ///
65 /// </summary>
66 GreaterThan,
67 /// <summary>
68 ///
69 /// </summary>
70 LessThanEqual,
71 /// <summary>
72 ///
73 /// </summary>
74 GreaterThanEqual
75 }
76
77 /// <summary>
78 ///
79 /// </summary>
80 public class Preprocessor
81 {
82 #region Fields
83
84 XmlDocument m_OutDoc;
85 Stack m_IfStack;
86 Hashtable m_Variables;
87
88 #endregion
89
90 #region Constructors
91
92 /// <summary>
93 /// Initializes a new instance of the <see cref="Preprocessor"/> class.
94 /// </summary>
95 public Preprocessor()
96 {
97 m_OutDoc = new XmlDocument();
98 m_IfStack = new Stack();
99 m_Variables = new Hashtable();
100
101 RegisterVariable("OS", GetOS());
102 RegisterVariable("RuntimeVersion", Environment.Version.Major);
103 RegisterVariable("RuntimeMajor", Environment.Version.Major);
104 RegisterVariable("RuntimeMinor", Environment.Version.Minor);
105 RegisterVariable("RuntimeRevision", Environment.Version.Revision);
106 }
107
108 #endregion
109
110 #region Properties
111
112 /// <summary>
113 /// Gets the processed doc.
114 /// </summary>
115 /// <value>The processed doc.</value>
116 public XmlDocument ProcessedDoc
117 {
118 get
119 {
120 return m_OutDoc;
121 }
122 }
123
124 #endregion
125
126 #region Private Methods
127
128 /// <summary>
129 /// Parts of this code were taken from NAnt and is subject to the GPL
130 /// as per NAnt's license. Thanks to the NAnt guys for this little gem.
131 /// </summary>
132 /// <returns></returns>
133 public static string GetOS()
134 {
135 PlatformID platId = Environment.OSVersion.Platform;
136 if(platId == PlatformID.Win32NT || platId == PlatformID.Win32Windows)
137 {
138 return "Win32";
139 }
140
141 /*
142 * .NET 1.x, under Mono, the UNIX code is 128. Under
143 * .NET 2.x, Mono or MS, the UNIX code is 4
144 */
145 if(Environment.Version.Major == 1)
146 {
147 if((int)platId == 128)
148 {
149 return "UNIX";
150 }
151 }
152 else if((int)platId == 4)
153 {
154 return "UNIX";
155 }
156
157 return "Unknown";
158 }
159
160 private static bool CompareNum(OperatorSymbol oper, int val1, int val2)
161 {
162 switch(oper)
163 {
164 case OperatorSymbol.Equal:
165 return (val1 == val2);
166 case OperatorSymbol.NotEqual:
167 return (val1 != val2);
168 case OperatorSymbol.LessThan:
169 return (val1 < val2);
170 case OperatorSymbol.LessThanEqual:
171 return (val1 <= val2);
172 case OperatorSymbol.GreaterThan:
173 return (val1 > val2);
174 case OperatorSymbol.GreaterThanEqual:
175 return (val1 >= val2);
176 }
177
178 throw new WarningException("Unknown operator type");
179 }
180
181 private static bool CompareStr(OperatorSymbol oper, string val1, string val2)
182 {
183 switch(oper)
184 {
185 case OperatorSymbol.Equal:
186 return (val1 == val2);
187 case OperatorSymbol.NotEqual:
188 return (val1 != val2);
189 case OperatorSymbol.LessThan:
190 return (val1.CompareTo(val2) < 0);
191 case OperatorSymbol.LessThanEqual:
192 return (val1.CompareTo(val2) <= 0);
193 case OperatorSymbol.GreaterThan:
194 return (val1.CompareTo(val2) > 0);
195 case OperatorSymbol.GreaterThanEqual:
196 return (val1.CompareTo(val2) >= 0);
197 }
198
199 throw new WarningException("Unknown operator type");
200 }
201
202 private static char NextChar(int idx, string str)
203 {
204 if((idx + 1) >= str.Length)
205 {
206 return Char.MaxValue;
207 }
208
209 return str[idx + 1];
210 }
211 // Very very simple expression parser. Can only match expressions of the form
212 // <var> <op> <value>:
213 // OS = Windows
214 // OS != Linux
215 // RuntimeMinor > 0
216 private bool ParseExpression(string exp)
217 {
218 if(exp == null)
219 {
220 throw new ArgumentException("Invalid expression, cannot be null");
221 }
222
223 exp = exp.Trim();
224 if(exp.Length < 1)
225 {
226 throw new ArgumentException("Invalid expression, cannot be 0 length");
227 }
228
229 string id = "";
230 string str = "";
231 OperatorSymbol oper = OperatorSymbol.None;
232 bool inStr = false;
233 char c;
234
235 for(int i = 0; i < exp.Length; i++)
236 {
237 c = exp[i];
238 if(Char.IsWhiteSpace(c))
239 {
240 continue;
241 }
242
243 if(Char.IsLetterOrDigit(c) || c == '_')
244 {
245 if(inStr)
246 {
247 str += c;
248 }
249 else
250 {
251 id += c;
252 }
253 }
254 else if(c == '\"')
255 {
256 inStr = !inStr;
257 if(inStr)
258 {
259 str = "";
260 }
261 }
262 else
263 {
264 if(inStr)
265 {
266 str += c;
267 }
268 else
269 {
270 switch(c)
271 {
272 case '=':
273 oper = OperatorSymbol.Equal;
274 break;
275
276 case '!':
277 if(NextChar(i, exp) == '=')
278 {
279 oper = OperatorSymbol.NotEqual;
280 }
281
282 break;
283
284 case '<':
285 if(NextChar(i, exp) == '=')
286 {
287 oper = OperatorSymbol.LessThanEqual;
288 }
289 else
290 {
291 oper = OperatorSymbol.LessThan;
292 }
293
294 break;
295
296 case '>':
297 if(NextChar(i, exp) == '=')
298 {
299 oper = OperatorSymbol.GreaterThanEqual;
300 }
301 else
302 {
303 oper = OperatorSymbol.GreaterThan;
304 }
305
306 break;
307 }
308 }
309 }
310 }
311
312
313 if(inStr)
314 {
315 throw new WarningException("Expected end of string in expression");
316 }
317
318 if(oper == OperatorSymbol.None)
319 {
320 throw new WarningException("Expected operator in expression");
321 }
322 else if(id.Length < 1)
323 {
324 throw new WarningException("Expected identifier in expression");
325 }
326 else if(str.Length < 1)
327 {
328 throw new WarningException("Expected value in expression");
329 }
330
331 bool ret = false;
332 try
333 {
334 object val = m_Variables[id.ToLower()];
335 if(val == null)
336 {
337 throw new WarningException("Unknown identifier '{0}'", id);
338 }
339
340 int numVal, numVal2;
341 string strVal, strVal2;
342 Type t = val.GetType();
343 if(t.IsAssignableFrom(typeof(int)))
344 {
345 numVal = (int)val;
346 numVal2 = Int32.Parse(str);
347 ret = CompareNum(oper, numVal, numVal2);
348 }
349 else
350 {
351 strVal = val.ToString();
352 strVal2 = str;
353 ret = CompareStr(oper, strVal, strVal2);
354 }
355 }
356 catch(ArgumentException ex)
357 {
358 ex.ToString();
359 throw new WarningException("Invalid value type for system variable '{0}', expected int", id);
360 }
361
362 return ret;
363 }
364
365 #endregion
366
367 #region Public Methods
368
369 /// <summary>
370 ///
371 /// </summary>
372 /// <param name="name"></param>
373 /// <param name="variableValue"></param>
374 public void RegisterVariable(string name, object variableValue)
375 {
376 if(name == null || variableValue == null)
377 {
378 return;
379 }
380
381 m_Variables[name.ToLower()] = variableValue;
382 }
383
384 /// <summary>
385 /// Performs validation on the xml source as well as evaluates conditional and flow expresions
386 /// </summary>
387 /// <exception cref="ArgumentException">For invalid use of conditional expressions or for invalid XML syntax. If a XmlValidatingReader is passed, then will also throw exceptions for non-schema-conforming xml</exception>
388 /// <param name="reader"></param>
389 /// <returns>the output xml </returns>
390 public string Process(XmlReader reader)
391 {
392 if(reader == null)
393 {
394 throw new ArgumentException("Invalid XML reader to pre-process");
395 }
396
397 IfContext context = new IfContext(true, true, IfState.None);
398 StringWriter xmlText = new StringWriter();
399 XmlTextWriter writer = new XmlTextWriter(xmlText);
400 writer.Formatting = Formatting.Indented;
401 while(reader.Read())
402 {
403 if(reader.NodeType == XmlNodeType.ProcessingInstruction)
404 {
405 bool ignore = false;
406 switch(reader.LocalName)
407 {
408 case "if":
409 m_IfStack.Push(context);
410 context = new IfContext(context.Keep & context.Active, ParseExpression(reader.Value), IfState.If);
411 ignore = true;
412 break;
413
414 case "elseif":
415 if(m_IfStack.Count == 0)
416 {
417 throw new WarningException("Unexpected 'elseif' outside of 'if'");
418 }
419 else if(context.State != IfState.If && context.State != IfState.ElseIf)
420 {
421 throw new WarningException("Unexpected 'elseif' outside of 'if'");
422 }
423
424 context.State = IfState.ElseIf;
425 if(!context.EverKept)
426 {
427 context.Keep = ParseExpression(reader.Value);
428 }
429 else
430 {
431 context.Keep = false;
432 }
433
434 ignore = true;
435 break;
436
437 case "else":
438 if(m_IfStack.Count == 0)
439 {
440 throw new WarningException("Unexpected 'else' outside of 'if'");
441 }
442 else if(context.State != IfState.If && context.State != IfState.ElseIf)
443 {
444 throw new WarningException("Unexpected 'else' outside of 'if'");
445 }
446
447 context.State = IfState.Else;
448 context.Keep = !context.EverKept;
449 ignore = true;
450 break;
451
452 case "endif":
453 if(m_IfStack.Count == 0)
454 {
455 throw new WarningException("Unexpected 'endif' outside of 'if'");
456 }
457
458 context = (IfContext)m_IfStack.Pop();
459 ignore = true;
460 break;
461 }
462
463 if(ignore)
464 {
465 continue;
466 }
467 }//end pre-proc instruction
468
469 if(!context.Active || !context.Keep)
470 {
471 continue;
472 }
473
474 switch(reader.NodeType)
475 {
476 case XmlNodeType.Element:
477 bool empty = reader.IsEmptyElement;
478 writer.WriteStartElement(reader.Name);
479
480 while (reader.MoveToNextAttribute())
481 {
482 writer.WriteAttributeString(reader.Name, reader.Value);
483 }
484
485 if(empty)
486 {
487 writer.WriteEndElement();
488 }
489
490 break;
491
492 case XmlNodeType.EndElement:
493 writer.WriteEndElement();
494 break;
495
496 case XmlNodeType.Text:
497 writer.WriteString(reader.Value);
498 break;
499
500 case XmlNodeType.CDATA:
501 writer.WriteCData(reader.Value);
502 break;
503
504 default:
505 break;
506 }
507 }
508
509 if(m_IfStack.Count != 0)
510 {
511 throw new WarningException("Mismatched 'if', 'endif' pair");
512 }
513
514 return xmlText.ToString();
515 }
516
517 #endregion
518 }
519}