Merge pull request #2377 from joelmartinez/docs-multiassembly-extension-fix
[mono.git] / mcs / class / referencesource / mscorlib / system / security / util / parser.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** CLASS:    Parser
9 ** 
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** PURPOSE:  Parse "Elementary XML", that is, XML without 
14 **           attributes or DTDs, in other words, XML with 
15 **           elements only.
16 ** 
17 ** 
18 ===========================================================*/
19 namespace System.Security.Util {
20     using System.Text;
21     using System.Runtime.InteropServices;
22     using System;
23     using BinaryReader = System.IO.BinaryReader ;
24     using ArrayList = System.Collections.ArrayList;
25     using Stream = System.IO.Stream;
26     using StreamReader = System.IO.StreamReader;
27     using Encoding = System.Text.Encoding;
28
29     sealed internal class Parser
30     {
31         private SecurityDocument _doc;
32         private Tokenizer _t;
33     
34         internal SecurityElement GetTopElement()
35         {
36             return _doc.GetRootElement();
37         }
38
39         private const short c_flag = 0x4000;
40         private const short c_elementtag = (short)(SecurityDocument.c_element << 8 | c_flag);
41         private const short c_attributetag = (short)(SecurityDocument.c_attribute << 8 | c_flag);
42         private const short c_texttag = (short)(SecurityDocument.c_text << 8 | c_flag);
43         private const short c_additionaltexttag = (short)(SecurityDocument.c_text << 8 | c_flag | 0x2000);
44         private const short c_childrentag = (short)(SecurityDocument.c_children << 8 | c_flag);
45         private const short c_wastedstringtag = (short)(0x1000 | c_flag);
46
47         private void GetRequiredSizes( TokenizerStream stream, ref int index )
48         {
49             //
50             // Iteratively collect stuff up until the next end-tag.
51             // We've already seen the open-tag.
52             //
53            
54             bool needToBreak = false;
55             bool needToPop = false;
56             bool createdNode = false;
57             bool intag = false;
58             int stackDepth = 1;
59             SecurityElementType type = SecurityElementType.Regular;
60             String strValue = null;
61             bool sawEquals = false;
62             bool sawText = false;
63             int status = 0;
64             
65             short i;
66
67             do
68             {
69                 for (i = stream.GetNextToken() ; i != -1 ; i = stream.GetNextToken())
70                 {
71                     switch (i & 0x00FF)
72                     {
73                     case Tokenizer.cstr:
74                         {
75                             if (intag)
76                             {
77                                 if (type == SecurityElementType.Comment)
78                                 {
79                                     // Ignore data in comments but still get the data 
80                                     // to keep the stream in the right place.
81                                     stream.ThrowAwayNextString();
82                                     stream.TagLastToken( c_wastedstringtag );
83                                 }
84                                 else
85                                 {
86                                     // We're in a regular tag, so we've found an attribute/value pair.
87                                 
88                                     if (strValue == null)
89                                     {
90                                         // Found attribute name, save it for later.
91                                     
92                                         strValue = stream.GetNextString();
93                                     }
94                                     else
95                                     {
96                                         // Found attribute text, add the pair to the current element.
97
98                                         if (!sawEquals)
99                                             throw new XmlSyntaxException( _t.LineNo );
100
101                                         stream.TagLastToken( c_attributetag );
102                                         index += SecurityDocument.EncodedStringSize( strValue ) +
103                                                  SecurityDocument.EncodedStringSize( stream.GetNextString() ) +
104                                                  1;
105                                         strValue = null;
106                                         sawEquals = false;
107                                     }
108                                 }
109                             }
110                             else
111                             {
112                                 // We're not in a tag, so we've found text between tags.
113
114                                 if (sawText)
115                                 {
116                                     stream.TagLastToken( c_additionaltexttag );
117                                     index += SecurityDocument.EncodedStringSize( stream.GetNextString() ) +
118                                              SecurityDocument.EncodedStringSize( " " );
119                                 }
120                                 else
121                                 {
122                                     stream.TagLastToken( c_texttag );
123                                     index += SecurityDocument.EncodedStringSize( stream.GetNextString() ) +
124                                              1;
125                                     sawText = true;
126                                 }
127                             }
128                         }
129                         break;
130         
131                     case Tokenizer.bra:
132                         intag = true;
133                         sawText = false;
134                         i = stream.GetNextToken();
135     
136                         if (i == Tokenizer.slash)
137                         {
138                             stream.TagLastToken( c_childrentag );
139                             while (true)
140                             {
141                                 // spin; don't care what's in here
142                                 i = stream.GetNextToken();
143                                 if (i == Tokenizer.cstr)
144                                 {
145                                     stream.ThrowAwayNextString();
146                                     stream.TagLastToken( c_wastedstringtag );
147                                 }
148                                 else if (i == -1)
149                                     throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_UnexpectedEndOfFile" ));
150                                 else
151                                     break;
152                             }
153         
154                             if (i != Tokenizer.ket)
155                             {
156                                 throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_ExpectedCloseBracket" ));
157                             }
158             
159                             intag = false;
160             
161                             // Found the end of this element
162                             index++;
163
164                             sawText = false;
165                             stackDepth--;
166                             
167                             needToBreak = true;
168                         }
169                         else if (i == Tokenizer.cstr)
170                         {
171                             // Found a child
172                             
173                             createdNode = true;
174
175                             stream.TagLastToken( c_elementtag );
176                             index += SecurityDocument.EncodedStringSize( stream.GetNextString() ) +
177                                      1;
178                             
179                             if (type != SecurityElementType.Regular)
180                                 throw new XmlSyntaxException( _t.LineNo );
181                             
182                             needToBreak = true;
183                             stackDepth++;
184                         }
185                         else if (i == Tokenizer.bang)
186                         {
187                             // Found a child that is a comment node.  Next up better be a cstr.
188
189                             status = 1;
190
191                             do
192                             {
193                                 i = stream.GetNextToken();
194
195                                 switch (i)
196                                 {
197                                 case Tokenizer.bra:
198                                     status++;
199                                     break;
200
201                                 case Tokenizer.ket:
202                                     status--;
203                                     break;
204
205                                 case Tokenizer.cstr:
206                                     stream.ThrowAwayNextString();
207                                     stream.TagLastToken( c_wastedstringtag );
208                                     break;
209
210                                 default:
211                                     break;
212                                 }
213                             } while (status > 0);
214
215                             intag = false;
216                             sawText = false;
217                             needToBreak = true;
218                         }
219                         else if (i == Tokenizer.quest)
220                         {
221                             // Found a child that is a format node.  Next up better be a cstr.
222
223                             i = stream.GetNextToken();
224
225                             if (i != Tokenizer.cstr)
226                                 throw new XmlSyntaxException( _t.LineNo );
227                             
228                             createdNode = true;
229
230                             type = SecurityElementType.Format;
231                             
232                             stream.TagLastToken( c_elementtag );
233                             index += SecurityDocument.EncodedStringSize( stream.GetNextString() ) +
234                                      1;
235                             
236                             status = 1;
237                             stackDepth++;
238                             
239                             needToBreak = true;
240                         }
241                         else   
242                         {
243                             throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_ExpectedSlashOrString" ));
244                         }
245                         break ;
246         
247                     case Tokenizer.equals:
248                         sawEquals = true;
249                         break;
250                         
251                     case Tokenizer.ket:
252                         if (intag)
253                         {
254                             intag = false;
255                             continue;
256                         }
257                         else
258                         {
259                             throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_UnexpectedCloseBracket" ));
260                         }
261                         // not reachable
262                         
263                     case Tokenizer.slash:
264                         i = stream.GetNextToken();
265                         
266                         if (i == Tokenizer.ket)
267                         {
268                             // Found the end of this element
269                             stream.TagLastToken( c_childrentag );
270                             index++;
271                             stackDepth--;
272                             sawText = false;
273                             
274                             needToBreak = true;
275                         }
276                         else
277                         {
278                             throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_ExpectedCloseBracket" ));
279                         }
280                         break;
281                         
282                     case Tokenizer.quest:
283                         if (intag && type == SecurityElementType.Format && status == 1)
284                         {
285                             i = stream.GetNextToken();
286
287                             if (i == Tokenizer.ket)
288                             {
289                                 stream.TagLastToken( c_childrentag );
290                                 index++;
291                                 stackDepth--;
292                                 sawText = false;
293
294                                 needToBreak = true;
295                             }
296                             else
297                             {
298                                 throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_ExpectedCloseBracket" ));
299                             }
300                         }
301                         else
302                         {
303                             throw new XmlSyntaxException (_t.LineNo);
304                         }
305                         break;
306
307                     case Tokenizer.dash:
308                     default:
309                         throw new XmlSyntaxException (_t.LineNo) ;
310                     }
311                     
312                     if (needToBreak)
313                     {
314                         needToBreak = false;
315                         needToPop = false;
316                         break;
317                     }
318                     else
319                     {
320                         needToPop = true;
321                     }
322                 }
323
324                 if (needToPop)
325                 {
326                     index++;
327                     stackDepth--;
328                     sawText = false;
329                 }
330                 else if (i == -1 && (stackDepth != 1 || !createdNode))
331                 {
332                     // This means that we still have items on the stack, but the end of our
333                     // stream has been reached.
334
335                     throw new XmlSyntaxException( _t.LineNo, Environment.GetResourceString( "XMLSyntax_UnexpectedEndOfFile" ));
336                 }
337             }
338             while (stackDepth > 1);
339         }
340         
341         private int DetermineFormat( TokenizerStream stream )
342         {
343             if (stream.GetNextToken() == Tokenizer.bra)
344             {
345                 if (stream.GetNextToken() == Tokenizer.quest)
346                 {
347                     _t.GetTokens( stream, -1, true );
348                     stream.GoToPosition( 2 );
349
350                     bool sawEquals = false;
351                     bool sawEncoding = false;
352
353                     short i;
354
355                     for (i = stream.GetNextToken(); i != -1 && i != Tokenizer.ket; i = stream.GetNextToken())
356                     {
357                         switch (i)
358                         {
359                         case Tokenizer.cstr:
360                             if (sawEquals && sawEncoding)
361                             {
362                                 _t.ChangeFormat( System.Text.Encoding.GetEncoding( stream.GetNextString() ) );
363                                 return 0;
364                             }
365                             else if (!sawEquals)
366                             {
367                                 if (String.Compare( stream.GetNextString(), "encoding", StringComparison.Ordinal) == 0)
368                                     sawEncoding = true;
369                             }
370                             else
371                             {
372                                 sawEquals = false;
373                                 sawEncoding = false;
374                                 stream.ThrowAwayNextString();
375                             }
376                             break;
377
378                         case Tokenizer.equals:
379                             sawEquals = true;
380                             break;
381
382                         default:
383                             throw new XmlSyntaxException (_t.LineNo, Environment.GetResourceString( "XMLSyntax_UnexpectedEndOfFile" ));
384                         }
385                     }
386
387                     return 0;
388                 }
389             }
390
391             return 2;
392         }
393                     
394
395         private void ParseContents()
396         {
397             short i;
398
399             TokenizerStream stream = new TokenizerStream();
400
401             _t.GetTokens( stream, 2, false );
402             stream.Reset();
403
404             int gotoPosition = DetermineFormat( stream );
405
406             stream.GoToPosition( gotoPosition );
407             _t.GetTokens( stream, -1, false );
408             stream.Reset();
409
410             int neededIndex = 0;
411             
412             GetRequiredSizes( stream, ref neededIndex );
413
414             _doc = new SecurityDocument( neededIndex );
415             int position = 0;
416
417             stream.Reset();
418
419             for (i = stream.GetNextFullToken(); i != -1; i = stream.GetNextFullToken())
420             {
421                 if ((i & c_flag) != c_flag)
422                     continue;
423                 else
424                 {
425                     switch((short)(i & 0xFF00))
426                     {
427                     case c_elementtag:
428                         _doc.AddToken( SecurityDocument.c_element, ref position );
429                         _doc.AddString( stream.GetNextString(), ref position );
430                         break;
431
432                     case c_attributetag:
433                         _doc.AddToken( SecurityDocument.c_attribute, ref position );
434                         _doc.AddString( stream.GetNextString(), ref position );
435                         _doc.AddString( stream.GetNextString(), ref position );
436                         break;
437
438                     case c_texttag:
439                         _doc.AddToken( SecurityDocument.c_text, ref position );
440                         _doc.AddString( stream.GetNextString(), ref position );
441                         break;
442
443                     case c_additionaltexttag:
444                         _doc.AppendString( " ", ref position );
445                         _doc.AppendString( stream.GetNextString(), ref position );
446                         break;
447
448                     case c_childrentag:
449                         _doc.AddToken( SecurityDocument.c_children, ref position );
450                         break;
451
452                     case c_wastedstringtag:
453                         stream.ThrowAwayNextString();
454                         break;
455
456                     default:
457                         throw new XmlSyntaxException();
458                     }
459                 }
460             }
461         }
462     
463         private Parser(Tokenizer t)
464         {
465             _t = t;
466             _doc = null;
467
468             try
469             {
470                 ParseContents();
471             }
472             finally
473             {
474                 _t.Recycle();
475             }
476         }
477         
478         internal Parser (String input)
479             : this (new Tokenizer (input))
480         {
481         }
482
483         internal Parser (String input, String[] searchStrings, String[] replaceStrings)
484             : this (new Tokenizer (input, searchStrings, replaceStrings))
485         {
486         }
487
488         internal Parser( byte[] array, Tokenizer.ByteTokenEncoding encoding )
489             : this (new Tokenizer( array, encoding, 0 ) )
490         {
491         }
492
493     
494         internal Parser( byte[] array, Tokenizer.ByteTokenEncoding encoding, int startIndex )
495             : this (new Tokenizer( array, encoding, startIndex ) )
496         {
497         }
498         
499         internal Parser( StreamReader input )
500             : this (new Tokenizer( input ) )
501         {
502         }
503
504         internal Parser( char[] array )
505             : this (new Tokenizer( array ) )
506         {
507         }
508         
509     }                                              
510     
511 }
512