2008-08-14 Marek Habersack <mhabersack@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 //
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
8 // Copyright (c) 2004,2006 Novell, Inc (http://www.novell.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.CodeDom.Compiler;
34 using System.Globalization;
35 using System.IO;
36 using System.Text;
37 using System.Web.Caching;
38 using System.Web.Configuration;
39 using System.Web.Hosting;
40 using System.Web.UI;
41 using System.Web.UI.HtmlControls;
42 using System.Web.Util;
43
44 namespace System.Web.Compilation
45 {
46         class BuilderLocation
47         {
48                 public ControlBuilder Builder;
49                 public ILocation Location;
50
51                 public BuilderLocation (ControlBuilder builder, ILocation location)
52                 {
53                         this.Builder = builder;
54                         this.Location = location;
55                 }
56         }
57
58         class BuilderLocationStack : Stack
59         {
60                 public override void Push (object o)
61                 {
62                         if (!(o is BuilderLocation))
63                                 throw new InvalidOperationException ();
64
65                         base.Push (o);
66                 }
67                 
68                 public virtual void Push (ControlBuilder builder, ILocation location)
69                 {
70                         BuilderLocation bl = new BuilderLocation (builder, location);
71                         Push (bl);
72                 }
73
74                 public new BuilderLocation Peek ()
75                 {
76                         return (BuilderLocation) base.Peek ();
77                 }
78
79                 public new BuilderLocation Pop ()
80                 {
81                         return (BuilderLocation) base.Pop ();
82                 }
83
84                 public ControlBuilder Builder {
85                         get { return Peek ().Builder; }
86                 }
87         }
88
89         class ParserStack
90         {
91                 Hashtable files;
92                 Stack parsers;
93                 AspParser current;
94
95                 public ParserStack ()
96                 {
97                         files = new Hashtable (); // may be this should be case sensitive for windows
98                         parsers = new Stack ();
99                 }
100                 
101                 public bool Push (AspParser parser)
102                 {
103                         if (files.Contains (parser.Filename))
104                                 return false;
105
106                         files [parser.Filename] = true;
107                         parsers.Push (parser);
108                         current = parser;
109                         return true;
110                 }
111
112                 public AspParser Pop ()
113                 {
114                         if (parsers.Count == 0)
115                                 return null;
116
117                         files.Remove (current.Filename);
118                         AspParser result = (AspParser) parsers.Pop ();
119                         if (parsers.Count > 0)
120                                 current = (AspParser) parsers.Peek ();
121                         else
122                                 current = null;
123
124                         return result;
125                 }
126
127                 public int Count {
128                         get { return parsers.Count; }
129                 }
130                 
131                 public AspParser Parser {
132                         get { return current; }
133                 }
134
135                 public string Filename {
136                         get { return current.Filename; }
137                 }
138         }
139
140         class TagStack
141         {
142                 Stack tags;
143
144                 public TagStack ()
145                 {
146                         tags = new Stack ();
147                 }
148                 
149                 public void Push (string tagid)
150                 {
151                         tags.Push (tagid);
152                 }
153
154                 public string Pop ()
155                 {
156                         if (tags.Count == 0)
157                                 return null;
158
159                         return (string) tags.Pop ();
160                 }
161
162                 public bool CompareTo (string tagid)
163                 {
164                         if (tags.Count == 0)
165                                 return false;
166
167                         return 0 == String.Compare (tagid, (string) tags.Peek (), true, CultureInfo.InvariantCulture);
168                 }
169                 
170                 public int Count {
171                         get { return tags.Count; }
172                 }
173
174                 public string Current {
175                         get { return (string) tags.Peek (); }
176                 }
177         }
178
179         class AspGenerator
180         {
181                 ParserStack pstack;
182                 BuilderLocationStack stack;
183                 TemplateParser tparser;
184                 StringBuilder text;
185                 RootBuilder rootBuilder;
186                 bool inScript, javascript, ignore_text;
187                 ILocation location;
188                 bool isApplication;
189                 StringBuilder tagInnerText = new StringBuilder ();
190                 static Hashtable emptyHash = new Hashtable ();
191                 bool inForm;
192                 bool useOtherTags;
193                 TagType lastTag;
194                 
195                 public AspGenerator (TemplateParser tparser)
196                 {
197                         this.tparser = tparser;
198                         text = new StringBuilder ();
199                         stack = new BuilderLocationStack ();
200                         rootBuilder = new RootBuilder (tparser);
201                         stack.Push (rootBuilder, null);
202                         tparser.RootBuilder = rootBuilder;
203                         pstack = new ParserStack ();
204                 }
205
206                 public RootBuilder RootBuilder {
207                         get { return tparser.RootBuilder; }
208                 }
209
210                 public AspParser Parser {
211                         get { return pstack.Parser; }
212                 }
213                 
214                 public string Filename {
215                         get { return pstack.Filename; }
216                 }
217                 
218                 BaseCompiler GetCompilerFromType ()
219                 {
220                         Type type = tparser.GetType ();
221                         if (type == typeof (PageParser))
222                                 return new PageCompiler ((PageParser) tparser);
223
224                         if (type == typeof (ApplicationFileParser))
225                                 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
226
227                         if (type == typeof (UserControlParser))
228                                 return new UserControlCompiler ((UserControlParser) tparser);
229 #if NET_2_0
230                         if (type == typeof(MasterPageParser))
231                                 return new MasterPageCompiler ((MasterPageParser) tparser);
232 #endif
233
234                         throw new Exception ("Got type: " + type);
235                 }
236
237                 void InitParser (TextReader reader, string filename)
238                 {
239                         AspParser parser = new AspParser (filename, reader);
240                         parser.Error += new ParseErrorHandler (ParseError);
241                         parser.TagParsed += new TagParsedHandler (TagParsed);
242                         parser.TextParsed += new TextParsedHandler (TextParsed);
243                         if (!pstack.Push (parser))
244                                 throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
245
246                         if (filename != "@@inner_string@@") {
247                                 string arvp = Path.Combine (tparser.BaseVirtualDir, Path.GetFileName (filename));
248                                 if (VirtualPathUtility.IsAbsolute (arvp))
249                                         arvp = VirtualPathUtility.ToAppRelative (arvp);
250                                 
251                                 tparser.AddDependency (arvp);
252                         }
253 #if NET_2_0
254                         tparser.MD5Checksum = parser.MD5Checksum;
255 #endif
256                 }
257                 
258 #if NET_2_0
259                 void InitParser (string filename)
260                 {
261                         StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
262                         InitParser (reader, filename);
263                 }
264 #endif
265                 
266                 public void Parse (string file)
267                 {
268 #if ONLY_1_1
269                         Parse (file, true);
270 #else
271                         Parse (file, false);
272 #endif
273                 }
274                 
275                 public void Parse (TextReader reader, string filename, bool doInitParser)
276                 {
277                         try {
278                                 isApplication = tparser.DefaultDirectiveName == "application";
279
280                                 if (doInitParser)
281                                         InitParser (reader, filename);
282
283                                 pstack.Parser.Parse ();
284                                 if (text.Length > 0)
285                                         FlushText ();
286
287                                 pstack.Pop ();
288
289 #if DEBUG
290                                 PrintTree (rootBuilder, 0);
291 #endif
292
293                                 if (stack.Count > 1 && pstack.Count == 0)
294                                         throw new ParseException (stack.Builder.location,
295                                                                   "Expecting </" + stack.Builder.TagName + "> " + stack.Builder);
296                         } finally {
297                                 if (reader != null)
298                                         reader.Close ();
299                         }
300                 }
301
302                 public void Parse (Stream stream, string filename, bool doInitParser)
303                 {
304                         Parse (new StreamReader (stream, WebEncoding.FileEncoding), filename, doInitParser);
305                 }
306                 
307                 public void Parse (string filename, bool doInitParser)
308                 {
309                         StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
310                         Parse (reader, filename, doInitParser);
311                 }
312
313                 public void Parse ()
314                 {
315 #if NET_2_0
316                         string inputFile = tparser.InputFile;
317                         TextReader inputReader = tparser.Reader;
318
319                         try {                   
320                                 if (String.IsNullOrEmpty (inputFile)) {
321                                         StreamReader sr = inputReader as StreamReader;
322                                         if (sr != null) {
323                                                 FileStream fr = sr.BaseStream as FileStream;
324                                                 if (fr != null)
325                                                         inputFile = fr.Name;
326                                         }
327
328                                         if (String.IsNullOrEmpty (inputFile))
329                                                 inputFile = "@@inner_string@@";
330                                 }
331
332                                 if (inputReader != null) {
333                                         Parse (inputReader, inputFile, true);
334                                 } else {
335                                         if (String.IsNullOrEmpty (inputFile))
336                                                 throw new HttpException ("Parser input file is empty, cannot continue.");
337                                         inputFile = Path.GetFullPath (inputFile);
338                                         InitParser (inputFile);
339                                         Parse (inputFile);
340                                 }
341                         } finally {
342                                 if (inputReader != null)
343                                         inputReader.Close ();
344                         }
345 #else
346                         Parse (Path.GetFullPath (tparser.InputFile));
347 #endif
348                 }
349
350                 internal static void AddTypeToCache (ArrayList dependencies, string inputFile, Type type)
351                 {
352                         string [] deps = (string []) dependencies.ToArray (typeof (string));
353                         HttpContext ctx = HttpContext.Current;
354                         HttpRequest req = ctx != null ? ctx.Request : null;
355
356                         if (req == null)
357                                 throw new HttpException ("No current context, cannot compile.");
358
359                         for (int i = 0; i < deps.Length; i++)
360                                 deps [i] = req.MapPath (deps [i]);                      
361
362                         HttpRuntime.InternalCache.Insert ("@@Type" + inputFile, type, new CacheDependency (deps));
363                 }
364                 
365                 public Type GetCompiledType ()
366                 {
367                         Type type = (Type) HttpRuntime.InternalCache.Get ("@@Type" + tparser.InputFile);
368                         if (type != null) {
369                                 return type;
370                         }
371
372                         Parse ();
373
374                         BaseCompiler compiler = GetCompilerFromType ();
375
376                         type = compiler.GetCompiledType ();
377                         AddTypeToCache (tparser.Dependencies, tparser.InputFile, type);
378                         return type;
379                 }
380
381 #if DEBUG
382                 static void PrintTree (ControlBuilder builder, int indent)
383                 {
384                         if (builder == null)
385                                 return;
386
387                         string i = new string ('\t', indent);
388                         Console.Write (i);
389                         Console.WriteLine ("b: {0} id: {1} type: {2} parent: {3}",
390                                            builder, builder.ID, builder.ControlType, builder.parentBuilder);
391
392                         if (builder.Children != null)
393                         foreach (object o in builder.Children) {
394                                 if (o is ControlBuilder)
395                                         PrintTree ((ControlBuilder) o, indent++);
396                         }
397                 }
398                 
399                 static void PrintLocation (ILocation loc)
400                 {
401                         Console.WriteLine ("\tFile name: " + loc.Filename);
402                         Console.WriteLine ("\tBegin line: " + loc.BeginLine);
403                         Console.WriteLine ("\tEnd line: " + loc.EndLine);
404                         Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
405                         Console.WriteLine ("\tEnd column: " + loc.EndColumn);
406                         Console.WriteLine ("\tPlainText: " + loc.PlainText);
407                         Console.WriteLine ();
408                 }
409 #endif
410
411                 void ParseError (ILocation location, string message)
412                 {
413                         throw new ParseException (location, message);
414                 }
415
416                 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
417                 {
418                         this.location = new Location (location);
419                         if (tparser != null)
420                                 tparser.Location = location;
421
422                         if (text.Length != 0)
423                                 FlushText (lastTag == TagType.CodeRender);
424
425                         if (0 == String.Compare (tagid, "script", true, CultureInfo.InvariantCulture)) {
426                                 bool in_script = (inScript || ignore_text);
427                                 if (in_script || (tagtype != TagType.Close && attributes != null)) {
428                                         if ((in_script || attributes.IsRunAtServer ()) && ProcessScript (tagtype, attributes))
429                                                 return;
430                                 }
431                         }
432
433                         lastTag = tagtype;
434                         switch (tagtype) {
435                         case TagType.Directive:
436                                 if (tagid == "")
437                                         tagid = tparser.DefaultDirectiveName;
438
439                                 tparser.AddDirective (tagid, attributes.GetDictionary (null));
440                                 break;
441                         case TagType.Tag:
442                                 if (ProcessTag (tagid, attributes, tagtype)) {
443                                         useOtherTags = true;
444                                         break;
445                                 }
446
447                                 if (useOtherTags) {
448                                         stack.Builder.EnsureOtherTags ();
449                                         stack.Builder.OtherTags.Add (tagid);
450                                 }
451
452                                 TextParsed (location, location.PlainText);
453                                 break;
454                         case TagType.Close:
455                                 bool notServer = (useOtherTags && TryRemoveTag (tagid, stack.Builder.OtherTags));
456                                 if (!notServer && CloseControl (tagid))
457                                         break;
458                                 
459                                 TextParsed (location, location.PlainText);
460                                 break;
461                         case TagType.SelfClosing:
462                                 int count = stack.Count;
463                                 if (!ProcessTag (tagid, attributes, tagtype)) {
464                                         TextParsed (location, location.PlainText);
465                                 } else if (stack.Count != count) {
466                                         CloseControl (tagid);
467                                 }
468                                 break;
469                         case TagType.DataBinding:
470                                 goto case TagType.CodeRender;
471                         case TagType.CodeRenderExpression:
472                                 goto case TagType.CodeRender;
473                         case TagType.CodeRender:
474                                 lastTag = TagType.CodeRender;
475                                 if (isApplication)
476                                         throw new ParseException (location, "Invalid content for application file.");
477                         
478                                 ProcessCode (tagtype, tagid, location);
479                                 break;
480                         case TagType.Include:
481                                 if (isApplication)
482                                         throw new ParseException (location, "Invalid content for application file.");
483                         
484                                 string file = attributes ["virtual"] as string;
485                                 bool isvirtual = (file != null);
486                                 if (!isvirtual)
487                                         file = attributes ["file"] as string;
488
489                                 if (isvirtual) {
490                                         bool parsed = false;
491 #if NET_2_0
492                                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
493
494                                         if (vpp.FileExists (file)) {
495                                                 VirtualFile vf = vpp.GetFile (file);
496                                                 if (vf != null) {
497                                                         Parse (vf.Open (), file, true);
498                                                         parsed = true;
499                                                 }
500                                         }
501 #endif
502                                         
503                                         if (!parsed)
504                                                 Parse (tparser.MapPath (file), true);
505                                 } else {
506                                         string includeFilePath = GetIncludeFilePath (tparser.ParserDir, file);
507                                         tparser.PushIncludeDir (Path.GetDirectoryName (includeFilePath));
508                                         try {
509                                                 Parse (includeFilePath, true);
510                                         } finally {
511                                                 tparser.PopIncludeDir ();
512                                         }
513                                 }
514                                 
515                                 break;
516                         default:
517                                 break;
518                         }
519                         //PrintLocation (location);
520                 }
521
522                 static bool TryRemoveTag (string tagid, ArrayList otags)
523                 {
524                         if (otags == null || otags.Count == 0)
525                                 return false;
526
527                         for (int idx = otags.Count - 1; idx >= 0; idx--) {
528                                 string otagid = (string) otags [idx];
529                                 if (0 == String.Compare (tagid, otagid, true, CultureInfo.InvariantCulture)) {
530                                         do {
531                                                 otags.RemoveAt (idx);
532                                         } while (otags.Count - 1 >= idx);
533                                         return true;
534                                 }
535                         }
536                         return false;
537                 }
538
539                 static string GetIncludeFilePath (string basedir, string filename)
540                 {
541                         if (Path.DirectorySeparatorChar == '/')
542                                 filename = filename.Replace ("\\", "/");
543
544                         return Path.GetFullPath (Path.Combine (basedir, filename));
545                 }
546                 
547                 void TextParsed (ILocation location, string text)
548                 {
549                         if (ignore_text)
550                                 return;
551
552                         if (text.IndexOf ("<%") != -1 && !inScript) {
553                                 if (this.text.Length > 0)
554                                         FlushText (true);
555                                 CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
556                                 r.AddChildren ();
557                                 return;
558                         }
559                         
560                         this.text.Append (text);
561                         //PrintLocation (location);
562                 }
563
564                 void FlushText ()
565                 {
566                         FlushText (false);
567                 }
568                 
569                 void FlushText (bool ignoreEmptyString)
570                 {
571                         string t = text.ToString ();
572                         text.Length = 0;
573
574                         if (ignoreEmptyString && t.Trim ().Length == 0)
575                                 return;
576                         
577                         if (inScript) {
578                                 tparser.Scripts.Add (new ServerSideScript (t, new System.Web.Compilation.Location (tparser.Location)));
579                                 return;
580                         }
581
582                         if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
583                                 throw new ParseException (location, "Content not valid for application file.");
584
585                         ControlBuilder current = stack.Builder;
586                         current.AppendLiteralString (t);
587                         if (current.NeedsTagInnerText ()) {
588                                 tagInnerText.Append (t);
589                         }
590                 }
591
592 #if NET_2_0
593                 bool BuilderHasOtherThan (Type type, ControlBuilder cb)
594                 {
595                         ArrayList al = cb.OtherTags;
596                         if (al != null && al.Count > 0)
597                                 return true;
598                         
599                         al = cb.Children;
600                         if (al != null) {
601                                 ControlBuilder tmp;
602                                 
603                                 foreach (object o in al) {
604                                         if (o == null)
605                                                 continue;
606                                         
607                                         tmp = o as ControlBuilder;
608                                         if (tmp == null) {
609                                                 string s = o as string;
610                                                 if (s != null && String.IsNullOrEmpty (s.Trim ()))
611                                                         continue;
612                                                 
613                                                 return true;
614                                         }
615                                         
616                                         if (tmp is System.Web.UI.WebControls.ContentBuilderInternal)
617                                                 continue;
618                                         
619                                         if (tmp.ControlType != typeof (System.Web.UI.WebControls.Content))
620                                                 return true;
621                                 }
622                         }
623
624                         return false;
625                 }
626                 
627                 bool OtherControlsAllowed (ControlBuilder cb)
628                 {
629                         if (cb == null)
630                                 return true;
631                         
632                         if (!typeof (System.Web.UI.WebControls.Content).IsAssignableFrom (cb.ControlType))
633                                 return true;
634
635                         if (BuilderHasOtherThan (typeof (System.Web.UI.WebControls.Content), rootBuilder))
636                                 return false;
637                         
638                         return true;
639                 }
640 #endif
641                 
642                 bool ProcessTag (string tagid, TagAttributes atts, TagType tagtype)
643                 {
644                         if (isApplication) {
645                                 if (String.Compare (tagid, "object", true, CultureInfo.InvariantCulture) != 0)
646                                         throw new ParseException (location, "Invalid tag for application file.");
647                         }
648
649                         ControlBuilder parent = stack.Builder;
650                         ControlBuilder builder = null;
651                         Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
652                         if (stack.Count > 1) {
653                                 try {
654                                         builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
655                                 } catch (TypeLoadException e) {
656                                         throw new ParseException (Location, "Type not found.", e);
657                                 } catch (Exception e) {
658                                         throw new ParseException (Location, e.Message, e);
659                                 }
660                         }
661
662                         if (builder == null && atts != null && atts.IsRunAtServer ()) {
663                                 string id = htable ["id"] as string;
664                                 if (id != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (id))
665                                         throw new ParseException (Location, "'" + id + "' is not a valid identifier");
666                                         
667                                 try {
668                                         builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
669                                 } catch (TypeLoadException e) {
670                                         throw new ParseException (Location, "Type not found.", e);
671                                 } catch (Exception e) {
672                                         throw new ParseException (Location, e.Message, e);
673                                 }
674                         }
675                         
676                         if (builder == null)
677                                 return false;
678
679 #if NET_2_0
680                         if (!OtherControlsAllowed (builder))
681                                 throw new ParseException (Location, "Only Content controls are allowed directly in a content page that contains Content controls.");
682 #endif
683                         
684                         builder.location = location;
685                         builder.ID = htable ["id"] as string;
686                         if (typeof (HtmlForm).IsAssignableFrom (builder.ControlType)) {
687                                 if (inForm)
688                                         throw new ParseException (location, "Only one <form> allowed.");
689
690                                 inForm = true;
691                         }
692
693                         if (builder.HasBody () && !(builder is ObjectTagBuilder)) {
694                                 if (builder is TemplateBuilder) {
695                                 //      push the id list
696                                 }
697                                 stack.Push (builder, location);
698                         } else {
699                                 if (!isApplication && builder is ObjectTagBuilder) {
700                                         ObjectTagBuilder ot = (ObjectTagBuilder) builder;
701                                         if (ot.Scope != null && ot.Scope != "")
702                                                 throw new ParseException (location, "Scope not allowed here");
703
704                                         if (tagtype == TagType.Tag) {
705                                                 stack.Push (builder, location);
706                                                 return true;
707                                         }
708                                 }
709                                 
710                                 parent.AppendSubBuilder (builder);
711                                 builder.CloseControl ();
712                         }
713
714                         return true;
715                 }
716
717                 string ReadFile (string filename)
718                 {
719                         string realpath = tparser.MapPath (filename);
720                         using (StreamReader sr = new StreamReader (realpath, WebEncoding.FileEncoding)) {
721                                 string content = sr.ReadToEnd ();
722                                 return content;
723                         }
724                 }
725
726                 bool ProcessScript (TagType tagtype, TagAttributes attributes)
727                 {
728                         if (tagtype != TagType.Close) {
729                                 if (attributes != null && attributes.IsRunAtServer ()) {
730                                         string language = (string) attributes ["language"];
731                                         if (language != null && language.Length > 0 && tparser.ImplicitLanguage)
732                                                 tparser.SetLanguage (language);
733                                         CheckLanguage (language);
734                                         string src = (string) attributes ["src"];
735                                         if (src != null) {
736                                                 if (src == "")
737                                                         throw new ParseException (Parser,
738                                                                 "src cannot be an empty string");
739
740                                                 string content = ReadFile (src);
741                                                 inScript = true;
742                                                 TextParsed (Parser, content);
743                                                 FlushText ();
744                                                 inScript = false;
745                                                 if (tagtype != TagType.SelfClosing) {
746                                                         ignore_text = true;
747                                                         Parser.VerbatimID = "script";
748                                                 }
749                                         } else if (tagtype == TagType.Tag) {
750                                                 Parser.VerbatimID = "script";
751                                                 inScript = true;
752                                         }
753
754                                         return true;
755                                 } else {
756                                         if (tagtype != TagType.SelfClosing) {
757                                                 Parser.VerbatimID = "script";
758                                                 javascript = true;
759                                         }
760                                         TextParsed (location, location.PlainText);
761                                         return true;
762                                 }
763                         }
764
765                         bool result;
766                         if (inScript) {
767                                 result = inScript;
768                                 inScript = false;
769                         } else if (!ignore_text) {
770                                 result = javascript;
771                                 javascript = false;
772                                 TextParsed (location, location.PlainText);
773                         } else {
774                                 ignore_text = false;
775                                 result = true;
776                         }
777
778                         return result;
779                 }
780
781                 bool CloseControl (string tagid)
782                 {
783                         ControlBuilder current = stack.Builder;
784                         string btag = current.TagName;
785                         if (String.Compare (btag, "tbody", true, CultureInfo.InvariantCulture) != 0 &&
786                             String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0) {
787                                 if (!current.ChildrenAsProperties) {
788                                         try {
789                                                 TextParsed (location, location.PlainText);
790                                                 FlushText ();
791                                         } catch {}
792                                 }
793                                 return true;
794                         }
795                         
796                         if (0 != String.Compare (tagid, btag, true, CultureInfo.InvariantCulture))
797                                 return false;
798
799                         // if (current is TemplateBuilder)
800                         //      pop from the id list
801                         if (current.NeedsTagInnerText ()) {
802                                 try { 
803                                         current.SetTagInnerText (tagInnerText.ToString ());
804                                 } catch (Exception e) {
805                                         throw new ParseException (current.location, e.Message, e);
806                                 }
807
808                                 tagInnerText.Length = 0;
809                         }
810
811                         if (typeof (HtmlForm).IsAssignableFrom (current.ControlType)) {
812                                 inForm = false;
813                         }
814
815                         current.CloseControl ();
816                         stack.Pop ();
817                         stack.Builder.AppendSubBuilder (current);
818                         return true;
819                 }
820
821                 bool ProcessCode (TagType tagtype, string code, ILocation location)
822                 {
823                         ControlBuilder b = null;
824                         if (tagtype == TagType.CodeRender)
825                                 b = new CodeRenderBuilder (code, false, location);
826                         else if (tagtype == TagType.CodeRenderExpression)
827                                 b = new CodeRenderBuilder (code, true, location);
828                         else if (tagtype == TagType.DataBinding)
829                                 b = new DataBindingBuilder (code, location);
830                         else
831                                 throw new HttpException ("Should never happen");
832
833                         stack.Builder.AppendSubBuilder (b);
834                         return true;
835                 }
836
837                 public ILocation Location {
838                         get { return location; }
839                 }
840
841                 void CheckLanguage (string lang)
842                 {
843                         if (lang == null || lang == "")
844                                 return;
845
846                         if (String.Compare (lang, tparser.Language, true, CultureInfo.InvariantCulture) == 0)
847                                 return;
848
849 #if NET_2_0
850                         CompilationSection section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
851                         if (section.Compilers[tparser.Language] != section.Compilers[lang])
852 #else
853                         CompilationConfiguration cfg = CompilationConfiguration.GetInstance (HttpContext.Current); 
854                         if (!cfg.Compilers.CompareLanguages (tparser.Language, lang))
855 #endif
856                                 throw new ParseException (Location,
857                                                 String.Format ("Trying to mix language '{0}' and '{1}'.", 
858                                                                 tparser.Language, lang));
859                 }
860
861                 // Used to get CodeRender tags in attribute values
862                 class CodeRenderParser
863                 {
864                         string str;
865                         ControlBuilder builder;
866
867                         public CodeRenderParser (string str, ControlBuilder builder)
868                         {
869                                 this.str = str;
870                                 this.builder = builder;
871                         }
872
873                         public void AddChildren ()
874                         {
875                                 int index = str.IndexOf ("<%");
876                                 if (index > 0) {
877                                         TextParsed (null, str.Substring (0, index));
878                                         str = str.Substring (index);
879                                 }
880
881                                 AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
882                                 parser.Error += new ParseErrorHandler (ParseError);
883                                 parser.TagParsed += new TagParsedHandler (TagParsed);
884                                 parser.TextParsed += new TextParsedHandler (TextParsed);
885                                 parser.Parse ();
886                         }
887
888                         void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
889                         {
890                                 if (tagtype == TagType.CodeRender)
891                                         builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
892                                 else if (tagtype == TagType.CodeRenderExpression)
893                                         builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
894                                 else if (tagtype == TagType.DataBinding)
895                                         builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
896                                 else
897                                         builder.AppendLiteralString (location.PlainText);
898                         }
899
900                         void TextParsed (ILocation location, string text)
901                         {
902                                 builder.AppendLiteralString (text);
903                         }
904
905                         void ParseError (ILocation location, string message)
906                         {
907                                 throw new ParseException (location, message);
908                         }
909                 }
910         }
911 }
912