2009-08-17 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AspGenerator.cs
1 //
2 // System.Web.Compilation.AspGenerator
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //      Marek Habersack <mhabersack@novell.com>
7 //
8 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
9 // Copyright (c) 2004-2009 Novell, Inc (http://www.novell.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32 using System;
33 using System.Collections;
34 using System.CodeDom.Compiler;
35 using System.Globalization;
36 using System.IO;
37 using System.Text;
38 using System.Text.RegularExpressions;
39 using System.Web.Caching;
40 using System.Web.Configuration;
41 using System.Web.Hosting;
42 using System.Web.UI;
43 using System.Web.UI.HtmlControls;
44 using System.Web.Util;
45
46 #if NET_2_0
47 using System.Collections.Generic;
48 #endif
49
50 namespace System.Web.Compilation
51 {
52         class BuilderLocation
53         {
54                 public ControlBuilder Builder;
55                 public ILocation Location;
56
57                 public BuilderLocation (ControlBuilder builder, ILocation location)
58                 {
59                         this.Builder = builder;
60                         this.Location = new Location (location);
61                 }
62         }
63
64         class BuilderLocationStack : Stack
65         {
66                 public override void Push (object o)
67                 {
68                         if (!(o is BuilderLocation))
69                                 throw new InvalidOperationException ();
70
71                         base.Push (o);
72                 }
73                 
74                 public virtual void Push (ControlBuilder builder, ILocation location)
75                 {
76                         BuilderLocation bl = new BuilderLocation (builder, location);
77                         Push (bl);
78                 }
79
80                 public new BuilderLocation Peek ()
81                 {
82                         return (BuilderLocation) base.Peek ();
83                 }
84
85                 public new BuilderLocation Pop ()
86                 {
87                         return (BuilderLocation) base.Pop ();
88                 }
89
90                 public ControlBuilder Builder {
91                         get { return Peek ().Builder; }
92                 }
93         }
94
95         class ParserStack
96         {
97                 Hashtable files;
98                 Stack parsers;
99                 AspParser current;
100
101                 public ParserStack ()
102                 {
103                         files = new Hashtable (); // may be this should be case sensitive for windows
104                         parsers = new Stack ();
105                 }
106                 
107                 public bool Push (AspParser parser)
108                 {
109                         if (files.Contains (parser.Filename))
110                                 return false;
111
112                         files [parser.Filename] = true;
113                         parsers.Push (parser);
114                         current = parser;
115                         return true;
116                 }
117
118                 public AspParser Pop ()
119                 {
120                         if (parsers.Count == 0)
121                                 return null;
122
123                         files.Remove (current.Filename);
124                         AspParser result = (AspParser) parsers.Pop ();
125                         if (parsers.Count > 0)
126                                 current = (AspParser) parsers.Peek ();
127                         else
128                                 current = null;
129
130                         return result;
131                 }
132
133                 public int Count {
134                         get { return parsers.Count; }
135                 }
136                 
137                 public AspParser Parser {
138                         get { return current; }
139                 }
140
141                 public string Filename {
142                         get { return current.Filename; }
143                 }
144         }
145
146         class TagStack
147         {
148                 Stack tags;
149
150                 public TagStack ()
151                 {
152                         tags = new Stack ();
153                 }
154                 
155                 public void Push (string tagid)
156                 {
157                         tags.Push (tagid);
158                 }
159
160                 public string Pop ()
161                 {
162                         if (tags.Count == 0)
163                                 return null;
164
165                         return (string) tags.Pop ();
166                 }
167
168                 public bool CompareTo (string tagid)
169                 {
170                         if (tags.Count == 0)
171                                 return false;
172
173                         return 0 == String.Compare (tagid, (string) tags.Peek (), true, CultureInfo.InvariantCulture);
174                 }
175                 
176                 public int Count {
177                         get { return tags.Count; }
178                 }
179
180                 public string Current {
181                         get { return (string) tags.Peek (); }
182                 }
183         }
184
185         enum TextBlockType
186         {
187                 Verbatim,
188                 Expression,
189                 Tag,
190                 Comment
191         }
192         
193         sealed class TextBlock
194         {
195                 public string Content;
196                 public readonly TextBlockType Type;
197                 public readonly int Length;
198                 
199                 public TextBlock (TextBlockType type, string content)
200                 {
201                         Content = content;
202                         Type = type;
203                         Length = content.Length;
204                 }
205
206                 public override string ToString ()
207                 {
208                         return this.GetType ().FullName + " [" + this.Type + "]";
209                 }
210         }
211         
212         class AspGenerator
213         {
214 #if NET_2_0
215                 const int READ_BUFFER_SIZE = 8192;
216                 
217                 internal static Regex DirectiveRegex = new Regex (@"<%\s*@(\s*(?<attrname>\w[\w:]*(?=\W))(\s*(?<equal>=)\s*""(?<attrval>[^""]*)""|\s*(?<equal>=)\s*'(?<attrval>[^']*)'|\s*(?<equal>=)\s*(?<attrval>[^\s%>]*)|(?<equal>)(?<attrval>\s*?)))*\s*?%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
218 #endif
219                 static readonly Regex runatServer = new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/?>",
220                                                                RegexOptions.Compiled | RegexOptions.Singleline |
221                                                                RegexOptions.Multiline | RegexOptions.IgnoreCase |
222                                                                RegexOptions.CultureInvariant);
223                 
224                 static readonly Regex endOfTag = new Regex (@"</[\w:\.]+\s*?>",
225                                                             RegexOptions.Compiled | RegexOptions.Singleline |
226                                                             RegexOptions.Multiline | RegexOptions.IgnoreCase |
227                                                             RegexOptions.CultureInvariant);
228                 
229                 static readonly Regex expressionRegex = new Regex (@"<%.*?%>",
230                                                                    RegexOptions.Compiled | RegexOptions.Singleline |
231                                                                    RegexOptions.Multiline | RegexOptions.IgnoreCase |
232                                                                    RegexOptions.CultureInvariant);
233
234                 static readonly Regex clientCommentRegex = new Regex (@"<!--(.|\s)*?-->",
235                                                                       RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase |
236                                                                       RegexOptions.CultureInvariant);
237                 
238                 ParserStack pstack;
239                 BuilderLocationStack stack;
240                 TemplateParser tparser;
241                 StringBuilder text;
242                 RootBuilder rootBuilder;
243                 bool inScript, javascript, ignore_text;
244                 ILocation location;
245                 bool isApplication;
246                 StringBuilder tagInnerText = new StringBuilder ();
247                 static Hashtable emptyHash = new Hashtable ();
248                 bool inForm;
249                 bool useOtherTags;
250                 TagType lastTag;
251 #if NET_2_0
252                 AspComponentFoundry componentFoundry;
253                 Stream inputStream;
254
255                 public AspGenerator (TemplateParser tparser, AspComponentFoundry componentFoundry) : this (tparser)
256                 {
257                         this.componentFoundry = componentFoundry;
258                 }
259 #endif
260                 
261                 public AspGenerator (TemplateParser tparser)
262                 {
263                         this.tparser = tparser;
264                         text = new StringBuilder ();
265                         stack = new BuilderLocationStack ();
266
267 #if !NET_2_0
268                         rootBuilder = new RootBuilder (tparser);
269                         tparser.RootBuilder = rootBuilder;
270                         stack.Push (rootBuilder, null);
271 #endif
272                         pstack = new ParserStack ();
273                 }
274
275                 public RootBuilder RootBuilder {
276                         get { return rootBuilder; }
277                 }
278
279                 public AspParser Parser {
280                         get { return pstack.Parser; }
281                 }
282                 
283                 public string Filename {
284                         get { return pstack.Filename; }
285                 }
286
287 #if NET_2_0
288                 PageParserFilter PageParserFilter {
289                         get {
290                                 if (tparser == null)
291                                         return null;
292
293                                 return tparser.PageParserFilter;
294                         }
295                 }
296
297                 // KLUDGE WARNING
298                 //
299                 // The kludge to determine the base type of the to-be-generated ASP.NET class is
300                 // very unfortunate but with our current parser it is, unfortunately, necessary. The
301                 // reason for reading the entire file into memory and parsing it with a regexp is
302                 // that we need to read the main directive (i.e. <%@Page %>, <%@Control %> etc),
303                 // pass it to the page parser filter if it exists, and finally read the inherits
304                 // attribute of the directive to get access to the base type of the class to be
305                 // generated. On that type we check whether it is decorated with the
306                 // FileLevelControlBuilder attribute and, if yes, use the indicated type as the
307                 // RootBuilder. This is necessary for the ASP.NET MVC views using the "generic"
308                 // inherits declaration to work properly. Our current parser is not able to parse
309                 // the input file out of sequence (i.e. directives first, then the rest) so we need
310                 // to do what we do below, alas.
311                 Hashtable GetDirectiveAttributesDictionary (string skipKeyName, CaptureCollection names, CaptureCollection values)
312                 {
313                         var ret = new Hashtable (StringComparer.InvariantCultureIgnoreCase);
314
315                         int index = 0;
316                         string keyName;
317                         foreach (Capture c in names) {
318                                 keyName = c.Value;
319                                 if (String.Compare (skipKeyName, keyName, StringComparison.OrdinalIgnoreCase) == 0) {
320                                         index++;
321                                         continue;
322                                 }
323                                 
324                                 ret.Add (c.Value, values [index++].Value);
325                         }
326
327                         return ret;
328                 }
329
330                 string GetDirectiveName (CaptureCollection names)
331                 {
332                         string val;
333                         foreach (Capture c in names) {
334                                 val = c.Value;
335                                 if (Directive.IsDirective (val))
336                                         return val;
337                         }
338
339                         return tparser.DefaultDirectiveName;
340                 }
341
342                 int GetLineNumberForIndex (string fileContents, int index)
343                 {
344                         int line = 1;
345                         char c;
346                         bool foundCR = false;
347                         
348                         for (int pos = 0; pos < index; pos++) {
349                                 c = fileContents [pos];
350                                 if (c == '\n' || foundCR) {
351                                         line++;
352                                         foundCR = false;
353                                 }
354                                 
355                                 foundCR = (c == '\r');
356                         }
357
358                         return line;
359                 }
360
361                 int GetNumberOfLinesForRange (string fileContents, int index, int length)
362                 {
363                         int lines = 0;
364                         int stop = index + length;
365                         char c;
366                         bool foundCR = false;
367                         
368                         for (int pos = index; pos < stop; pos++) {
369                                 c = fileContents [pos];
370                                 if (c == '\n' || foundCR) {
371                                         lines++;
372                                         foundCR = false;
373                                 }
374
375                                 foundCR = (c == '\r');
376                         }
377
378                         return lines;
379                 }
380                 
381                 Type GetInheritedType (string fileContents, string filename)
382                 {
383                         MatchCollection matches = DirectiveRegex.Matches (fileContents);
384                         if (matches == null || matches.Count == 0)
385                                 return null;
386
387                         string wantedDirectiveName = tparser.DefaultDirectiveName.ToLower ();
388                         string directiveName;
389                         GroupCollection groups;
390                         CaptureCollection ccNames;
391                         
392                         foreach (Match match in matches) {
393                                 groups = match.Groups;
394                                 if (groups.Count < 6)
395                                         continue;
396
397                                 ccNames = groups [3].Captures;
398                                 directiveName = GetDirectiveName (ccNames);
399                                 if (String.IsNullOrEmpty (directiveName))
400                                         continue;
401                                 
402                                 if (String.Compare (directiveName.ToLower (), wantedDirectiveName, StringComparison.Ordinal) != 0)
403                                         continue;
404
405                                 var loc = new Location (null);
406                                 int index = match.Index;
407                                 
408                                 loc.Filename = filename;
409                                 loc.BeginLine = GetLineNumberForIndex (fileContents, index);
410                                 loc.EndLine = loc.BeginLine + GetNumberOfLinesForRange (fileContents, index, match.Length);
411                                 
412                                 tparser.Location = loc;
413                                 tparser.allowedMainDirectives = 2;
414                                 tparser.AddDirective (wantedDirectiveName, GetDirectiveAttributesDictionary (wantedDirectiveName, ccNames, groups [5].Captures));
415
416                                 return tparser.BaseType;
417                         }
418                         
419                         return null;
420                 }
421
422                 string ReadFileContents (Stream inputStream, string filename)
423                 {
424                         string ret = null;
425                         
426                         if (inputStream != null) {
427                                 if (inputStream.CanSeek) {
428                                         long curPos = inputStream.Position;
429                                         inputStream.Seek (0, SeekOrigin.Begin);
430
431                                         Encoding enc = WebEncoding.FileEncoding;
432                                         StringBuilder sb = new StringBuilder ();
433                                         byte[] buffer = new byte [READ_BUFFER_SIZE];
434                                         int nbytes;
435                                         
436                                         while ((nbytes = inputStream.Read (buffer, 0, READ_BUFFER_SIZE)) > 0)
437                                                 sb.Append (enc.GetString (buffer, 0, nbytes));
438                                         inputStream.Seek (curPos, SeekOrigin.Begin);
439                                         
440                                         ret = sb.ToString ();
441                                         sb.Length = 0;
442                                         sb.Capacity = 0;
443                                 } else {
444                                         FileStream fs = inputStream as FileStream;
445                                         if (fs != null) {
446                                                 string fname = fs.Name;
447                                                 try {
448                                                         if (File.Exists (fname))
449                                                                 ret = File.ReadAllText (fname);
450                                                 } catch {
451                                                         // ignore
452                                                 }
453                                         }
454                                 }
455                         }
456
457                         if (ret == null && !String.IsNullOrEmpty (filename) && String.Compare (filename, "@@inner_string@@", StringComparison.Ordinal) != 0) {
458                                 try {
459                                         if (File.Exists (filename))
460                                                 ret = File.ReadAllText (filename);
461                                 } catch {
462                                         // ignore
463                                 }
464                         }
465
466                         return ret;
467                 }
468                 
469                 Type GetRootBuilderType (Stream inputStream, string filename)
470                 {
471                         Type ret = null;
472                         string fileContents;
473
474                         if (tparser != null)
475                                 fileContents = ReadFileContents (inputStream, filename);
476                         else
477                                 fileContents = null;
478                         
479                         if (!String.IsNullOrEmpty (fileContents)) {
480                                 Type inheritedType = GetInheritedType (fileContents, filename);
481                                 fileContents = null;
482                                 if (inheritedType != null) {
483                                         FileLevelControlBuilderAttribute attr;
484                                         
485                                         try {
486                                                 object[] attrs = inheritedType.GetCustomAttributes (typeof (FileLevelControlBuilderAttribute), true);
487                                                 if (attrs != null && attrs.Length > 0)
488                                                         attr = attrs [0] as FileLevelControlBuilderAttribute;
489                                                 else
490                                                         attr = null;
491                                         } catch {
492                                                 attr = null;
493                                         }
494
495                                         ret = attr != null ? attr.BuilderType : null;
496                                 }
497                         }
498                         
499                         if (ret == null) {
500                                 if (tparser is PageParser)
501                                         return typeof (FileLevelPageControlBuilder);
502                                 else if (tparser is UserControlParser)
503                                         return typeof (FileLevelUserControlBuilder);
504                                 else
505                                         return typeof (RootBuilder);
506                         } else
507                                 return ret;
508                 }
509                 
510                 void CreateRootBuilder (Stream inputStream, string filename)
511                 {
512                         if (rootBuilder != null)
513                                 return;
514                         
515                         Type rootBuilderType = GetRootBuilderType (inputStream, filename);
516                         rootBuilder = Activator.CreateInstance (rootBuilderType) as RootBuilder;
517                         if (rootBuilder == null)
518                                 throw new HttpException ("Cannot create an instance of file-level control builder.");
519                         rootBuilder.Init (tparser, null, null, null, null, null);
520                         if (componentFoundry != null)
521                                 rootBuilder.Foundry = componentFoundry;
522                         
523                         stack.Push (rootBuilder, null);
524                         tparser.RootBuilder = rootBuilder;
525                 }
526 #endif
527                 
528                 BaseCompiler GetCompilerFromType ()
529                 {
530                         Type type = tparser.GetType ();
531                         if (type == typeof (PageParser))
532                                 return new PageCompiler ((PageParser) tparser);
533
534                         if (type == typeof (ApplicationFileParser))
535                                 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
536
537                         if (type == typeof (UserControlParser))
538                                 return new UserControlCompiler ((UserControlParser) tparser);
539 #if NET_2_0
540                         if (type == typeof(MasterPageParser))
541                                 return new MasterPageCompiler ((MasterPageParser) tparser);
542 #endif
543
544                         throw new Exception ("Got type: " + type);
545                 }
546
547                 void InitParser (TextReader reader, string filename)
548                 {
549                         AspParser parser = new AspParser (filename, reader);
550                         parser.Error += new ParseErrorHandler (ParseError);
551                         parser.TagParsed += new TagParsedHandler (TagParsed);
552                         parser.TextParsed += new TextParsedHandler (TextParsed);
553 #if NET_2_0
554                         parser.ParsingComplete += new ParsingCompleteHandler (ParsingCompleted);
555                         tparser.AspGenerator = this;
556                         CreateRootBuilder (inputStream, filename);
557 #endif
558                         if (!pstack.Push (parser))
559                                 throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
560
561                         if (filename != "@@inner_string@@") {
562                                 string arvp = Path.Combine (tparser.BaseVirtualDir, Path.GetFileName (filename));
563                                 if (VirtualPathUtility.IsAbsolute (arvp))
564                                         arvp = VirtualPathUtility.ToAppRelative (arvp);
565                                 
566                                 tparser.AddDependency (arvp);
567                         }
568                 }
569                 
570 #if NET_2_0
571                 void InitParser (string filename)
572                 {
573                         StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
574                         InitParser (reader, filename);
575                 }
576 #endif
577
578                 void CheckForDuplicateIds (ControlBuilder root, Stack scopes)
579                 {
580                         if (root == null)
581                                 return;
582                         
583                         if (scopes == null)
584                                 scopes = new Stack ();
585                         
586 #if NET_2_0
587                         Dictionary <string, bool> ids;
588 #else
589                         Hashtable ids;
590 #endif
591                         
592                         if (scopes.Count == 0 || root.IsNamingContainer) {
593 #if NET_2_0
594                                 ids = new Dictionary <string, bool> (StringComparer.Ordinal);
595 #else
596                                 ids = new Hashtable ();
597 #endif
598                                 scopes.Push (ids);
599                         } else {
600 #if NET_2_0
601                                 ids = scopes.Peek () as Dictionary <string, bool>;
602 #else
603                                 ids = scopes.Peek () as Hashtable;
604 #endif
605                         }
606                         
607                         if (ids == null)
608                                 return;
609
610                         ControlBuilder cb;
611                         string id;
612                         ArrayList children = root.Children;
613                         if (children != null) {
614                                 foreach (object o in children) {
615                                         cb = o as ControlBuilder;
616                                         if (cb == null)
617                                                 continue;
618
619                                         id = cb.ID;
620                                         if (id == null || id.Length == 0)
621                                                 continue;
622                                 
623                                         if (ids.ContainsKey (id))
624                                                 throw new ParseException (cb.Location, "Id '" + id + "' is already used by another control.");
625
626                                         ids.Add (id, true);
627                                         CheckForDuplicateIds (cb, scopes);
628                                 }
629                         }
630                 }
631                 
632                 public void Parse (string file)
633                 {
634 #if ONLY_1_1
635                         Parse (file, true);
636 #else
637                         Parse (file, false);
638 #endif
639                 }
640                 
641                 public void Parse (TextReader reader, string filename, bool doInitParser)
642                 {
643                         try {
644                                 isApplication = tparser.DefaultDirectiveName == "application";
645
646                                 if (doInitParser)
647                                         InitParser (reader, filename);
648
649                                 pstack.Parser.Parse ();
650                                 if (text.Length > 0)
651                                         FlushText ();
652
653 #if NET_2_0
654                                 tparser.MD5Checksum = pstack.Parser.MD5Checksum;
655 #endif
656                                 pstack.Pop ();
657
658 #if DEBUG
659                                 PrintTree (RootBuilder, 0);
660 #endif
661
662                                 if (stack.Count > 1 && pstack.Count == 0)
663                                         throw new ParseException (stack.Builder.Location,
664                                                                   "Expecting </" + stack.Builder.TagName + "> " + stack.Builder);
665
666                                 CheckForDuplicateIds (RootBuilder, null);
667                         } finally {
668                                 if (reader != null)
669                                         reader.Close ();
670                         }
671                 }
672
673                 public void Parse (Stream stream, string filename, bool doInitParser)
674                 {
675 #if NET_2_0
676                         inputStream = stream;
677 #endif
678                         Parse (new StreamReader (stream, WebEncoding.FileEncoding), filename, doInitParser);
679                 }
680                 
681                 public void Parse (string filename, bool doInitParser)
682                 {
683                         StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
684                         Parse (reader, filename, doInitParser);
685                 }
686
687                 public void Parse ()
688                 {
689 #if NET_2_0
690                         string inputFile = tparser.InputFile;
691                         TextReader inputReader = tparser.Reader;
692
693                         try {                   
694                                 if (String.IsNullOrEmpty (inputFile)) {
695                                         StreamReader sr = inputReader as StreamReader;
696                                         if (sr != null) {
697                                                 FileStream fr = sr.BaseStream as FileStream;
698                                                 if (fr != null)
699                                                         inputFile = fr.Name;
700                                         }
701
702                                         if (String.IsNullOrEmpty (inputFile))
703                                                 inputFile = "@@inner_string@@";
704                                 }
705
706                                 if (inputReader != null) {
707                                         Parse (inputReader, inputFile, true);
708                                 } else {
709                                         if (String.IsNullOrEmpty (inputFile))
710                                                 throw new HttpException ("Parser input file is empty, cannot continue.");
711                                         inputFile = Path.GetFullPath (inputFile);
712                                         InitParser (inputFile);
713                                         Parse (inputFile);
714                                 }
715                         } finally {
716                                 if (inputReader != null)
717                                         inputReader.Close ();
718                         }
719 #else
720                         Parse (Path.GetFullPath (tparser.InputFile));
721 #endif
722                 }
723
724                 internal static void AddTypeToCache (ArrayList dependencies, string inputFile, Type type)
725                 {
726                         if (type == null || inputFile == null || inputFile.Length == 0)
727                                 return;
728
729                         if (dependencies != null && dependencies.Count > 0) {
730                                 string [] deps = (string []) dependencies.ToArray (typeof (string));
731                                 HttpContext ctx = HttpContext.Current;
732                                 HttpRequest req = ctx != null ? ctx.Request : null;
733                                 
734                                 if (req == null)
735                                         throw new HttpException ("No current context, cannot compile.");
736
737                                 for (int i = 0; i < deps.Length; i++)
738                                         deps [i] = req.MapPath (deps [i]);
739
740                                 HttpRuntime.InternalCache.Insert ("@@Type" + inputFile, type, new CacheDependency (deps));
741                         } else
742                                 HttpRuntime.InternalCache.Insert ("@@Type" + inputFile, type);
743                 }
744                 
745                 public Type GetCompiledType ()
746                 {
747                         Type type = (Type) HttpRuntime.InternalCache.Get ("@@Type" + tparser.InputFile);
748                         if (type != null) {
749                                 return type;
750                         }
751
752                         Parse ();
753
754                         BaseCompiler compiler = GetCompilerFromType ();
755                         
756                         type = compiler.GetCompiledType ();
757                         AddTypeToCache (tparser.Dependencies, tparser.InputFile, type);
758                         return type;
759                 }
760
761 #if DEBUG
762                 static void PrintTree (ControlBuilder builder, int indent)
763                 {
764                         if (builder == null)
765                                 return;
766
767                         string i = new string ('\t', indent);
768                         Console.Write (i);
769                         Console.WriteLine ("b: {0}; naming container: {1}; id: {2}; type: {3}; parent: {4}",
770                                            builder, builder.IsNamingContainer, builder.ID, builder.ControlType, builder.ParentBuilder);
771
772                         if (builder.Children != null)
773                         foreach (object o in builder.Children) {
774                                 if (o is ControlBuilder)
775                                         PrintTree ((ControlBuilder) o, indent++);
776                         }
777                 }
778                 
779                 static void PrintLocation (ILocation loc)
780                 {
781                         Console.WriteLine ("\tFile name: " + loc.Filename);
782                         Console.WriteLine ("\tBegin line: " + loc.BeginLine);
783                         Console.WriteLine ("\tEnd line: " + loc.EndLine);
784                         Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
785                         Console.WriteLine ("\tEnd column: " + loc.EndColumn);
786                         Console.WriteLine ("\tPlainText: " + loc.PlainText);
787                         Console.WriteLine ();
788                 }
789 #endif
790
791                 void ParseError (ILocation location, string message)
792                 {
793                         throw new ParseException (location, message);
794                 }
795
796                 // KLUDGE WARNING!!
797                 //
798                 // The code below (ProcessTagsInAttributes, ParseAttributeTag) serves the purpose to work
799                 // around a limitation of the current asp.net parser which is unable to parse server
800                 // controls inside client tag attributes. Since the architecture of the current
801                 // parser does not allow for clean solution of this problem, hence the kludge
802                 // below. It will be gone as soon as the parser is rewritten.
803                 //
804                 // The kludge supports only self-closing tags inside attributes.
805                 //
806                 // KLUDGE WARNING!!
807                 bool ProcessTagsInAttributes (ILocation location, string tagid, TagAttributes attributes, TagType type)
808                 {
809                         if (attributes == null || attributes.Count == 0)
810                                 return false;
811                         
812                         Match match;
813                         Group group;
814                         string value;
815                         bool retval = false;
816                         int index, length;
817                         StringBuilder sb = new StringBuilder ();
818
819                         sb.AppendFormat ("\t<{0}", tagid);
820                         foreach (string key in attributes.Keys) {
821                                 value = attributes [key] as string;
822                                 if (value == null || value.Length < 16) { // optimization
823                                         sb.AppendFormat (" {0}=\"{1}\"", key, value);
824                                         continue;
825                                 }
826                                 
827                                 match = runatServer.Match (attributes [key] as string);
828                                 if (!match.Success) {
829                                         sb.AppendFormat (" {0}=\"{1}\"", key, value);
830                                         continue;
831                                 }
832                                 if (sb.Length > 0) {
833                                         TextParsed (location, sb.ToString ());
834                                         sb.Length = 0;
835                                 }
836                                 
837                                 retval = true;
838                                 group = match.Groups [0];
839                                 index = group.Index;
840                                 length = group.Length;
841
842                                 TextParsed (location, String.Format (" {0}=\"{1}", key, index > 0 ? value.Substring (0, index) : String.Empty));;
843                                 FlushText ();
844                                 ParseAttributeTag (group.Value, location);
845                                 if (index + length < value.Length)
846                                         TextParsed (location, value.Substring (index + length) + "\"");
847                                 else
848                                         TextParsed (location, "\"");
849                         }
850                         if (type == TagType.SelfClosing)
851                                 sb.Append ("/>");
852                         else
853                                 sb.Append (">");
854
855                         if (retval && sb.Length > 0)
856                                 TextParsed (location, sb.ToString ());
857                         
858                         return retval;
859                 }
860
861                 void ParseAttributeTag (string code, ILocation location)
862                 {
863                         AspParser parser = new AspParser ("@@attribute_tag@@", new StringReader (code), location.BeginLine - 1, location as AspParser);
864                         parser.Error += new ParseErrorHandler (ParseError);
865                         parser.TagParsed += new TagParsedHandler (TagParsed);
866                         parser.TextParsed += new TextParsedHandler (TextParsed);
867                         parser.Parse ();
868                         if (text.Length > 0)
869                                 FlushText ();
870                 }
871
872 #if NET_2_0
873                 void ParsingCompleted ()
874                 {
875                         PageParserFilter pfilter = PageParserFilter;
876                         if (pfilter == null)
877                                 return;
878
879                         pfilter.ParseComplete (RootBuilder);
880                 }
881 #endif
882
883                 void CheckIfIncludeFileIsSecure (string filePath)
884                 {
885                         if (filePath == null || filePath.Length == 0)
886                                 return;
887                         
888                         // a bit slow, but fully portable
889                         string newdir = null;
890                         Exception exception = null;
891                         try {
892                                 string origdir = Directory.GetCurrentDirectory ();
893                                 Directory.SetCurrentDirectory (Path.GetDirectoryName (filePath));
894                                 newdir = Directory.GetCurrentDirectory ();
895                                 Directory.SetCurrentDirectory (origdir);
896                                 if (newdir [newdir.Length - 1] != '/')
897                                         newdir += "/";
898                         } catch (DirectoryNotFoundException) {
899                                 return; // will be converted into 404
900                         } catch (FileNotFoundException) {
901                                 return; // as above
902                         } catch (Exception ex) {
903                                 // better safe than sorry
904                                 exception = ex;
905                         }
906
907                         if (exception != null || !StrUtils.StartsWith (newdir, HttpRuntime.AppDomainAppPath))
908                                 throw new ParseException (Location, "Files above the application's root directory cannot be included.");
909                 }
910                 
911                 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
912                 {
913                         bool tagIgnored;
914                         
915                         this.location = new Location (location);
916                         if (tparser != null)
917                                 tparser.Location = location;
918
919                         if (text.Length != 0)
920                                 FlushText (lastTag == TagType.CodeRender);
921
922                         if (0 == String.Compare (tagid, "script", true, CultureInfo.InvariantCulture)) {
923                                 bool in_script = (inScript || ignore_text);
924                                 if (in_script) {
925                                         if (ProcessScript (tagtype, attributes))
926                                                 return;
927                                 } else
928                                         if (ProcessScript (tagtype, attributes))
929                                                 return;
930                         }
931
932                         lastTag = tagtype;
933                         switch (tagtype) {
934                         case TagType.Directive:
935                                 if (tagid.Length == 0)
936                                         tagid = tparser.DefaultDirectiveName;
937
938                                 tparser.AddDirective (tagid, attributes.GetDictionary (null));
939                                 break;
940                         case TagType.Tag:
941                                 if (ProcessTag (location, tagid, attributes, tagtype, out tagIgnored)) {
942                                         if (!tagIgnored)
943                                                 useOtherTags = true;
944                                         break;
945                                 }
946
947                                 if (useOtherTags) {
948                                         stack.Builder.EnsureOtherTags ();
949                                         stack.Builder.OtherTags.Add (tagid);
950                                 }
951
952                                 {
953                                         string plainText = location.PlainText;
954                                         if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.Tag))
955                                                 TextParsed (location, plainText);
956                                 }
957                                 break;
958                         case TagType.Close:
959                                 bool notServer = (useOtherTags && TryRemoveTag (tagid, stack.Builder.OtherTags));
960                                 if (!notServer && CloseControl (tagid))
961                                         break;
962                                 
963                                 TextParsed (location, location.PlainText);
964                                 break;
965                         case TagType.SelfClosing:
966                                 int count = stack.Count;
967                                 if (!ProcessTag (location, tagid, attributes, tagtype, out tagIgnored) && !tagIgnored) {
968                                         string plainText = location.PlainText;
969                                         if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.SelfClosing))
970                                                 TextParsed (location, plainText);
971                                 } else if (stack.Count != count) {
972                                         CloseControl (tagid);
973                                 }
974                                 break;
975                         case TagType.DataBinding:
976                                 goto case TagType.CodeRender;
977                         case TagType.CodeRenderExpression:
978                                 goto case TagType.CodeRender;
979                         case TagType.CodeRender:
980                                 if (isApplication)
981                                         throw new ParseException (location, "Invalid content for application file.");
982                         
983                                 ProcessCode (tagtype, tagid, location);
984                                 break;
985                         case TagType.Include:
986                                 if (isApplication)
987                                         throw new ParseException (location, "Invalid content for application file.");
988                         
989                                 string file = attributes ["virtual"] as string;
990                                 bool isvirtual = (file != null);
991                                 if (!isvirtual)
992                                         file = attributes ["file"] as string;
993
994                                 if (isvirtual) {
995                                         bool parsed = false;
996 #if NET_2_0
997                                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
998
999                                         if (vpp.FileExists (file)) {
1000                                                 VirtualFile vf = vpp.GetFile (file);
1001                                                 if (vf != null) {
1002                                                         Parse (vf.Open (), file, true);
1003                                                         parsed = true;
1004                                                 }
1005                                         }
1006 #endif
1007                                         
1008                                         if (!parsed)
1009                                                 Parse (tparser.MapPath (file), true);
1010                                 } else {
1011                                         string includeFilePath = GetIncludeFilePath (tparser.ParserDir, file);
1012                                         CheckIfIncludeFileIsSecure (includeFilePath);
1013                                         tparser.PushIncludeDir (Path.GetDirectoryName (includeFilePath));
1014                                         try {
1015                                                 Parse (includeFilePath, true);
1016                                         } finally {
1017                                                 tparser.PopIncludeDir ();
1018                                         }
1019                                 }
1020                                 
1021                                 break;
1022                         default:
1023                                 break;
1024                         }
1025                         //PrintLocation (location);
1026                 }
1027
1028                 static bool TryRemoveTag (string tagid, ArrayList otags)
1029                 {
1030                         if (otags == null || otags.Count == 0)
1031                                 return false;
1032
1033                         for (int idx = otags.Count - 1; idx >= 0; idx--) {
1034                                 string otagid = (string) otags [idx];
1035                                 if (0 == String.Compare (tagid, otagid, true, CultureInfo.InvariantCulture)) {
1036                                         do {
1037                                                 otags.RemoveAt (idx);
1038                                         } while (otags.Count - 1 >= idx);
1039                                         return true;
1040                                 }
1041                         }
1042                         return false;
1043                 }
1044
1045                 static string GetIncludeFilePath (string basedir, string filename)
1046                 {
1047                         if (Path.DirectorySeparatorChar == '/')
1048                                 filename = filename.Replace ("\\", "/");
1049
1050                         return Path.GetFullPath (Path.Combine (basedir, filename));
1051                 }
1052
1053                 delegate bool CheckBlockEnd (string text);
1054                 
1055                 bool CheckTagEndNeeded (string text)
1056                 {
1057                         return !text.EndsWith ("/>");
1058                 }
1059                 
1060 #if NET_2_0
1061                 List <TextBlock>
1062 #else
1063                 ArrayList
1064 #endif
1065                 FindRegexBlocks (Regex rxStart, Regex rxEnd, CheckBlockEnd checkEnd, IList blocks, TextBlockType typeForMatches, bool discardBlocks)
1066                 {
1067 #if NET_2_0
1068                         var ret = new List <TextBlock> ();
1069 #else
1070                         ArrayList ret = new ArrayList ();
1071 #endif
1072                         
1073                         foreach (TextBlock block in blocks) {
1074                                 if (block.Type != TextBlockType.Verbatim) {
1075                                         ret.Add (block);
1076                                         continue;
1077                                 }
1078
1079                                 int lastIndex = 0, index;
1080                                 MatchCollection matches = rxStart.Matches (block.Content);
1081                                 bool foundMatches = matches.Count > 0;
1082                                 foreach (Match match in matches) {
1083                                         foundMatches = true;
1084                                         index = match.Index;
1085                                         if (lastIndex < index)
1086                                                 ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex, index - lastIndex)));
1087
1088                                         string value = match.Value;
1089                                         if (rxEnd != null && checkEnd (value)) {
1090                                                 int startFrom = index + value.Length;
1091                                                 Match m = rxEnd.Match (block.Content, startFrom);
1092                                                 if (m.Success)
1093                                                         value += block.Content.Substring (startFrom, m.Index - startFrom) + m.Value;
1094                                         }
1095
1096                                         if (!discardBlocks)
1097                                                 ret.Add (new TextBlock (typeForMatches, value));
1098                                         lastIndex = index + value.Length;
1099                                 }
1100
1101                                 if (lastIndex > 0 && lastIndex < block.Content.Length)
1102                                         ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex)));
1103
1104                                 if (!foundMatches)
1105                                         ret.Add (block);
1106                         }
1107
1108                         return ret;
1109                 }
1110                 
1111                 IList SplitTextIntoBlocks (string text)
1112                 {
1113 #if NET_2_0
1114                         var ret = new List <TextBlock> ();
1115 #else
1116                         ArrayList ret = new ArrayList ();
1117 #endif
1118
1119                         ret.Add (new TextBlock (TextBlockType.Verbatim, text));
1120                         ret = FindRegexBlocks (clientCommentRegex, null, null, ret, TextBlockType.Comment, false);
1121                         ret = FindRegexBlocks (runatServer, endOfTag, CheckTagEndNeeded, ret, TextBlockType.Tag, false);
1122                         ret = FindRegexBlocks (expressionRegex, null, null, ret, TextBlockType.Expression, false);
1123
1124                         return ret;
1125                 }
1126
1127                 void TextParsed (ILocation location, string text)
1128                 {
1129                         if (ignore_text)
1130                                 return;
1131
1132                         if (inScript) {
1133                                 this.text.Append (text);
1134                                 FlushText (true);
1135                                 return;
1136                         }
1137                         
1138                         IList blocks = SplitTextIntoBlocks (text);
1139                         foreach (TextBlock block in blocks) {
1140                                 switch (block.Type) {
1141                                         case TextBlockType.Verbatim:
1142                                                 this.text.Append (block.Content);
1143                                                 break;
1144
1145                                         case TextBlockType.Expression:
1146                                                 if (this.text.Length > 0)
1147                                                         FlushText (true);
1148                                                 CodeRenderParser r = new CodeRenderParser (block.Content, stack.Builder, location);
1149                                                 r.AddChildren (this);
1150                                                 break;
1151
1152                                         case TextBlockType.Tag:
1153                                                 ParseAttributeTag (block.Content, location);
1154                                                 break;
1155
1156                                         case TextBlockType.Comment: {
1157                                                 this.text.Append ("<!--");
1158                                                 FlushText (true);
1159                                                 AspParser parser = new AspParser ("@@comment_code@@",
1160                                                                                   new StringReader (block.Content.Substring (4, block.Length - 7)),
1161                                                                                   location.BeginLine - 1,
1162                                                                                   location as AspParser);
1163                                                 parser.Error += new ParseErrorHandler (ParseError);
1164                                                 parser.TagParsed += new TagParsedHandler (TagParsed);
1165                                                 parser.TextParsed += new TextParsedHandler (TextParsed);
1166                                                 parser.Parse ();
1167                                                 this.text.Append ("-->");
1168                                                 FlushText (true);
1169                                                 break;
1170                                         }
1171                                 }
1172                         }
1173                 }
1174
1175                 void FlushText ()
1176                 {
1177                         FlushText (false);
1178                 }
1179                 
1180                 void FlushText (bool ignoreEmptyString)
1181                 {
1182                         string t = text.ToString ();
1183                         text.Length = 0;
1184
1185                         if (ignoreEmptyString && t.Trim ().Length == 0)
1186                                 return;
1187                         
1188                         if (inScript) {
1189 #if NET_2_0
1190                                 PageParserFilter pfilter = PageParserFilter;
1191                                 if (pfilter != null && !pfilter.ProcessCodeConstruct (CodeConstructType.ScriptTag, t))
1192                                         return;
1193 #endif
1194                                 tparser.Scripts.Add (new ServerSideScript (t, new System.Web.Compilation.Location (tparser.Location)));
1195                                 return;
1196                         }
1197
1198                         if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
1199                                 throw new ParseException (location, "Content not valid for application file.");
1200
1201                         ControlBuilder current = stack.Builder;
1202                         current.AppendLiteralString (t);
1203                         if (current.NeedsTagInnerText ()) {
1204                                 tagInnerText.Append (t);
1205                         }
1206                 }
1207
1208 #if NET_2_0
1209                 bool BuilderHasOtherThan (Type type, ControlBuilder cb)
1210                 {
1211                         ArrayList al = cb.OtherTags;
1212                         if (al != null && al.Count > 0)
1213                                 return true;
1214                         
1215                         al = cb.Children;
1216                         if (al != null) {
1217                                 ControlBuilder tmp;
1218                                 
1219                                 foreach (object o in al) {
1220                                         if (o == null)
1221                                                 continue;
1222                                         
1223                                         tmp = o as ControlBuilder;
1224                                         if (tmp == null) {
1225                                                 string s = o as string;
1226                                                 if (s != null && String.IsNullOrEmpty (s.Trim ()))
1227                                                         continue;
1228                                                 
1229                                                 return true;
1230                                         }
1231                                         
1232                                         if (tmp is System.Web.UI.WebControls.ContentBuilderInternal)
1233                                                 continue;
1234                                         
1235                                         if (tmp.ControlType != typeof (System.Web.UI.WebControls.Content))
1236                                                 return true;
1237                                 }
1238                         }
1239
1240                         return false;
1241                 }
1242                 
1243                 bool OtherControlsAllowed (ControlBuilder cb)
1244                 {
1245                         if (cb == null)
1246                                 return true;
1247                         
1248                         if (!typeof (System.Web.UI.WebControls.Content).IsAssignableFrom (cb.ControlType))
1249                                 return true;
1250
1251                         if (BuilderHasOtherThan (typeof (System.Web.UI.WebControls.Content), RootBuilder))
1252                                 return false;
1253                         
1254                         return true;
1255                 }
1256 #endif
1257
1258                 public void AddControl (Type type, IDictionary attributes)
1259                 {
1260                         ControlBuilder parent = stack.Builder;
1261                         ControlBuilder builder = ControlBuilder.CreateBuilderFromType (tparser, parent, type, null, null,
1262                                                                                        attributes, location.BeginLine,
1263                                                                                        location.Filename);
1264                         if (builder != null)
1265                                 parent.AppendSubBuilder (builder);
1266                 }
1267                 
1268                 bool ProcessTag (ILocation location, string tagid, TagAttributes atts, TagType tagtype, out bool ignored)
1269                 {
1270                         ignored = false;
1271                         if (isApplication) {
1272                                 if (String.Compare (tagid, "object", true, CultureInfo.InvariantCulture) != 0)
1273                                         throw new ParseException (location, "Invalid tag for application file.");
1274                         }
1275
1276                         ControlBuilder parent = stack.Builder;
1277                         ControlBuilder builder = null;
1278                         if (parent != null && parent.ControlType == typeof (HtmlTable) &&
1279                             (String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0 ||
1280                              String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0)) {
1281                                 ignored = true;
1282                                 return true;
1283                         }
1284                                 
1285                         Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
1286                         if (stack.Count > 1) {
1287                                 try {
1288                                         builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
1289                                 } catch (TypeLoadException e) {
1290                                         throw new ParseException (Location, "Type not found.", e);
1291                                 } catch (Exception e) {
1292                                         throw new ParseException (Location, e.Message, e);
1293                                 }
1294                         }
1295
1296                         bool runatServer = atts != null && atts.IsRunAtServer ();
1297                         if (builder == null && runatServer) {
1298                                 string id = htable ["id"] as string;
1299                                 if (id != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (id))
1300                                         throw new ParseException (Location, "'" + id + "' is not a valid identifier");
1301                                         
1302                                 try {
1303                                         builder = RootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
1304                                 } catch (TypeLoadException e) {
1305                                         throw new ParseException (Location, "Type not found.", e);
1306                                 } catch (HttpException e) {
1307                                         CompilationException inner = e.InnerException as CompilationException;
1308                                         if (inner != null)
1309                                                 throw inner;
1310                                         
1311                                         throw new ParseException (Location, e.Message, e);
1312                                 } catch (Exception e) {
1313                                         throw new ParseException (Location, e.Message, e);
1314                                 }
1315                         }
1316                         
1317                         if (builder == null)
1318                                 return false;
1319
1320                         // This is as good as we can do for now - if the parsed location contains
1321                         // both expressions and code render blocks then we're out of luck...
1322                         string plainText = location.PlainText;
1323                         if (!runatServer && plainText.IndexOf ("<%$") == -1&& plainText.IndexOf ("<%") > -1)
1324                                 return false;
1325 #if NET_2_0
1326                         PageParserFilter pfilter = PageParserFilter;
1327                         if (pfilter != null && !pfilter.AllowControl (builder.ControlType, builder))
1328                                 throw new ParseException (Location, "Control type '" + builder.ControlType + "' not allowed.");
1329                         
1330                         if (!OtherControlsAllowed (builder))
1331                                 throw new ParseException (Location, "Only Content controls are allowed directly in a content page that contains Content controls.");
1332 #endif
1333                         
1334                         builder.Location = location;
1335                         builder.ID = htable ["id"] as string;
1336                         if (typeof (HtmlForm).IsAssignableFrom (builder.ControlType)) {
1337                                 if (inForm)
1338                                         throw new ParseException (location, "Only one <form> allowed.");
1339
1340                                 inForm = true;
1341                         }
1342
1343                         if (builder.HasBody () && !(builder is ObjectTagBuilder)) {
1344                                 if (builder is TemplateBuilder) {
1345                                 //      push the id list
1346                                 }
1347                                 stack.Push (builder, location);
1348                         } else {
1349                                 if (!isApplication && builder is ObjectTagBuilder) {
1350                                         ObjectTagBuilder ot = (ObjectTagBuilder) builder;
1351                                         if (ot.Scope != null && ot.Scope.Length > 0)
1352                                                 throw new ParseException (location, "Scope not allowed here");
1353
1354                                         if (tagtype == TagType.Tag) {
1355                                                 stack.Push (builder, location);
1356                                                 return true;
1357                                         }
1358                                 }
1359                                 
1360                                 parent.AppendSubBuilder (builder);
1361                                 builder.CloseControl ();
1362                         }
1363
1364                         return true;
1365                 }
1366
1367                 string ReadFile (string filename)
1368                 {
1369                         string realpath = tparser.MapPath (filename);
1370                         using (StreamReader sr = new StreamReader (realpath, WebEncoding.FileEncoding)) {
1371                                 string content = sr.ReadToEnd ();
1372                                 return content;
1373                         }
1374                 }
1375
1376                 bool ProcessScript (TagType tagtype, TagAttributes attributes)
1377                 {
1378                         if (tagtype != TagType.Close) {
1379                                 if (attributes != null && attributes.IsRunAtServer ()) {
1380                                         string language = (string) attributes ["language"];
1381                                         if (language != null && language.Length > 0 && tparser.ImplicitLanguage)
1382                                                 tparser.SetLanguage (language);
1383                                         CheckLanguage (language);
1384                                         string src = (string) attributes ["src"];
1385                                         if (src != null) {
1386                                                 if (src.Length == 0)
1387                                                         throw new ParseException (Parser,
1388                                                                 "src cannot be an empty string");
1389
1390                                                 string content = ReadFile (src);
1391                                                 inScript = true;
1392                                                 TextParsed (Parser, content);
1393                                                 FlushText ();
1394                                                 inScript = false;
1395                                                 if (tagtype != TagType.SelfClosing) {
1396                                                         ignore_text = true;
1397                                                         Parser.VerbatimID = "script";
1398                                                 }
1399                                         } else if (tagtype == TagType.Tag) {
1400                                                 Parser.VerbatimID = "script";
1401                                                 inScript = true;
1402                                         }
1403
1404                                         return true;
1405                                 } else {
1406                                         if (tagtype != TagType.SelfClosing) {
1407                                                 Parser.VerbatimID = "script";
1408                                                 javascript = true;
1409                                         }
1410                                         TextParsed (location, location.PlainText);
1411                                         return true;
1412                                 }
1413                         }
1414
1415                         bool result;
1416                         if (inScript) {
1417                                 result = inScript;
1418                                 inScript = false;
1419                         } else if (!ignore_text) {
1420                                 result = javascript;
1421                                 javascript = false;
1422                                 TextParsed (location, location.PlainText);
1423                         } else {
1424                                 ignore_text = false;
1425                                 result = true;
1426                         }
1427
1428                         return result;
1429                 }
1430
1431                 bool CloseControl (string tagid)
1432                 {
1433                         ControlBuilder current = stack.Builder;
1434                         string btag = current.OriginalTagName;
1435                         if (String.Compare (btag, "tbody", true, CultureInfo.InvariantCulture) != 0 &&
1436                             String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0) {
1437                                 if (!current.ChildrenAsProperties) {
1438                                         try {
1439                                                 TextParsed (location, location.PlainText);
1440                                                 FlushText ();
1441                                         } catch {}
1442                                 }
1443                                 return true;
1444                         }
1445
1446                         if (current.ControlType == typeof (HtmlTable) && String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0)
1447                                 return true;
1448                         
1449                         if (0 != String.Compare (tagid, btag, true, CultureInfo.InvariantCulture))
1450                                 return false;
1451
1452                         // if (current is TemplateBuilder)
1453                         //      pop from the id list
1454                         if (current.NeedsTagInnerText ()) {
1455                                 try { 
1456                                         current.SetTagInnerText (tagInnerText.ToString ());
1457                                 } catch (Exception e) {
1458                                         throw new ParseException (current.Location, e.Message, e);
1459                                 }
1460
1461                                 tagInnerText.Length = 0;
1462                         }
1463
1464                         if (typeof (HtmlForm).IsAssignableFrom (current.ControlType)) {
1465                                 inForm = false;
1466                         }
1467
1468                         current.CloseControl ();
1469                         stack.Pop ();
1470                         stack.Builder.AppendSubBuilder (current);
1471                         return true;
1472                 }
1473
1474 #if NET_2_0
1475                 CodeConstructType MapTagTypeToConstructType (TagType tagtype)
1476                 {
1477                         switch (tagtype) {
1478                                 case TagType.CodeRenderExpression:
1479                                         return CodeConstructType.ExpressionSnippet;
1480
1481                                 case TagType.CodeRender:
1482                                         return CodeConstructType.CodeSnippet;
1483
1484                                 case TagType.DataBinding:
1485                                         return CodeConstructType.DataBindingSnippet;
1486
1487                                 default:
1488                                         throw new InvalidOperationException ("Unexpected tag type.");
1489                         }
1490                 }
1491                 
1492 #endif
1493                 bool ProcessCode (TagType tagtype, string code, ILocation location)
1494                 {
1495 #if NET_2_0
1496                         PageParserFilter pfilter = PageParserFilter;
1497                         // LAMESPEC:
1498                         //
1499                         // http://msdn.microsoft.com/en-us/library/system.web.ui.pageparserfilter.processcodeconstruct.aspx
1500                         //
1501                         // The above page says if false is returned then we should NOT process the
1502                         // code further, wheras in reality it's the other way around. The
1503                         // ProcessCodeConstruct return value means whether or not the filter
1504                         // _processed_ the code.
1505                         //
1506                         if (pfilter != null && (!pfilter.AllowCode || pfilter.ProcessCodeConstruct (MapTagTypeToConstructType (tagtype), code)))
1507                                 return true;
1508 #endif
1509                         ControlBuilder b = null;
1510                         if (tagtype == TagType.CodeRender)
1511                                 b = new CodeRenderBuilder (code, false, location);
1512                         else if (tagtype == TagType.CodeRenderExpression)
1513                                 b = new CodeRenderBuilder (code, true, location);
1514                         else if (tagtype == TagType.DataBinding)
1515                                 b = new DataBindingBuilder (code, location);
1516                         else
1517                                 throw new HttpException ("Should never happen");
1518
1519                         stack.Builder.AppendSubBuilder (b);
1520                         return true;
1521                 }
1522
1523                 public ILocation Location {
1524                         get { return location; }
1525                 }
1526
1527                 void CheckLanguage (string lang)
1528                 {
1529                         if (lang == null || lang == "")
1530                                 return;
1531
1532                         if (String.Compare (lang, tparser.Language, true, CultureInfo.InvariantCulture) == 0)
1533                                 return;
1534
1535 #if NET_2_0
1536                         CompilationSection section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
1537                         if (section.Compilers[tparser.Language] != section.Compilers[lang])
1538 #else
1539                         CompilationConfiguration cfg = CompilationConfiguration.GetInstance (HttpContext.Current); 
1540                         if (!cfg.Compilers.CompareLanguages (tparser.Language, lang))
1541 #endif
1542                                 throw new ParseException (Location,
1543                                                 String.Format ("Trying to mix language '{0}' and '{1}'.", 
1544                                                                 tparser.Language, lang));
1545                 }
1546
1547                 // Used to get CodeRender tags in attribute values
1548                 class CodeRenderParser
1549                 {
1550                         string str;
1551                         ControlBuilder builder;
1552                         AspGenerator generator;
1553                         ILocation location;
1554                         
1555                         public CodeRenderParser (string str, ControlBuilder builder, ILocation location)
1556                         {
1557                                 this.str = str;
1558                                 this.builder = builder;
1559                                 this.location = location;
1560                         }
1561
1562                         public void AddChildren (AspGenerator generator)
1563                         {
1564                                 this.generator = generator;
1565                                 int index = str.IndexOf ("<%");
1566                                 if (index > 0)
1567                                         DoParseExpressions (str);
1568                                 else
1569                                         DoParse (str);
1570                         }
1571
1572                         void DoParseExpressions (string str)
1573                         {
1574                                 int startIndex = 0, index = 0;
1575                                 Regex codeDirective = new Regex ("(<%(?!@)(?<code>(.|\\s)*?)%>)|(<[\\w:\\.]+.*?runat=[\"']?server[\"']?.*?/>)",
1576                                                                  RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
1577                                 Match match;
1578                                 int strLen = str.Length;
1579                                 
1580                                 while (index > -1 && startIndex < strLen) {
1581                                         match = codeDirective.Match (str, index);
1582                                         
1583                                         if (match.Success) {
1584                                                 string value = match.Value;
1585                                                 index = match.Index;
1586                                                 if (index > startIndex)
1587                                                         TextParsed (null, str.Substring (startIndex, index - startIndex));
1588                                                 DoParse (value);
1589                                                 index += value.Length;
1590                                                 startIndex = index;
1591                                         } else
1592                                                 break;
1593
1594                                         if (index < strLen)
1595                                                 index = str.IndexOf ('<', index);
1596                                         else
1597                                                 break;
1598                                 }
1599                                 
1600                                 if (startIndex < strLen)
1601                                         TextParsed (null, str.Substring (startIndex));
1602                         }
1603                         
1604                         void DoParse (string str)
1605                         {
1606                                 AspParser parser = new AspParser ("@@code_render@@", new StringReader (str), location.BeginLine - 1, location as AspParser);
1607                                 parser.Error += new ParseErrorHandler (ParseError);
1608                                 parser.TagParsed += new TagParsedHandler (TagParsed);
1609                                 parser.TextParsed += new TextParsedHandler (TextParsed);
1610                                 parser.Parse ();
1611                         }
1612
1613                         void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
1614                         {
1615                                 switch (tagtype) {
1616                                         case TagType.CodeRender:
1617                                                 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
1618                                                 break;
1619                                                 
1620                                         case TagType.CodeRenderExpression:
1621                                                 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
1622                                                 break;
1623                                                 
1624                                         case TagType.DataBinding:
1625                                                 builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
1626                                                 break;
1627
1628                                         case TagType.Tag:
1629                                         case TagType.SelfClosing:
1630                                         case TagType.Close:
1631                                                 if (generator != null)
1632                                                         generator.TagParsed (location, tagtype, tagid, attributes);
1633                                                 else
1634                                                         goto default;
1635                                                 break;
1636                                                 
1637                                         default:
1638                                                 string text = location.PlainText;
1639                                                 if (text != null && text.Trim ().Length > 0)
1640                                                         builder.AppendLiteralString (text);
1641                                                 break;
1642                                 }
1643                         }
1644
1645                         void TextParsed (ILocation location, string text)
1646                         {
1647                                 builder.AppendLiteralString (text);
1648                         }
1649
1650                         void ParseError (ILocation location, string message)
1651                         {
1652                                 throw new ParseException (location, message);
1653                         }
1654                 }
1655         }
1656 }
1657