2003-03-27 Gonzalo Paniagua Javier <gonzalo@ximian.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 //
9 using System;
10 using System.Collections;
11 using System.ComponentModel;
12 using System.Drawing;
13 using System.Diagnostics;
14 using System.IO;
15 using System.Reflection;
16 using System.Text;
17 using System.Text.RegularExpressions;
18 using System.Web.UI;
19 using System.Web.UI.HtmlControls;
20 using System.Web.UI.WebControls;
21 using System.Web.Util;
22
23 namespace System.Web.Compilation
24 {
25
26 class ControlStack
27 {
28         private Stack controls;
29         private ControlStackData top;
30         private bool space_between_tags;
31         private bool sbt_valid;
32
33         class ControlStackData 
34         {
35                 public Type controlType;
36                 public string controlID;
37                 public string tagID;
38                 public ChildrenKind childKind;
39                 public string defaultPropertyName;
40                 public int childrenNumber;
41                 public Type container;
42                 public StringBuilder dataBindFunction;
43                 public StringBuilder codeRenderFunction;
44                 public bool useCodeRender;
45                 public int codeRenderIndex;
46
47                 public ControlStackData (Type controlType,
48                                          string controlID,
49                                          string tagID,
50                                          ChildrenKind childKind,
51                                          string defaultPropertyName,
52                                          Type container)
53                 {
54                         this.controlType = controlType;
55                         this.controlID = controlID;
56                         this.tagID = tagID;
57                         this.childKind = childKind;
58                         this.defaultPropertyName = defaultPropertyName;
59                         this.container = container;
60                         childrenNumber = 0;
61                 }
62
63                 public override string ToString ()
64                 {
65                         return controlType + " " + controlID + " " + tagID + " " + childKind + " " + childrenNumber;
66                 }
67         }
68         
69         public ControlStack ()
70         {
71                 controls = new Stack ();
72         }
73
74         private Type GetContainerType (Type type)
75         {
76                 if (type != typeof (System.Web.UI.Control) &&
77                     !type.IsSubclassOf (typeof (System.Web.UI.Control)))
78                         return null;
79                 
80                 Type container_type;
81                 if (type == typeof (System.Web.UI.WebControls.DataList))
82                         container_type = typeof (System.Web.UI.WebControls.DataListItem);
83                 else if (type == typeof (System.Web.UI.WebControls.DataGrid))
84                         container_type = typeof (System.Web.UI.WebControls.DataGridItem);
85                 else if (type == typeof (System.Web.UI.WebControls.Repeater))
86                         container_type = typeof (System.Web.UI.WebControls.RepeaterItem);
87                 else if (type == typeof (ListControl) || type.IsSubclassOf (typeof (ListControl)))
88                         container_type = type;
89                 else
90                         container_type = Container;
91
92                 return container_type;
93         }
94
95         public void Push (object o)
96         {
97                 if (!(o is ControlStackData))
98                         return;
99
100                 controls.Push (o);
101                 top = (ControlStackData) o;
102                 sbt_valid = false;
103         }
104         
105         public void Push (Type controlType,
106                           string controlID,
107                           string tagID,
108                           ChildrenKind childKind,
109                           string defaultPropertyName)
110         {
111                 Type container_type = null;
112                 if (controlType != null){
113                         AddChild ();
114                         container_type = GetContainerType (controlType);
115                         if (container_type == null)
116                                 container_type = this.Container;
117                 }
118
119                 top = new ControlStackData (controlType,
120                                             controlID,
121                                             tagID,
122                                             childKind,
123                                             defaultPropertyName,
124                                             container_type);
125                 sbt_valid = false;
126                 controls.Push (top);
127         }
128
129         public object Pop ()
130         {
131                 object item = controls.Pop ();
132                 if (controls.Count != 0)
133                         top = (ControlStackData) controls.Peek ();
134                 sbt_valid = false;
135                 return item;
136         }
137
138         public Type PeekType ()
139         {
140                 return top.controlType;
141         }
142
143         public string PeekControlID ()
144         {
145                 return top.controlID;
146         }
147
148         public string PeekTagID ()
149         {
150                 return top.tagID;
151         }
152
153         public ChildrenKind PeekChildKind ()
154         {
155                 return top.childKind;
156         }
157
158         public string PeekDefaultPropertyName ()
159         {
160                 return top.defaultPropertyName;
161         }
162
163         public void AddChild ()
164         {
165                 if (top != null)
166                         top.childrenNumber++;
167         }
168
169         public bool HasDataBindFunction ()
170         {
171                 if (top.dataBindFunction == null || top.dataBindFunction.Length == 0)
172                         return false;
173                 return true;
174         }
175         
176         public bool UseCodeRender
177         {
178                 get {
179                         if (top.codeRenderFunction == null || top.codeRenderFunction.Length == 0)
180                                 return false;
181                         return top.useCodeRender;
182                 }
183
184                 set { top.useCodeRender= value; }
185         }
186         
187         public bool SpaceBetweenTags
188         {
189                 get {
190                         if (!sbt_valid){
191                                 sbt_valid = true;
192                                 Type type = top.controlType;
193                                 if (type.Namespace == "System.Web.UI.WebControls")
194                                         space_between_tags = true;
195                                 else if (type.IsSubclassOf (typeof (System.Web.UI.WebControls.WebControl)))
196                                         space_between_tags = true;
197                                 else if (type == typeof (System.Web.UI.HtmlControls.HtmlSelect))
198                                         space_between_tags = true;
199                                 else if (type == typeof (System.Web.UI.HtmlControls.HtmlTable))
200                                         space_between_tags = true;
201                                 else if (type == typeof (System.Web.UI.HtmlControls.HtmlTableRow))
202                                         space_between_tags = true;
203                                 else if (type == typeof (System.Web.UI.HtmlControls.HtmlTableCell))
204                                         space_between_tags = true;
205                                 else
206                                         space_between_tags = false;
207                         }
208                         return space_between_tags;
209                 }
210         }
211         
212         public Type Container {
213                 get {
214                         if (top == null)
215                                 return null;
216                                 
217                         return top.container;
218                 }
219         }
220         
221         public StringBuilder DataBindFunction
222         {
223                 get {
224                         if (top.dataBindFunction == null)
225                                 top.dataBindFunction = new StringBuilder ();
226                         return top.dataBindFunction;
227                 }
228         }
229
230         public int CodeRenderIndex {
231                 get {
232                         return top.codeRenderIndex++;
233                 }
234         }
235         
236         public StringBuilder CodeRenderFunction
237         {
238                 get {
239                         if (top.codeRenderFunction == null)
240                                 top.codeRenderFunction = new StringBuilder ();
241                         return top.codeRenderFunction;
242                 }
243         }
244
245         public int ChildIndex
246         {
247                 get { return top.childrenNumber - 1; }
248         }
249         
250         public int Count
251         {
252                 get { return controls.Count; }
253         }
254
255         public override string ToString ()
256         {
257                 return top.ToString () + " " + top.useCodeRender;
258         }
259                 
260 }
261
262 enum ScriptStatus
263 {
264         Close,
265         Open,
266         Text
267 }
268
269 class AspGenerator
270 {
271         object [] parts;
272         StringBuilder prolog;
273         StringBuilder declarations;
274         StringBuilder script;
275         StringBuilder constructor;
276         StringBuilder init_funcs;
277         StringBuilder epilog;
278         StringBuilder current_function;
279         StringBuilder textChunk;
280         Stack functions;
281         ControlStack controls;
282         bool parse_ok;
283         bool has_form_tag;
284         AspComponentFoundry aspFoundry;
285
286         string classDecl;
287         string className;
288         string interfaces;
289         string basetype;
290         string parent;
291         Type parentType;
292         string fullPath;
293
294         Hashtable options;
295         string privateBinPath;
296         string main_directive;
297         static string app_file_wrong = "The content in the application file is not valid.";
298
299         bool isPage;
300         bool isUserControl;
301         bool isApplication;
302
303         Element current;
304         ScriptStatus sstatus = ScriptStatus.Close;
305         string waitClosing;
306         ArrayList dependencies;
307
308         HttpContext context;
309
310         SessionState sessionState = SessionState.Enabled;
311
312         static Type styleType = typeof (System.Web.UI.WebControls.Style);
313         static Type fontinfoType = typeof (System.Web.UI.WebControls.FontInfo);
314
315         enum UserControlResult
316         {
317                 OK = 0,
318                 FileNotFound = 1,
319                 CompilationFailed = 2
320         }
321
322         enum SessionState
323         {
324                 Enabled,
325                 ReadOnly,
326                 Disabled
327         }
328         
329         public AspGenerator (string pathToFile)
330         {
331                 if (pathToFile == null)
332                         throw new ArgumentNullException ("pathToFile");
333
334                 string filename = Path.GetFileName (pathToFile);
335                 this.className = filename.Replace ('.', '_'); // Overridden by @ Page classname
336                 this.className = className.Replace ('-', '_'); 
337                 this.className = className.Replace (' ', '_');
338                 Options ["ClassName"] = this.className;
339                 this.fullPath = Path.GetFullPath (pathToFile);
340
341                 this.has_form_tag = false;
342                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
343                 privateBinPath = setup.PrivateBinPath;
344                 // This is a hack until we can run stuff in different domains
345                 if (privateBinPath == null || privateBinPath.Length == 0)
346                         privateBinPath = "bin";
347                         
348                 if (!Path.IsPathRooted (privateBinPath)) {
349                         string appbase = setup.ApplicationBase;
350                         if (appbase.StartsWith ("file://")) {
351                                 appbase = appbase.Substring (7);
352                                 if (Path.DirectorySeparatorChar != '/')
353                                         appbase = appbase.Replace ('/', Path.DirectorySeparatorChar);
354                         }
355                         privateBinPath = Path.Combine (appbase, privateBinPath);
356                 }
357                 
358                 Init ();
359         }
360
361         public string BaseType {
362                 get { return basetype; }
363
364                 set {
365                         if (parent == null)
366                                 parent = value;
367
368                         basetype = value;
369                         isUserControl = (basetype == "System.Web.UI.UserControl");
370                         isPage = (basetype == "System.Web.UI.Page");
371                         isApplication = (basetype == "System.Web.HttpApplication");
372                 }
373         }
374
375         public bool IsUserControl {
376                 get { return isUserControl; }
377         }
378         
379         public bool IsPage {
380                 get { return isPage; }
381         }
382         
383         public bool IsApplication {
384                 get { return isApplication; }
385         }
386
387         public string Interfaces {
388                 get { return interfaces; }
389         }
390
391         public Hashtable Options {
392                 get {
393                         if (options == null)
394                                 options = new Hashtable ();
395
396                         return options;
397                 }
398         }
399         
400         public ArrayList Dependencies {
401                 get { return dependencies; }
402         }
403         
404         internal HttpContext Context {
405                 get { return context; }
406                 set { context = value; }
407         }
408         
409         bool AddUsing (string nspace)
410         {
411                 string _using = "using " + nspace + ";";
412                 if (prolog.ToString ().IndexOf (_using) == -1) {
413                         prolog.AppendFormat ("\t{0}\n", _using);
414                         return true;
415                 }
416
417                 return false;
418         }
419
420         void AddInterface (Type type)
421         {
422                 AddInterface (type.ToString ());
423         }
424         
425         public void AddInterface (string iface)
426         {
427                 if (interfaces == null) {
428                         interfaces = ", " + iface;
429                 } else {
430                         string s = ", " + iface;
431                         if (interfaces.IndexOf (s) == -1)
432                                 interfaces += s;
433                 }
434         }
435
436         private AspComponentFoundry Foundry
437         {
438                 get {
439                         if (aspFoundry == null)
440                                 aspFoundry = new AspComponentFoundry ();
441
442                         return aspFoundry;
443                 }
444         }
445
446         private void Init ()
447         {
448                 dependencies = new ArrayList ();
449                 dependencies.Add (fullPath);
450
451                 controls = new ControlStack ();
452                 controls.Push (typeof (System.Web.UI.Control), "Root", null, ChildrenKind.CONTROLS, null);
453                 prolog = new StringBuilder ();
454                 declarations = new StringBuilder ();
455                 script = new StringBuilder ();
456                 constructor = new StringBuilder ();
457                 init_funcs = new StringBuilder ();
458                 epilog = new StringBuilder ();
459                 textChunk = new StringBuilder ();
460
461                 current_function = new StringBuilder ();
462                 functions = new Stack ();
463                 functions.Push (current_function);
464
465                 parts = new Object [6];
466                 parts [0] = prolog;
467                 parts [1] = declarations;
468                 parts [2] = script;
469                 parts [3] = constructor;
470                 parts [4] = init_funcs;
471                 parts [5] = epilog;
472
473                 prolog.Append ("namespace ASP {\n" +
474                               "\tusing System;\n" + 
475                               "\tusing System.Collections;\n" + 
476                               "\tusing System.Collections.Specialized;\n" + 
477                               "\tusing System.Configuration;\n" + 
478                               "\tusing System.IO;\n" + 
479                               "\tusing System.Text;\n" + 
480                               "\tusing System.Text.RegularExpressions;\n" + 
481                               "\tusing System.Web;\n" + 
482                               "\tusing System.Web.Caching;\n" + 
483                               "\tusing System.Web.Security;\n" + 
484                               "\tusing System.Web.SessionState;\n" + 
485                               "\tusing System.Web.UI;\n" + 
486                               "\tusing System.Web.UI.WebControls;\n" + 
487                               "\tusing System.Web.UI.HtmlControls;\n");
488
489                 declarations.Append ("\t\tprivate static int __autoHandlers;\n");
490
491                 current_function.Append ("\t\tprivate void __BuildControlTree (System.Web.UI.Control __ctrl)\n\t\t{\n");
492                 if (!IsUserControl)
493                         current_function.Append ("\t\t\tSystem.Web.UI.IParserAccessor __parser = " + 
494                                                  "(System.Web.UI.IParserAccessor) __ctrl;\n\n");
495                 else
496                         controls.UseCodeRender = true;
497         }
498
499         public TextReader GetCode ()
500         {
501                 if (!parse_ok)
502                         throw new InvalidOperationException ("Parsing not done yet! (may be there were errors?)");
503
504                 StringBuilder code = new StringBuilder ();
505                 for (int i = 0; i < parts.Length; i++)
506                         code.Append ((StringBuilder) parts [i]);
507
508                 return new StringReader (code.ToString ());
509         }
510
511         // Regex.Escape () make some illegal escape sequences for a C# source.
512         private string Escape (string input)
513         {
514                 if (input == null)
515                         return String.Empty;
516
517                 string output = input.Replace ("\\", "\\\\");
518                 output = output.Replace ("\"", "\\\"");
519                 output = output.Replace ("\t", "\\t");
520                 output = output.Replace ("\r", "\\r");
521                 output = output.Replace ("\n", "\\n");
522                 output = output.Replace ("\n", "\\n");
523                 return output;
524         }
525         
526         bool AddProtectedField (Type type, string fieldName)
527         {
528                 if (parentType == null) {
529                         declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
530                         return true;
531                 }
532
533                 FieldInfo field = parentType.GetField (fieldName, BindingFlags.Public |
534                                                                   BindingFlags.NonPublic |
535                                                                   BindingFlags.Instance |
536                                                                   BindingFlags.Static);
537
538                 if (field == null || (!field.IsPublic && !field.IsFamily)) {
539                         declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
540                         return true;
541                 }
542
543                 if (!field.FieldType.IsAssignableFrom (type)) {
544                         string message = String.Format ("The base class includes the field '{0}', but its " +
545                                                         "type '{1}' is not compatible with {2}",
546                                                         fieldName, field.FieldType, type);
547
548                         throw new ApplicationException (message);
549                 }
550
551                 return false;
552         }
553         
554         private Type LoadParentType (string typeName)
555         {
556                 // First try loaded assemblies, then try assemblies in Bin directory.
557                 // By now i do this 'by hand' but may be this is a runtime/gac task.
558                 Type type = null;
559                 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
560                 foreach (Assembly ass in assemblies) {
561                         type = ass.GetType (typeName);
562                         if (type != null)
563                                 return type;
564                 }
565
566                 Assembly assembly;
567                 string [] binDlls = Directory.GetFiles (privateBinPath, "*.dll");
568                 foreach (string dll in binDlls) {
569                         string dllPath = Path.Combine (privateBinPath, dll);
570                         assembly = null;
571                         try {
572                                 assembly = Assembly.LoadFrom (dllPath);
573                                 type = assembly.GetType (typeName);
574                         } catch (Exception e) {
575                                 if (assembly != null) {
576                                         Console.WriteLine ("ASP.NET Warning: assembly {0} loaded", dllPath);
577                                         Console.WriteLine ("ASP.NET Warning: but type {0} not found", typeName);
578                                 } else {
579                                         Console.WriteLine ("ASP.NET Warning: unable to load type {0} from {1}",
580                                                            typeName, dllPath);
581                                 }
582                                 Console.WriteLine ("ASP.NET Warning: error was: {0}", e.Message);
583                         }
584
585                         if (type != null) {
586                                 dependencies.Add (dllPath);
587                                 return type;
588                         }
589                 }
590
591                 return null;
592         }
593
594         private void PageDirective (TagAttributes att)
595         {
596                 if (att ["ClassName"] != null){
597                         this.className = (string) att ["ClassName"];
598                         Options ["ClassName"] = className;
599                 }
600
601                 if (att ["EnableSessionState"] != null){
602                         if (!IsPage)
603                                 throw new ApplicationException ("EnableSessionState not allowed here.");
604                         
605                         string est = (string) att ["EnableSessionState"];
606                         if (0 == String.Compare (est, "false", true))
607                                 sessionState = SessionState.Disabled;
608                         else if (0 == String.Compare (est, "true", true))
609                                 sessionState = SessionState.Enabled;
610                         else if (0 == String.Compare (est, "readonly", true))
611                                 sessionState = SessionState.ReadOnly;
612                         else
613                                 throw new ApplicationException ("EnableSessionState in Page directive not set to " +
614                                                                 "a correct value: " + est);
615                 }
616
617                 if (att ["Inherits"] != null) {
618                         parent = (string) att ["Inherits"];
619                         parentType = LoadParentType (parent);
620                         if (parentType == null)
621                                 throw new ApplicationException ("The class " + parent + " cannot be found.");
622                 }
623
624                 if (att ["CompilerOptions"] != null)
625                         Options ["CompilerOptions"] = (string) att ["CompilerOptions"];
626
627                 if (att ["AutoEventWireup"] != null) {
628                         if (options ["AutoEventWireup"] != null)
629                                 throw new ApplicationException ("Already have an AutoEventWireup attribute");
630                         
631                         bool autoevent = true;
632                         string v = att ["AutoEventWireup"] as string;
633                         try {
634                                 autoevent = Convert.ToBoolean (v);
635                         } catch (Exception) {
636                                 throw new ApplicationException ("'" + v + "' is not a valid value for AutoEventWireup");
637                         }
638                         options ["AutoEventWireup"] = autoevent;
639                 }
640
641                 //FIXME: add support for more attributes.
642         }
643
644         void AddReference (string dll)
645         {
646                 string references = Options ["References"] as string;
647                 if (references == null)
648                         references = dll;
649                 else
650                         references = references + "|" + dll;
651
652                 Options ["References"] = references;
653         }
654
655         private void RegisterDirective (TagAttributes att)
656         {
657                 string tag_prefix = (string) (att ["tagprefix"] == null ?  "" : att ["tagprefix"]);
658                 string name_space = (string) (att ["namespace"] == null ?  "" : att ["namespace"]);
659                 string assembly_name = (string) (att ["assembly"] == null ?  "" : att ["assembly"]);
660                 string tag_name =  (string) (att ["tagname"] == null ?  "" : att ["tagname"]);
661                 string src = (string) (att ["src"] == null ?  "" : att ["src"]);
662
663                 if (tag_prefix != "" && name_space != "" && assembly_name != ""){
664                         if (tag_name != "" || src != "")
665                                 throw new ApplicationException ("Invalid attributes for @ Register: " +
666                                                                 att.ToString ());
667
668                         AddUsing (name_space);
669                         string dll = Path.Combine (privateBinPath, assembly_name + ".dll");
670                         // Hack: it should use assembly.load semantics...
671                         // may be when we don't run mcs as a external program...
672                         if (!File.Exists (dll))
673                                 dll = assembly_name;
674                         else
675                                 dependencies.Add (dll);
676
677                         Foundry.RegisterFoundry (tag_prefix, dll, name_space);
678                         AddReference (dll);
679                         return;
680                 }
681
682                 if (tag_prefix != "" && tag_name != "" && src != ""){
683                         if (name_space != "" && assembly_name != "")
684                                 throw new ApplicationException ("Invalid attributes for @ Register: " +
685                                                                 att.ToString ());
686                         
687                         if (!src.EndsWith (".ascx"))
688                                 throw new ApplicationException ("Source file extension for controls " + 
689                                                                 "must be .ascx");
690
691                         UserControlData data = GenerateUserControl (src, Context);
692                         switch (data.result) {
693                         case UserControlResult.OK:
694                                 AddUsing ("ASP");
695                                 Foundry.RegisterFoundry (tag_prefix, tag_name, data.assemblyName, "ASP", data.className);
696                                 AddReference (data.assemblyName);
697                                 break;
698                         case UserControlResult.FileNotFound:
699                                 throw new ApplicationException ("File '" + src + "' not found.");
700                         case UserControlResult.CompilationFailed:
701                                 //TODO: should say where the generated .cs file is for the server to
702                                 //show the source and the compiler error
703                                 throw new NotImplementedException ();
704                         }
705                         return;
706                 }
707
708                 throw new ApplicationException ("Invalid combination of attributes in " +
709                                                 "@ Register: " + att.ToString ());
710         }
711
712         private void ProcessDirective ()
713         {
714                 FlushPlainText ();
715                 Directive directive = (Directive) current;
716                 TagAttributes att = directive.Attributes;
717                 if (att == null)
718                         return;
719
720                 string value;
721                 string id = directive.TagID.ToUpper ();
722                 switch (id){
723                 case "APPLICATION":
724                         if (main_directive != null)
725                                 throw new ApplicationException (id + " not allowed after " + main_directive);
726
727                         if (!IsApplication)
728                                 throw new ApplicationException ("@Application not allowed.");
729
730                         string inherits = att ["inherits"] as string;
731                         if (inherits != null) {
732                                 parent = inherits;
733                                 parentType = LoadParentType (parent);
734                                 if (parentType == null)
735                                         throw new ApplicationException ("The class " + parent + " cannot be found.");
736                         }
737
738                         main_directive = directive.TagID;
739                         break;
740                 case "PAGE":
741                 case "CONTROL":
742                         if (main_directive != null)
743                                 throw new ApplicationException (id + " not allowed after " + main_directive);
744
745                         if (IsUserControl && id != "CONTROL")
746                                 throw new ApplicationException ("@Page not allowed for user controls.");
747                         else if (IsPage && id != "PAGE")
748                                 throw new ApplicationException ("@Control not allowed here. This is a page!");
749
750                         PageDirective (att);
751                         main_directive = directive.TagID;
752                         break;
753                 case "IMPORT":
754                         value = att ["namespace"] as string;
755                         if (value == null || att.Count > 1)
756                                 throw new ApplicationException ("Wrong syntax in Import directive.");
757
758                         string _using = "using " + value + ";";
759                         if (AddUsing (value) == true) {
760                                 string imports = Options ["Import"] as string;
761                                 if (imports == null) {
762                                         imports = value;
763                                 } else {
764                                         imports += "," + value;
765                                 }
766
767                                 Options ["Import"] = imports;
768                         }
769                         break;
770                 case "IMPLEMENTS":
771                         if (IsApplication)
772                                 throw new ApplicationException ("@ Implements not allowed in an application file.");
773
774                         string iface = (string) att ["interface"];
775                         AddInterface (iface);
776                         break;
777                 case "REGISTER":
778                         if (IsApplication)
779                                 throw new ApplicationException ("@ Register not allowed in an application file.");
780
781                         RegisterDirective (att);
782                         break;
783                 case "ASSEMBLY":
784                         if (att.Count > 1)
785                                 throw new ApplicationException ("Wrong syntax in Assembly directive.");
786
787                         string name = att ["name"] as string;
788                         string src = att ["src"] as string;
789
790                         if (name == null && src == null)
791                                 throw new ApplicationException ("Wrong syntax in Assembly directive.");
792
793                         if (IsApplication && src != null)
794                                 throw new ApplicationException ("'name' attribute expected.");
795
796                         value = (name == null) ? src : name;
797                         string assemblies = Options ["Assembly"] as string;
798                         if (assemblies == null) {
799                                 assemblies = value;
800                         } else {
801                                 assemblies += "," + value;
802                         }
803
804                         Options ["Assembly"] = assemblies;
805                         break;
806                 }
807         }
808
809         private void ProcessPlainText ()
810         {
811                 PlainText asis = (PlainText) current;
812                 string trimmed = asis.Text.Trim ();
813                 if (trimmed == String.Empty && controls.SpaceBetweenTags == true)
814                         return;
815
816                 if (IsApplication) {
817                         if (trimmed != String.Empty)
818                                 throw new ApplicationException (app_file_wrong);
819                         return;
820                 }
821
822                 if (trimmed != String.Empty && controls.PeekChildKind () != ChildrenKind.CONTROLS){
823                         string tag_id = controls.PeekTagID ();
824                         throw new ApplicationException ("Literal content not allowed for " + tag_id);
825                 }
826                 
827                 string escaped_text = Escape (asis.Text);
828                 current_function.AppendFormat ("\t\t\t__parser.AddParsedSubObject (" + 
829                                                "new System.Web.UI.LiteralControl (\"{0}\"));\n",
830                                                escaped_text);
831                 StringBuilder codeRenderFunction = controls.CodeRenderFunction;
832                 codeRenderFunction.AppendFormat ("\t\t\t__output.Write (\"{0}\");\n", escaped_text);
833         }
834
835         private string EnumValueNameToString (Type enum_type, string value_name)
836         {
837                 if (value_name.EndsWith ("*"))
838                         throw new ApplicationException ("Invalid property value: '" + value_name + 
839                                                         ". It must be a valid " + enum_type.ToString () + " value.");
840
841                 MemberInfo [] nested_types = enum_type.FindMembers (MemberTypes.Field, 
842                                                                     BindingFlags.Public | BindingFlags.Static,
843                                                                     Type.FilterNameIgnoreCase,
844                                                                     value_name);
845
846                 if (nested_types.Length == 0)
847                         throw new ApplicationException ("Value " + value_name + " not found in enumeration " +
848                                                         enum_type.ToString ());
849                 if (nested_types.Length > 1)
850                         throw new ApplicationException ("Value " + value_name + " found " + 
851                                                         nested_types.Length + " in enumeration " +
852                                                         enum_type.ToString ());
853
854                 return enum_type.ToString () + "." + nested_types [0].Name;
855         }
856         
857         private void NewControlFunction (string tag_id,
858                                          string control_id,
859                                          Type control_type,
860                                          ChildrenKind children_kind,
861                                          string defaultPropertyName)
862         {
863                 ChildrenKind prev_children_kind = controls.PeekChildKind ();
864                 if (prev_children_kind == ChildrenKind.NONE || 
865                     prev_children_kind == ChildrenKind.PROPERTIES){
866                         string prev_tag_id = controls.PeekTagID ();
867                         throw new ApplicationException ("Child controls not allowed for " + prev_tag_id);
868                 }
869
870                 Type allowed = null;
871                 if (prev_children_kind == ChildrenKind.DBCOLUMNS &&
872                     !typeof (DataGridColumn).IsAssignableFrom (control_type)) {
873                         allowed = typeof (DataGridColumn);
874                 } else if (prev_children_kind == ChildrenKind.LISTITEM &&
875                          control_type != typeof (System.Web.UI.WebControls.ListItem)) {
876                         allowed = typeof (DataGridColumn);
877                 } else if (prev_children_kind == ChildrenKind.HTMLROW) {
878                         Type prevType = controls.PeekType ();
879                         if (prevType == typeof (HtmlTable) && control_type != typeof (HtmlTableRow))
880                                 allowed = typeof (HtmlTableRow);
881                         else if (prevType == typeof (Table) && control_type != typeof (TableRow))
882                                 allowed = typeof (TableRow);
883                 } else if (prev_children_kind == ChildrenKind.HTMLCELL) {
884                         Type prevType = controls.PeekType ();
885                         if (prevType == typeof (HtmlTableRow) && control_type != typeof (HtmlTableCell))
886                                 allowed = typeof (HtmlTableCell);
887                         else if (prevType == typeof (TableRow) && control_type != typeof (TableCell))
888                                 allowed = typeof (TableCell);
889                 }
890         
891                 if (allowed != null)
892                         throw new ApplicationException ("Inside " + controls.PeekTagID () + " only " + 
893                                                         allowed + " objects are allowed");
894
895                 StringBuilder func_code = new StringBuilder ();
896                 current_function = func_code;
897                 if (0 == String.Compare (tag_id, "form", true)){
898                         if (has_form_tag)
899                                 throw new ApplicationException ("Only one form server tag allowed.");
900                         has_form_tag = true;
901                 }
902
903                 controls.Push (control_type, control_id, tag_id, children_kind, defaultPropertyName);
904                 bool is_generic = control_type ==  typeof (System.Web.UI.HtmlControls.HtmlGenericControl);
905                 functions.Push (current_function);
906                 if (control_type != typeof (System.Web.UI.WebControls.ListItem) &&
907                     prev_children_kind != ChildrenKind.DBCOLUMNS) {
908                         current_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_" +
909                                                         "{0} ()\n\t\t{{\n\t\t\t{1} __ctrl;\n\n\t\t\t__ctrl" +
910                                                         " = new {1} ({2});\n\t\t\tthis.{0} = __ctrl;\n",
911                                                         control_id, control_type,
912                                                         (is_generic? "\"" + tag_id + "\"" : ""));
913                 } else {
914                         current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ()\n\t\t{{" +
915                                                         "\n\t\t\t{1} __ctrl;\n\t\t\t__ctrl = new {1} ();" +
916                                                         "\n\t\t\tthis.{0} = __ctrl;\n",
917                                                         control_id, control_type);
918                 }
919
920                 if (children_kind == ChildrenKind.CONTROLS || children_kind == ChildrenKind.OPTION)
921                         current_function.Append ("\t\t\tSystem.Web.UI.IParserAccessor __parser = " + 
922                                                  "(System.Web.UI.IParserAccessor) __ctrl;\n");
923         }
924         
925         private void DataBoundProperty (Type target, string varName, string value)
926         {
927                 if (value == "")
928                         throw new ApplicationException ("Empty data binding tag.");
929
930                 string control_id = controls.PeekControlID ();
931                 string control_type_string = controls.PeekType ().ToString ();
932                 StringBuilder db_function = controls.DataBindFunction;
933                 string container;
934                 if (controls.Container == null || !typeof (INamingContainer).IsAssignableFrom (controls.Container))
935                         container = "System.Web.UI.Control";
936                 else {
937                         container = controls.Container.ToString ();
938                 }
939
940                 if (db_function.Length == 0)
941                         db_function.AppendFormat ("\t\tpublic void __DataBind_{0} (object sender, " + 
942                                                   "System.EventArgs e) {{\n" +
943                                                   "\t\t\t{1} Container;\n" +
944                                                   "\t\t\t{2} target;\n" +
945                                                   "\t\t\ttarget = ({2}) sender;\n" +
946                                                   "\t\t\tContainer = ({1}) target.BindingContainer;\n",
947                                                   control_id, container, control_type_string);
948
949                 /* Removes '<%#' and '%>' */
950                 string real_value = value.Remove (0,3);
951                 real_value = real_value.Remove (real_value.Length - 2, 2);
952                 real_value = real_value.Trim ();
953
954                 if (target == typeof (string))
955                         db_function.AppendFormat ("\t\t\ttarget.{0} = System.Convert.ToString ({1});\n",
956                                                   varName, real_value);
957                 else
958                         db_function.AppendFormat ("\t\t\ttarget.{0} = ({1}) ({2});\n",
959                                                   varName, target, real_value);
960         }
961
962         /*
963          * Returns true if it generates some code for the specified property
964          */
965         private void AddCodeForPropertyOrField (Type type, string var_name, string att, bool isDataBound)
966         {
967                 /* FIXME: should i check for this or let the compiler fail?
968                  * if (!prop.CanWrite)
969                  *    ....
970                  */
971                 if (isDataBound) {
972                         DataBoundProperty (type, var_name, att);
973                 }
974                 else if (type == typeof (string)){
975                         if (att == null)
976                                 throw new ApplicationException ("null value for attribute " + var_name );
977
978                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = \"{1}\";\n", var_name,
979                                                         Escape (att)); // FIXME: really Escape this?
980                 } 
981                 else if (type.IsEnum){
982                         if (att == null)
983                                 throw new ApplicationException ("null value for attribute " + var_name );
984
985                         string enum_value = EnumValueNameToString (type, att);
986
987                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, enum_value);
988                 } 
989                 else if (type == typeof (bool)){
990                         string value;
991                         if (att == null)
992                                 value = "true"; //FIXME: is this ok for non Style properties?
993                         else if (0 == String.Compare (att, "true", true))
994                                 value = "true";
995                         else if (0 == String.Compare (att, "false", true))
996                                 value = "false";
997                         else
998                                 throw new ApplicationException ("Value '" + att  + "' is not a valid boolean.");
999
1000                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1001                 }
1002                 else if (type == typeof (System.Web.UI.WebControls.Unit)){
1003                          //FIXME: should use the culture specified in Page
1004                         try {
1005                                 Unit value = Unit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
1006                         } catch (Exception) {
1007                                 throw new ApplicationException ("'" + att + "' cannot be parsed as a unit.");
1008                         }
1009                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = " + 
1010                                                         "System.Web.UI.WebControls.Unit.Parse (\"{1}\", " + 
1011                                                         "System.Globalization.CultureInfo.InvariantCulture);\n", 
1012                                                         var_name, att);
1013                 }
1014                 else if (type == typeof (System.Web.UI.WebControls.FontUnit)){
1015                          //FIXME: should use the culture specified in Page
1016                         try {
1017                                 FontUnit value = FontUnit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
1018                         } catch (Exception) {
1019                                 throw new ApplicationException ("'" + att + "' cannot be parsed as a unit.");
1020                         }
1021                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = " + 
1022                                                         "System.Web.UI.WebControls.FontUnit.Parse (\"{1}\", " + 
1023                                                         "System.Globalization.CultureInfo.InvariantCulture);\n", 
1024                                                         var_name, att);
1025                 }
1026                 else if (type == typeof (Int16) || type == typeof (Int32) || type == typeof (Int64)) {
1027                         long value;
1028                         try {
1029                                 value = Int64.Parse (att); //FIXME: should use the culture specified in Page
1030                         } catch (Exception){
1031                                 throw new ApplicationException (att + " is not a valid signed number " + 
1032                                                                 "or is out of range.");
1033                         }
1034
1035                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1036                 }
1037                 else if (type == typeof (UInt16) || type == typeof (UInt32) || type == typeof (UInt64)) {
1038                         ulong value;
1039                         try {
1040                                 value = UInt64.Parse (att); //FIXME: should use the culture specified in Page
1041                         } catch (Exception){
1042                                 throw new ApplicationException (att + " is not a valid unsigned number " + 
1043                                                                 "or is out of range.");
1044                         }
1045
1046                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1047                 }
1048                 else if (type == typeof (float)) {
1049                         float value;
1050                         try {
1051                                 value = Single.Parse (att);
1052                         } catch (Exception){
1053                                 throw new ApplicationException (att + " is not  avalid float number or " +
1054                                                                 "is out of range.");
1055                         }
1056
1057                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1058                 }
1059                 else if (type == typeof (double)){
1060                         double value;
1061                         try {
1062                                 value = Double.Parse (att);
1063                         } catch (Exception){
1064                                 throw new ApplicationException (att + " is not  avalid double number or " +
1065                                                                 "is out of range.");
1066                         }
1067
1068                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1069                 }
1070                 else if (type == typeof (System.Drawing.Color)){
1071                         Color c;
1072                         try {
1073                                 c = (Color) TypeDescriptor.GetConverter (typeof (Color)).ConvertFromString (att);
1074                         } catch (Exception e){
1075                                 throw new ApplicationException ("Color " + att + " is not a valid color.", e);
1076                         }
1077
1078                         // Should i also test for IsSystemColor?
1079                         // Are KnownColor members in System.Drawing.Color?
1080                         if (c.IsKnownColor){
1081                                 current_function.AppendFormat ("\t\t\t__ctrl.{0} = System.Drawing.Color." +
1082                                                                "{1};\n", var_name, c.Name);
1083                         }
1084                         else {
1085                                 current_function.AppendFormat ("\t\t\t__ctrl.{0} = System.Drawing.Color." +
1086                                                                "FromArgb ({1}, {2}, {3}, {4});\n",
1087                                                                var_name, c.A, c.R, c.G, c.B);
1088                         }
1089                 }
1090                 else if (type == typeof (string [])) {
1091                         string [] subStrings = att.Split (',');
1092                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = new String [] {{\n", var_name);
1093                         int end = subStrings.Length;
1094                         for (int i = 0; i < end; i++) {
1095                                 string s = subStrings [i].Trim ();
1096                                 current_function.AppendFormat ("\t\t\t\t\"{0}\"", s);
1097                                 if (i == end - 1)
1098                                         current_function.Append ("\t\t\t\t};\n");
1099                                 else
1100                                         current_function.Append (",\n");
1101                         }
1102                 } else {
1103                         throw new ApplicationException ("Unsupported type in property: " + 
1104                                                         type.ToString ());
1105                 }
1106         }
1107
1108         private bool ProcessPropertiesAndFields (MemberInfo member, string id, TagAttributes att)
1109         {
1110                 int hyphen = id.IndexOf ('-');
1111
1112                 bool isPropertyInfo = (member is PropertyInfo);
1113
1114                 bool is_processed = false;
1115                 bool isDataBound = att.IsDataBound ((string) att [id]);
1116                 Type type;
1117                 if (isPropertyInfo) {
1118                         type = ((PropertyInfo) member).PropertyType;
1119                         if (hyphen == -1 && ((PropertyInfo) member).CanWrite == false)
1120                                 return false;
1121                 } else {
1122                         type = ((FieldInfo) member).FieldType;
1123                 }
1124
1125                 if (0 == String.Compare (member.Name, id, true)){
1126                         AddCodeForPropertyOrField (type, member.Name, (string) att [id], isDataBound);
1127                         is_processed = true;
1128                 } else if (hyphen != -1 && (type == fontinfoType || type == styleType || type.IsSubclassOf (styleType))){
1129                         string prop_field = id.Replace ("-", ".");
1130                         string [] parts = prop_field.Split (new char [] {'.'});
1131                         if (parts.Length != 2 || 0 != String.Compare (member.Name, parts [0], true))
1132                                 return false;
1133
1134                         PropertyInfo [] subprops = type.GetProperties ();
1135                         foreach (PropertyInfo subprop in subprops){
1136                                 if (0 != String.Compare (subprop.Name, parts [1], true))
1137                                         continue;
1138
1139                                 if (subprop.CanWrite == false)
1140                                         return false;
1141
1142                                 bool is_bool = subprop.PropertyType == typeof (bool);
1143                                 if (!is_bool && att [id] == null){
1144                                         att [id] = ""; // Font-Size -> Font-Size="" as html
1145                                         return false;
1146                                 }
1147
1148                                 string value;
1149                                 if (att [id] == null && is_bool)
1150                                         value = "true"; // Font-Bold <=> Font-Bold="true"
1151                                 else
1152                                         value = (string) att [id];
1153
1154                                 AddCodeForPropertyOrField (subprop.PropertyType,
1155                                                  member.Name + "." + subprop.Name,
1156                                                  value, isDataBound);
1157                                 is_processed = true;
1158                         }
1159                 }
1160
1161                 return is_processed;
1162         }
1163         
1164         private void AddCodeForAttributes (Type type, TagAttributes att)
1165         {
1166                 EventInfo [] ev_info = type.GetEvents ();
1167                 PropertyInfo [] prop_info = type.GetProperties ();
1168                 FieldInfo [] field_info = type.GetFields ();
1169                 bool is_processed = false;
1170                 ArrayList processed = new ArrayList ();
1171
1172                 foreach (string id in att.Keys){
1173                         if (0 == String.Compare (id, "runat", true) || 0 == String.Compare (id, "id", true))
1174                                 continue;
1175
1176                         if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){
1177                                 string id_as_event = id.Substring (2);
1178                                 foreach (EventInfo ev in ev_info){
1179                                         if (0 == String.Compare (ev.Name, id_as_event, true)){
1180                                                 current_function.AppendFormat (
1181                                                                 "\t\t\t__ctrl.{0} += " + 
1182                                                                 "new {1} (this.{2});\n", 
1183                                                                 ev.Name, ev.EventHandlerType, att [id]);
1184                                                 is_processed = true;
1185                                                 break;
1186                                         }
1187                                 }
1188                                 if (is_processed){
1189                                         is_processed = false;
1190                                         continue;
1191                                 }
1192                         } 
1193
1194                         foreach (PropertyInfo prop in prop_info){
1195                                 is_processed = ProcessPropertiesAndFields (prop, id, att);
1196                                 if (is_processed)
1197                                         break;
1198                         }
1199
1200                         if (!is_processed) {
1201                                 foreach (FieldInfo field in field_info){
1202                                         is_processed = ProcessPropertiesAndFields (field, id, att);
1203                                         if (is_processed)
1204                                                 break;
1205                                 }
1206                         }
1207
1208                         if (is_processed){
1209                                 is_processed = false;
1210                                 continue;
1211                         }
1212
1213                         current_function.AppendFormat ("\t\t\t((System.Web.UI.IAttributeAccessor) __ctrl)." +
1214                                                 "SetAttribute (\"{0}\", \"{1}\");\n",
1215                                                 id, Escape ((string) att [id]));
1216                 }
1217         }
1218         
1219         private void AddCodeRenderControl (StringBuilder function)
1220         {
1221                 AddCodeRenderControl (function, controls.CodeRenderIndex);
1222         }
1223
1224         private void AddCodeRenderControl (StringBuilder function, int index)
1225         {
1226                 function.AppendFormat ("\t\t\tparameterContainer.Controls [{0}]." + 
1227                                        "RenderControl (__output);\n", index);
1228         }
1229
1230         private void AddRenderMethodDelegate (StringBuilder function, string control_id)
1231         {
1232                 function.AppendFormat ("\t\t\t__ctrl.SetRenderMethodDelegate (new System.Web." + 
1233                                        "UI.RenderMethod (this.__Render_{0}));\n", control_id);
1234         }
1235
1236         private void AddCodeRenderFunction (string codeRender, string control_id)
1237         {
1238                 StringBuilder codeRenderFunction = new StringBuilder ();
1239                 codeRenderFunction.AppendFormat ("\t\tprivate void __Render_{0} " + 
1240                                                  "(System.Web.UI.HtmlTextWriter __output, " + 
1241                                                  "System.Web.UI.Control parameterContainer)\n" +
1242                                                  "\t\t{{\n", control_id);
1243                 codeRenderFunction.Append (codeRender);
1244                 codeRenderFunction.Append ("\t\t}\n\n");
1245                 init_funcs.Append (codeRenderFunction);
1246         }
1247
1248         private void RemoveLiterals (StringBuilder function)
1249         {
1250                 string no_literals = Regex.Replace (function.ToString (),
1251                                                     @"\t\t\t__parser.AddParsedSubObject \(" + 
1252                                                     @"new System.Web.UI.LiteralControl \(.+\);\n", "");
1253                 function.Length = 0;
1254                 function.Append (no_literals);
1255         }
1256
1257         private bool FinishControlFunction (string tag_id)
1258         {
1259                 if (functions.Count == 0)
1260                         throw new ApplicationException ("Unbalanced open/close tags");
1261
1262                 if (controls.Count == 0)
1263                         return false;
1264
1265                 string saved_id = controls.PeekTagID ();
1266                 if (0 != String.Compare (saved_id, tag_id, true))
1267                         return false;
1268
1269                 FlushPlainText ();
1270                 StringBuilder old_function = (StringBuilder) functions.Pop ();
1271                 current_function = (StringBuilder) functions.Peek ();
1272
1273                 string control_id = controls.PeekControlID ();
1274                 Type control_type = controls.PeekType ();
1275                 ChildrenKind child_kind = controls.PeekChildKind ();
1276
1277                 bool hasDataBindFunction = controls.HasDataBindFunction ();
1278                 if (hasDataBindFunction)
1279                         old_function.AppendFormat ("\t\t\t__ctrl.DataBinding += new System.EventHandler " +
1280                                                    "(this.__DataBind_{0});\n", control_id);
1281
1282                 bool useCodeRender = controls.UseCodeRender;
1283                 if (useCodeRender)
1284                         AddRenderMethodDelegate (old_function, control_id);
1285                 
1286                 if (control_type == typeof (System.Web.UI.ITemplate)){
1287                         old_function.Append ("\n\t\t}\n\n");
1288                         current_function.AppendFormat ("\t\t\t__ctrl.{0} = new System.Web.UI." + 
1289                                                        "CompiledTemplateBuilder (new System.Web.UI." +
1290                                                        "BuildTemplateMethod (this.__BuildControl_{1}));\n",
1291                                                        saved_id, control_id);
1292                 }
1293                 else if (control_type == typeof (System.Web.UI.WebControls.DataGridColumnCollection)){
1294                         old_function.Append ("\n\t\t}\n\n");
1295                         current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n",
1296                                                         control_id, saved_id);
1297                 }
1298                 else if (control_type == typeof (System.Web.UI.WebControls.DataGridColumn) ||
1299                          control_type.IsSubclassOf (typeof (System.Web.UI.WebControls.DataGridColumn)) ||
1300                          control_type == typeof (System.Web.UI.WebControls.ListItem)){
1301                         old_function.Append ("\n\t\t}\n\n");
1302                         string parsed = "";
1303                         string ctrl_name = "ctrl";
1304                         Type cont = controls.Container;
1305                         if (cont == null || cont == typeof (System.Web.UI.HtmlControls.HtmlSelect)){
1306                                 parsed = "ParsedSubObject";
1307                                 ctrl_name = "parser";
1308                         }
1309
1310                         current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n" +
1311                                                        "\t\t\t__{1}.Add{2} (this.{0});\n\n",
1312                                                        control_id, ctrl_name, parsed);
1313                 }
1314                 else if (child_kind == ChildrenKind.LISTITEM){
1315                         old_function.Append ("\n\t\t}\n\n");
1316                         init_funcs.Append (old_function); // Closes the BuildList function
1317                         old_function = (StringBuilder) functions.Pop ();
1318                         current_function = (StringBuilder) functions.Peek ();
1319                         old_function.AppendFormat ("\n\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n\t\t\t" +
1320                                                    "return __ctrl;\n\t\t}}\n\n",
1321                                                    control_id, controls.PeekDefaultPropertyName ());
1322
1323                         controls.Pop ();
1324                         control_id = controls.PeekControlID ();
1325                         current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
1326                                                        "AddParsedSubObject (this.{0});\n\n", control_id);
1327                 } else if (control_type == typeof (HtmlTableCell) || control_type == typeof (TableCell)) {
1328                         old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
1329                         object top = controls.Pop ();
1330                         Type t = controls.PeekType ();
1331                         controls.Push (top);
1332                         string parsed = "";
1333                         string ctrl_name = "ctrl";
1334                         if (!t.IsSubclassOf (typeof (WebControl)) && t != typeof (HtmlTableRow)) {
1335                                 parsed = "ParsedSubObject";
1336                                 ctrl_name = "parser";
1337                         }
1338
1339                         current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n" +
1340                                                        "\t\t\t__{1}.Add{2} (this.{0});\n\n",
1341                                                        control_id, ctrl_name, parsed);
1342                 } else if (child_kind == ChildrenKind.HTMLROW || child_kind == ChildrenKind.HTMLCELL) {
1343                         old_function.Append ("\n\t\t}\n\n");
1344                         init_funcs.Append (old_function);
1345                         old_function = (StringBuilder) functions.Pop ();
1346                         current_function = (StringBuilder) functions.Peek ();
1347                         old_function.AppendFormat ("\n\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n\t\t\t" +
1348                                                    "return __ctrl;\n\t\t}}\n\n",
1349                                                    control_id, controls.PeekDefaultPropertyName ());
1350
1351                         controls.Pop ();
1352                         control_id = controls.PeekControlID ();
1353                         current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n", control_id);
1354                         if (child_kind == ChildrenKind.HTMLROW) {
1355                                 current_function.AppendFormat ("\t\t\t__parser.AddParsedSubObject ({0});\n",
1356                                                                 control_id);
1357                         } else {
1358                                 current_function.AppendFormat ("\t\t\t__ctrl.Add (this.{0});\n", control_id);
1359                         }
1360                 } else {
1361                         old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
1362                         current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
1363                                                        "AddParsedSubObject (this.{0});\n\n", control_id);
1364                 }
1365
1366                 if (useCodeRender)
1367                         RemoveLiterals (old_function);
1368
1369                 init_funcs.Append (old_function);
1370                 if (useCodeRender)
1371                         AddCodeRenderFunction (controls.CodeRenderFunction.ToString (), control_id);
1372                 
1373                 if (hasDataBindFunction){
1374                         StringBuilder db_function = controls.DataBindFunction;
1375                         db_function.Append ("\t\t}\n\n");
1376                         init_funcs.Append (db_function);
1377                 }
1378
1379                 // Avoid getting empty stacks for unbalanced open/close tags
1380                 if (controls.Count > 1){
1381                         controls.Pop ();
1382                         AddCodeRenderControl (controls.CodeRenderFunction, controls.ChildIndex);
1383                 }
1384
1385                 return true;
1386         }
1387
1388         private void NewTableElementFunction (HtmlControlTag ctrl)
1389         {
1390                 string control_id = Tag.GetDefaultID ();
1391                 ChildrenKind child_kind;
1392
1393                 Type t;
1394                 if (ctrl.ControlType == typeof (HtmlTable)) {
1395                         t = typeof (HtmlTableRowCollection);
1396                         child_kind = ChildrenKind.HTMLROW;
1397                 } else {
1398                         t = typeof (HtmlTableCellCollection);
1399                         child_kind = ChildrenKind.HTMLCELL;
1400                 }
1401
1402                 controls.Push (ctrl.ControlType,
1403                                control_id,
1404                                ctrl.TagID,
1405                                child_kind,
1406                                ctrl.ParseChildren);
1407
1408                 current_function = new StringBuilder ();
1409                 functions.Push (current_function);
1410                 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ({1} __ctrl)\n" +
1411                                                 "\t\t{{\n", control_id, t);
1412         }
1413
1414         private void ProcessHtmlControlTag ()
1415         {
1416                 FlushPlainText ();
1417                 HtmlControlTag html_ctrl = (HtmlControlTag) current;
1418                 if (html_ctrl.TagID.ToUpper () == "SCRIPT"){
1419                         //FIXME: if the is script is to be read from disk, do it!
1420                         if (html_ctrl.SelfClosing)
1421                                 throw new ApplicationException ("Read script from file not supported yet.");
1422
1423                         sstatus = ScriptStatus.Open;
1424                         return;
1425                 }
1426                 
1427                 if (IsApplication)
1428                         throw new ApplicationException (app_file_wrong);
1429                 
1430                 Type controlType = html_ctrl.ControlType;
1431                 AddProtectedField (controlType, html_ctrl.ControlID);
1432
1433                 ChildrenKind children_kind;
1434                 if (0 == String.Compare (html_ctrl.TagID, "table", true))
1435                         children_kind = ChildrenKind.HTMLROW;
1436                 else if (0 == String.Compare (html_ctrl.TagID, "tr", true))
1437                         children_kind = ChildrenKind.HTMLCELL;
1438                 else if (0 != String.Compare (html_ctrl.TagID, "select", true))
1439                         children_kind = html_ctrl.IsContainer ? ChildrenKind.CONTROLS :
1440                                                                 ChildrenKind.NONE;
1441                 else
1442                         children_kind = ChildrenKind.OPTION;
1443
1444                 NewControlFunction (html_ctrl.TagID, html_ctrl.ControlID, controlType, children_kind, html_ctrl.ParseChildren); 
1445
1446                 current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", html_ctrl.ControlID);
1447                 AddCodeForAttributes (html_ctrl.ControlType, html_ctrl.Attributes);
1448
1449                 if (children_kind == ChildrenKind.HTMLROW || children_kind == ChildrenKind.HTMLCELL)
1450                         NewTableElementFunction (html_ctrl);
1451
1452                 if (html_ctrl.SelfClosing)
1453                         FinishControlFunction (html_ctrl.TagID);
1454         }
1455
1456         // Closing is performed in FinishControlFunction ()
1457         private void NewBuildListFunction (AspComponent component)
1458         {
1459                 string control_id = Tag.GetDefaultID ();
1460
1461                 controls.Push (component.ComponentType,
1462                                control_id, 
1463                                component.TagID, 
1464                                component.ChildrenKind, 
1465                                component.DefaultPropertyName);
1466
1467                 Type childType = component.DefaultPropertyType;
1468                 if (childType == null)
1469                         childType = typeof (ListItemCollection);
1470
1471                 current_function = new StringBuilder ();
1472                 functions.Push (current_function);
1473                 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
1474                                                 "({1} __ctrl)\n" +
1475                                                 "\t\t{{\n", control_id, childType);
1476         }
1477
1478         private void ProcessComponent ()
1479         {
1480                 FlushPlainText ();
1481                 AspComponent component = (AspComponent) current;
1482                 Type component_type = component.ComponentType;
1483                 AddProtectedField (component_type, component.ControlID);
1484
1485                 NewControlFunction (component.TagID, component.ControlID, component_type,
1486                                     component.ChildrenKind, component.DefaultPropertyName); 
1487
1488                 if (component_type == typeof (UserControl) ||
1489                     component_type.IsSubclassOf (typeof (System.Web.UI.UserControl)))
1490                         current_function.Append ("\t\t\t__ctrl.InitializeAsUserControl (Page);\n");
1491
1492                 if (component_type == typeof (Control) ||
1493                     component_type.IsSubclassOf (typeof (System.Web.UI.Control)))
1494                         current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", component.ControlID);
1495
1496                 AddCodeForAttributes (component.ComponentType, component.Attributes);
1497                 if (component.ChildrenKind == ChildrenKind.LISTITEM || component.DefaultPropertyType != null)
1498                         NewBuildListFunction (component);
1499
1500                 if (component.SelfClosing)
1501                         FinishControlFunction (component.TagID);
1502         }
1503
1504         private void ProcessServerObjectTag ()
1505         {
1506                 FlushPlainText ();
1507                 ServerObjectTag obj = (ServerObjectTag) current;
1508                 declarations.AppendFormat ("\t\tprivate {0} cached{1};\n", obj.ObjectClass, obj.ObjectID);
1509                 constructor.AppendFormat ("\n\t\tprivate {0} {1}\n\t\t{{\n\t\t\tget {{\n\t\t\t\t" + 
1510                                           "if (this.cached{1} == null)\n\t\t\t\t\tthis.cached{1} = " + 
1511                                           "new {0} ();\n\t\t\t\treturn cached{1};\n\t\t\t}}\n\t\t}}\n\n",
1512                                           obj.ObjectClass, obj.ObjectID);
1513         }
1514
1515         // Creates a new function that sets the values of subproperties.
1516         private void NewStyleFunction (PropertyTag tag)
1517         {
1518                 current_function = new StringBuilder ();
1519
1520                 string prop_id = tag.PropertyID;
1521                 Type prop_type = tag.PropertyType;
1522                 // begin function
1523                 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ({1} __ctrl)\n" +
1524                                                 "\t\t{{\n", prop_id, prop_type);
1525                 
1526                 // Add property initialization code
1527                 PropertyInfo [] subprop_info = prop_type.GetProperties ();
1528                 TagAttributes att = tag.Attributes;
1529
1530                 string subprop_name = null;
1531                 foreach (string id in att.Keys){
1532                         if (0 == String.Compare (id, "runat", true) || 0 == String.Compare (id, "id", true))
1533                                 continue;
1534
1535                         bool is_processed = false;
1536                         foreach (PropertyInfo subprop in subprop_info){
1537                                 is_processed = ProcessPropertiesAndFields (subprop, id, att);
1538                                 if (is_processed){
1539                                         subprop_name = subprop.Name;
1540                                         break;
1541                                 }
1542                         }
1543
1544                         if (subprop_name == null)
1545                                 throw new ApplicationException ("Property " + tag.TagID + " does not have " + 
1546                                                                 "a " + id + " subproperty.");
1547                 }
1548
1549                 // Finish function
1550                 current_function.Append ("\n\t\t}\n\n");
1551                 init_funcs.Append (current_function);
1552                 current_function = (StringBuilder) functions.Peek ();
1553                 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n",
1554                                                 prop_id, tag.PropertyName);
1555
1556                 if (!tag.SelfClosing){
1557                         // Next tag should be the closing tag
1558                         controls.Push (null, null, null, ChildrenKind.NONE, null);
1559                         waitClosing = tag.TagID;
1560                 }
1561         }
1562
1563         // This one just opens the function. Closing is performed in FinishControlFunction ()
1564         private void NewTemplateFunction (PropertyTag tag)
1565         {
1566                 /*
1567                  * FIXME
1568                  * This function does almost the same as NewControlFunction.
1569                  * Consider merging.
1570                  */
1571                 string prop_id = tag.PropertyID;
1572                 Type prop_type = tag.PropertyType;
1573                 string tag_id = tag.PropertyName; // Real property name used in FinishControlFunction
1574
1575                 controls.Push (prop_type, prop_id, tag_id, ChildrenKind.CONTROLS, null);
1576                 current_function = new StringBuilder ();
1577                 functions.Push (current_function);
1578                 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
1579                                                 "(System.Web.UI.Control __ctrl)\n" +
1580                                                 "\t\t{{\n" +
1581                                                 "\t\t\tSystem.Web.UI.IParserAccessor __parser " + 
1582                                                 "= (System.Web.UI.IParserAccessor) __ctrl;\n" , prop_id);
1583         }
1584
1585         // Closing is performed in FinishControlFunction ()
1586         private void NewDBColumnFunction (PropertyTag tag)
1587         {
1588                 /*
1589                  * FIXME
1590                  * This function also does almost the same as NewControlFunction.
1591                  * Consider merging.
1592                  */
1593                 string prop_id = tag.PropertyID;
1594                 Type prop_type = tag.PropertyType;
1595                 string tag_id = tag.PropertyName; // Real property name used in FinishControlFunction
1596
1597                 controls.Push (prop_type, prop_id, tag_id, ChildrenKind.DBCOLUMNS, null);
1598                 current_function = new StringBuilder ();
1599                 functions.Push (current_function);
1600                 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
1601                                                 "(System.Web.UI.WebControls.DataGridColumnCollection __ctrl)\n" +
1602                                                 "\t\t{{\n", prop_id);
1603         }
1604
1605         private void NewPropertyFunction (PropertyTag tag)
1606         {
1607                 FlushPlainText ();
1608                 if (tag.PropertyType == typeof (System.Web.UI.WebControls.Style) ||
1609                     tag.PropertyType.IsSubclassOf (typeof (System.Web.UI.WebControls.Style)))
1610                         NewStyleFunction (tag);
1611                 else if (tag.PropertyType == typeof (System.Web.UI.ITemplate))
1612                         NewTemplateFunction (tag);
1613                 else if (tag.PropertyType == typeof (System.Web.UI.WebControls.DataGridColumnCollection))
1614                         NewDBColumnFunction (tag);
1615                 else
1616                         throw new ApplicationException ("Other than Style and ITemplate not supported yet. " + 
1617                                                         tag.PropertyType);
1618         }
1619         
1620         private void ProcessHtmlTag ()
1621         {
1622                 Tag tag = (Tag) current;
1623                 ChildrenKind child_kind = controls.PeekChildKind ();
1624                 if (child_kind == ChildrenKind.NONE){
1625                         string tag_id = controls.PeekTagID ();
1626                         throw new ApplicationException (tag + " not allowed inside " + tag_id);
1627                 }
1628                                         
1629                 if (child_kind == ChildrenKind.OPTION){
1630                         if (0 != String.Compare (tag.TagID, "option", true))
1631                                 throw new ApplicationException ("Only <option> tags allowed inside <select>.");
1632
1633                         string default_id = Tag.GetDefaultID ();
1634                         Type type = typeof (System.Web.UI.WebControls.ListItem);
1635                         AddProtectedField (type, default_id);
1636                         NewControlFunction (tag.TagID, default_id, type, ChildrenKind.CONTROLS, null); 
1637                         return;
1638                 }
1639
1640                 if (child_kind == ChildrenKind.CONTROLS) {
1641                         ArrayList tag_elements = tag.GetElements ();
1642                         foreach (Element e in tag_elements) {
1643                                 if (e is PlainText) {
1644                                         current = e;
1645                                         textChunk.Append (((PlainText) e).Text);
1646                                 } else if (e is CodeRenderTag) {
1647                                         current = e;
1648                                         ProcessCodeRenderTag ();
1649                                 } else if (e is DataBindingTag) {
1650                                         current = e;
1651                                         ProcessDataBindingLiteral ();
1652                                 } else {
1653                                         throw new ApplicationException (fullPath + ": unexpected tag type " + e.GetType ());
1654                                 }
1655                         }
1656                         return;
1657                 }
1658
1659                 if (child_kind == ChildrenKind.HTMLROW) {
1660                         if (0 == String.Compare (tag.TagID, "tr", true)) {
1661                                 current = new HtmlControlTag (tag);
1662                                 ProcessHtmlControlTag ();
1663                                 return;
1664                         }
1665                 }
1666
1667                 if (child_kind == ChildrenKind.HTMLCELL) {
1668                         if (0 == String.Compare (tag.TagID, "td", true)) {
1669                                 current = new HtmlControlTag (tag);
1670                                 ProcessHtmlControlTag ();
1671                                 return;
1672                         }
1673                 }
1674
1675                 // Now child_kind should be PROPERTIES, so only allow tag_id == property
1676                 Type control_type = controls.PeekType ();
1677                 PropertyInfo [] prop_info = control_type.GetProperties ();
1678                 bool is_processed = false;
1679                 foreach (PropertyInfo prop in prop_info){
1680                         if (0 == String.Compare (prop.Name, tag.TagID, true)){
1681                                 PropertyTag prop_tag = new PropertyTag (tag, prop.PropertyType, prop.Name);
1682                                 NewPropertyFunction (prop_tag);
1683                                 is_processed = true;
1684                                 break;
1685                         }
1686                 }
1687                 
1688                 if (!is_processed){
1689                         string tag_id = controls.PeekTagID ();
1690                         throw new ApplicationException (tag.TagID + " is not a property of " + control_type);
1691                 }
1692         }
1693
1694         private Tag Map (Tag tag)
1695         {
1696                 int pos = tag.TagID.IndexOf (":");
1697                 if (pos == -1) {
1698                         ChildrenKind child_kind = controls.PeekChildKind ();
1699                         if (child_kind == ChildrenKind.HTMLROW && 0 == String.Compare (tag.TagID, "tr", true)) {
1700                                 tag.Attributes.Add ("runat", "server");
1701                                 return new HtmlControlTag (tag);
1702                         } else if (child_kind == ChildrenKind.HTMLROW && 0 == String.Compare (tag.TagID, "tr", true)) {
1703                                 tag.Attributes.Add ("runat", "server");
1704                                 return new HtmlControlTag (tag);
1705                         }
1706                 }
1707
1708                 if (tag is CloseTag ||
1709                     ((tag.Attributes == null || 
1710                     !tag.Attributes.IsRunAtServer ()) && pos == -1))
1711                         return tag;
1712
1713                 if (pos == -1){
1714                         if (0 == String.Compare (tag.TagID, "object", true))
1715                                 return new ServerObjectTag (tag);
1716
1717                         return new HtmlControlTag (tag);
1718                 }
1719
1720                 string foundry_name = tag.TagID.Substring (0, pos);
1721                 string component_name = tag.TagID.Substring (pos + 1);
1722
1723                 if (Foundry.LookupFoundry (foundry_name) == false)
1724                         throw new ApplicationException ("Cannot find foundry for alias'" + foundry_name + "'");
1725
1726                 AspComponent component = Foundry.MakeAspComponent (foundry_name, component_name, tag);
1727                 if (component == null)
1728                         throw new ApplicationException ("Cannot find component '" + component_name + 
1729                                                         "' for alias '" + foundry_name + "'");
1730
1731                 return component;
1732         }
1733         
1734         private void ProcessCloseTag ()
1735         {
1736                 CloseTag closeTag = (CloseTag) current;
1737                 if (FinishControlFunction (closeTag.TagID))
1738                         return;
1739
1740                 textChunk.Append (closeTag.PlainHtml);
1741         }
1742
1743         private void ProcessDataBindingLiteral ()
1744         {
1745                 FlushPlainText ();
1746                 DataBindingTag dataBinding = (DataBindingTag) current;
1747                 string actual_value = dataBinding.Data;
1748                 if (actual_value == "")
1749                         throw new ApplicationException ("Empty data binding tag.");
1750
1751                 if (controls.PeekChildKind () != ChildrenKind.CONTROLS)
1752                         throw new ApplicationException ("Data bound content not allowed for " + 
1753                                                         controls.PeekTagID ());
1754
1755                 StringBuilder db_function = new StringBuilder ();
1756                 string control_id = Tag.GetDefaultID ();
1757                 string control_type_string = "System.Web.UI.DataBoundLiteralControl";
1758                 AddProtectedField (typeof (System.Web.UI.DataBoundLiteralControl), control_id);
1759                 // Build the control
1760                 db_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_{0} ()\n" +
1761                                           "\t\t{{\n\t\t\t{1} __ctrl;\n\n" +
1762                                           "\t\t\t__ctrl = new {1} (0, 1);\n" + 
1763                                           "\t\t\tthis.{0} = __ctrl;\n" +
1764                                           "\t\t\t__ctrl.DataBinding += new System.EventHandler " + 
1765                                           "(this.__DataBind_{0});\n" +
1766                                           "\t\t\treturn __ctrl;\n"+
1767                                           "\t\t}}\n\n",
1768                                           control_id, control_type_string);
1769                 // DataBinding handler
1770                 db_function.AppendFormat ("\t\tpublic void __DataBind_{0} (object sender, " + 
1771                                           "System.EventArgs e) {{\n" +
1772                                           "\t\t\t{1} Container;\n" +
1773                                           "\t\t\t{2} target;\n" +
1774                                           "\t\t\ttarget = ({2}) sender;\n" +
1775                                           "\t\t\tContainer = ({1}) target.BindingContainer;\n" +
1776                                           "\t\t\ttarget.SetDataBoundString (0, System.Convert." +
1777                                           "ToString ({3}));\n" +
1778                                           "\t\t}}\n\n",
1779                                           control_id, controls.Container, control_type_string,
1780                                           actual_value);
1781
1782                 init_funcs.Append (db_function);
1783                 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
1784                                                "AddParsedSubObject (this.{0});\n\n", control_id);
1785
1786                 AddCodeRenderControl (controls.CodeRenderFunction);
1787         }
1788
1789         private void ProcessCodeRenderTag ()
1790         {
1791                 FlushPlainText ();
1792                 CodeRenderTag code_tag = (CodeRenderTag) current;
1793
1794                 controls.UseCodeRender = true;
1795                 if (code_tag.IsVarName)
1796                         controls.CodeRenderFunction.AppendFormat ("\t\t\t__output.Write ({0});\n",
1797                                                                   code_tag.Code);
1798                 else
1799                         controls.CodeRenderFunction.AppendFormat ("\t\t\t{0}\n", code_tag.Code);
1800         }
1801         
1802         void FlushPlainText ()
1803         {
1804                 if (textChunk.Length != 0) {
1805                         Element saved = current;
1806                         current = new PlainText (textChunk.ToString ());
1807                         textChunk.Length = 0;
1808                         ProcessPlainText ();
1809                         current = saved;
1810                 }
1811         }
1812         
1813         void ParseError (string msg, int line, int col)
1814         {
1815                 throw new ParseException (fullPath, msg, line, col);
1816         }
1817
1818         void TagParsed (Tag tag, int line, int col)
1819         {
1820                 if (waitClosing != null) {
1821                         if (!(tag is CloseTag) || tag.TagID.ToUpper () != waitClosing.ToUpper ())
1822                                 throw new HttpException ("Tag " + waitClosing + " not properly closed.");
1823
1824                         waitClosing = null;
1825                         controls.Pop ();
1826                         return;
1827                 }
1828                 
1829                 if ((sstatus == ScriptStatus.Open || sstatus == ScriptStatus.Text) &&
1830                      tag.TagID.ToUpper () == "SCRIPT" && tag is CloseTag) {
1831                         sstatus = ScriptStatus.Close;
1832                         return;
1833                 }
1834
1835                 current = tag;
1836
1837                 if (current is Directive) {
1838                         ProcessDirective ();
1839                         return;
1840                 } else if (current is DataBindingTag) {
1841                         if (IsApplication)
1842                                 throw new ApplicationException (app_file_wrong);
1843
1844                         ProcessDataBindingLiteral ();
1845                         return;
1846                 } else if (current is CodeRenderTag) {
1847                         if (IsApplication)
1848                                 throw new ApplicationException (app_file_wrong);
1849
1850                         ProcessCodeRenderTag ();
1851                         return;
1852                 }
1853
1854                 current = Map (tag);
1855
1856                 if (current is ServerObjectTag) {
1857                         ProcessServerObjectTag ();
1858                         return;
1859                 } else if (current is HtmlControlTag) {
1860                         ProcessHtmlControlTag ();
1861                         return;
1862                 }
1863
1864                 if (IsApplication)
1865                         throw new ApplicationException (app_file_wrong);
1866
1867                 if (current is AspComponent) {
1868                         ProcessComponent ();
1869                 } else if (current is CloseTag) {
1870                         ProcessCloseTag ();
1871                 } else if (current is Tag) {
1872                         ProcessHtmlTag ();
1873                 } else {
1874                         throw new HttpException ("This place should not be reached.");
1875                 }
1876         }
1877
1878         void TextParsed (string text, int line, int col)
1879         {
1880                 if (sstatus == ScriptStatus.Open) {
1881                         script.Append (text);
1882                         sstatus = ScriptStatus.Text;
1883                         return;
1884                 }
1885
1886                 textChunk.Append (text);
1887         }
1888
1889         public void ProcessElements ()
1890         {
1891                 AspParser parser = new AspParser (fullPath, File.OpenRead (fullPath));
1892                 
1893                 parser.Error += new ParseErrorHandler (ParseError);
1894                 parser.TagParsed += new TagParsedHandler (TagParsed);
1895                 parser.TextParsed += new TextParsedHandler (TextParsed);
1896
1897                 try {
1898                         parser.Parse ();
1899                 } catch (CompilationException e) {
1900                         throw;
1901                 } catch (ParseException e) {
1902                         throw;
1903                 } catch (Exception e) {
1904                         throw new ParseException (fullPath, e.Message, parser.Line, parser.Column, e);
1905                 }
1906
1907                 End ();
1908                 parse_ok = true;
1909         }
1910         
1911         private string GetTemplateDirectory ()
1912         {
1913                 string templatePath = Path.GetDirectoryName (fullPath);
1914                 string appPath = Path.GetDirectoryName (HttpRuntime.AppDomainAppPath);
1915
1916                 if (templatePath == appPath)
1917                         return "/";
1918
1919                 templatePath = templatePath.Substring (appPath.Length);
1920                 if (Path.DirectorySeparatorChar != '/')
1921                         templatePath = templatePath.Replace (Path.DirectorySeparatorChar, '/');
1922                         
1923                 return templatePath;
1924         }
1925
1926         private void End ()
1927         {
1928                 FlushPlainText ();
1929
1930                 if (isPage) {
1931                         if (sessionState == SessionState.Enabled || sessionState == SessionState.ReadOnly)
1932                                 AddInterface (typeof (System.Web.SessionState.IRequiresSessionState));
1933
1934                         if (sessionState == SessionState.ReadOnly)
1935                                 AddInterface (typeof (System.Web.SessionState.IReadOnlySessionState));
1936                 }
1937                 
1938                 classDecl = "\tpublic class " + className + " : " + parent + interfaces + " {\n"; 
1939                 prolog.Append ("\n" + classDecl);
1940                 declarations.Append ("\t\tprivate static bool __intialized = false;\n\n");
1941                 if (IsPage)
1942                         declarations.Append ("\t\tprivate static ArrayList __fileDependencies;\n\n");
1943
1944                 // adds the constructor
1945                 constructor.AppendFormat ("\t\tpublic {0} ()\n\t\t{{\n", className);
1946                 if (!IsApplication)
1947                         constructor.Append ("\t\t\tSystem.Collections.ArrayList dependencies;\n\n");
1948                         
1949                 constructor.AppendFormat ("\t\t\tif (ASP.{0}.__intialized == false){{\n", className); 
1950
1951                 if (IsPage) {
1952                         constructor.AppendFormat ("\t\t\t\tdependencies = new System.Collections.ArrayList ();\n" +
1953                                                 "\t\t\t\tdependencies.Add (@\"{1}\");\n" +
1954                                                 "\t\t\t\tASP.{0}.__fileDependencies = dependencies;\n",
1955                                                 className, fullPath);
1956                 }
1957
1958                 constructor.AppendFormat ("\t\t\t\tASP.{0}.__intialized = true;\n\t\t\t}}\n\t\t}}\n\n",
1959                                           className);
1960          
1961                 if (!IsApplication) {
1962                         //FIXME: add AutoHandlers: don't know what for...yet!
1963                         constructor.AppendFormat (
1964                                 "\t\tprotected override int AutoHandlers\n\t\t{{\n" +
1965                                 "\t\t\tget {{ return ASP.{0}.__autoHandlers; }}\n" +
1966                                 "\t\t\tset {{ ASP.{0}.__autoHandlers = value; }}\n" +
1967                                 "\t\t}}\n\n", className);
1968
1969                         constructor.Append (
1970                                 "\t\tprotected System.Web.HttpApplication ApplicationInstance\n\t\t{\n" +
1971                                 "\t\t\tget { return (System.Web.HttpApplication) this.Context.ApplicationInstance; }\n" +
1972                                 "\t\t}\n\n");
1973
1974                         constructor.AppendFormat (
1975                                 "\t\tpublic override string TemplateSourceDirectory\n\t\t{{\n" +
1976                                 "\t\t\tget {{ return \"{0}\"; }}\n" +
1977                                 "\t\t}}\n\n", GetTemplateDirectory ());
1978
1979                         epilog.Append ("\n\t\tprotected override void FrameworkInitialize ()\n\t\t{\n" +
1980                                         "\t\t\tthis.__BuildControlTree (this);\n");
1981
1982                         if (IsPage) {
1983                                 epilog.AppendFormat ("\t\t\tthis.FileDependencies = ASP.{0}.__fileDependencies;\n" +
1984                                                         "\t\t\tthis.EnableViewStateMac = true;\n", className);
1985                         }
1986
1987                         epilog.Append ("\t\t}\n\n");
1988                 }
1989
1990                 if (IsPage) {
1991                         Random rnd = new Random ();
1992                         epilog.AppendFormat ("\t\tpublic override int GetTypeHashCode ()\n\t\t{{\n" +
1993                                              "\t\t\treturn {0};\n" +
1994                                              "\t\t}}\n", rnd.Next ());
1995                 }
1996
1997                 epilog.Append ("\t}\n}\n");
1998
1999                 // Closes the currently opened tags
2000                 StringBuilder old_function = current_function;
2001                 string control_id;
2002                 while (functions.Count > 1){
2003                         old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
2004                         init_funcs.Append (old_function);
2005                         control_id = controls.PeekControlID ();
2006                         FinishControlFunction (control_id);
2007                         controls.AddChild ();
2008                         old_function = (StringBuilder) functions.Pop ();
2009                         current_function = (StringBuilder) functions.Peek ();
2010                         controls.Pop ();
2011                 }
2012
2013                 bool useCodeRender = controls.UseCodeRender;
2014                 if (useCodeRender){
2015                         RemoveLiterals (current_function);
2016                         AddRenderMethodDelegate (current_function, controls.PeekControlID ());
2017                 }
2018                 
2019                 current_function.Append ("\t\t}\n\n");
2020                 init_funcs.Append (current_function);
2021                 if (useCodeRender)
2022                         AddCodeRenderFunction (controls.CodeRenderFunction.ToString (), controls.PeekControlID ());
2023
2024                 functions.Pop ();
2025         }
2026
2027         //
2028         // Functions related to compilation of user controls
2029         //
2030         
2031         private static char dirSeparator = Path.DirectorySeparatorChar;
2032         struct UserControlData
2033         {
2034                 public UserControlResult result;
2035                 public string className;
2036                 public string assemblyName;
2037         }
2038
2039         private UserControlData GenerateUserControl (string src, HttpContext context)
2040         {
2041                 UserControlData data = new UserControlData ();
2042                 data.result = UserControlResult.OK;
2043
2044                 UserControlCompiler compiler = new UserControlCompiler (new UserControlParser (src, context));
2045                 Type t = compiler.GetCompiledType ();
2046                 if (t == null) {
2047                         data.result = UserControlResult.CompilationFailed;
2048                         return data;
2049                 }
2050                 
2051                 data.className = t.Name;
2052                 data.assemblyName = compiler.TargetFile;
2053                 dependencies.Add (src);
2054                 foreach (string s in compiler.Dependencies)
2055                         dependencies.Add (s);
2056                 
2057                 return data;
2058         }
2059 }
2060
2061 }
2062