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