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