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