2003-01-20 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AspGenerator.cs
index c0fc6436b58bb1f18cfada1b997b87e82b4d4ec2..8aebca2408759595ea186742458bd5c6922a9802 100644 (file)
@@ -18,6 +18,7 @@ using System.Text.RegularExpressions;
 using System.Web.UI;
 using System.Web.UI.HtmlControls;
 using System.Web.UI.WebControls;
+using System.Web.Util;
 
 namespace System.Web.Compilation
 {
@@ -41,6 +42,7 @@ class ControlStack
                public StringBuilder dataBindFunction;
                public StringBuilder codeRenderFunction;
                public bool useCodeRender;
+               public int codeRenderIndex;
 
                public ControlStackData (Type controlType,
                                         string controlID,
@@ -82,12 +84,24 @@ class ControlStack
                        container_type = typeof (System.Web.UI.WebControls.DataGridItem);
                else if (type == typeof (System.Web.UI.WebControls.Repeater))
                        container_type = typeof (System.Web.UI.WebControls.RepeaterItem);
-               else 
+               else if (type == typeof (ListControl) || type.IsSubclassOf (typeof (ListControl)))
                        container_type = type;
+               else
+                       container_type = Container;
 
                return container_type;
        }
 
+       public void Push (object o)
+       {
+               if (!(o is ControlStackData))
+                       return;
+
+               controls.Push (o);
+               top = (ControlStackData) o;
+               sbt_valid = false;
+       }
+       
        public void Push (Type controlType,
                          string controlID,
                          string tagID,
@@ -112,12 +126,13 @@ class ControlStack
                controls.Push (top);
        }
 
-       public void Pop ()
+       public object Pop ()
        {
-               controls.Pop ();
+               object item = controls.Pop ();
                if (controls.Count != 0)
                        top = (ControlStackData) controls.Peek ();
                sbt_valid = false;
+               return item;
        }
 
        public Type PeekType ()
@@ -181,6 +196,12 @@ class ControlStack
                                        space_between_tags = true;
                                else if (type == typeof (System.Web.UI.HtmlControls.HtmlSelect))
                                        space_between_tags = true;
+                               else if (type == typeof (System.Web.UI.HtmlControls.HtmlTable))
+                                       space_between_tags = true;
+                               else if (type == typeof (System.Web.UI.HtmlControls.HtmlTableRow))
+                                       space_between_tags = true;
+                               else if (type == typeof (System.Web.UI.HtmlControls.HtmlTableCell))
+                                       space_between_tags = true;
                                else
                                        space_between_tags = false;
                        }
@@ -188,9 +209,13 @@ class ControlStack
                }
        }
        
-       public Type Container
-       {
-               get { return top.container; }
+       public Type Container {
+               get {
+                       if (top == null)
+                               return null;
+                               
+                       return top.container;
+               }
        }
        
        public StringBuilder DataBindFunction
@@ -202,6 +227,12 @@ class ControlStack
                }
        }
 
+       public int CodeRenderIndex {
+               get {
+                       return top.codeRenderIndex++;
+               }
+       }
+       
        public StringBuilder CodeRenderFunction
        {
                get {
@@ -271,7 +302,6 @@ class AspGenerator
 {
        private object [] parts;
        private ArrayListWrapper elements;
-       private StringBuilder buildOptions;
        private StringBuilder prolog;
        private StringBuilder declarations;
        private StringBuilder script;
@@ -288,18 +318,41 @@ class AspGenerator
        private string classDecl;
        private string className;
        private string interfaces;
+       private string basetype;
        private string parent;
+       private Type parentType;
        private string fullPath;
-       private static string enableSessionStateLiteral =  ", System.Web.SessionState.IRequiresSessionState";
+
+       Hashtable options;
+       string privateBinPath;
+       string main_directive;
+       static string app_file_wrong = "The content in the application file is not valid.";
+
+       bool isPage;
+       bool isUserControl;
+       bool isApplication;
+
+       HttpContext context;
+
+       SessionState sessionState = SessionState.Enabled;
+
+       static Type styleType = typeof (System.Web.UI.WebControls.Style);
+       static Type fontinfoType = typeof (System.Web.UI.WebControls.FontInfo);
 
        enum UserControlResult
        {
                OK = 0,
                FileNotFound = 1,
-               XspFailed = 2,
-               CompilationFailed = 3
+               CompilationFailed = 2
        }
 
+       enum SessionState
+       {
+               Enabled,
+               ReadOnly,
+               Disabled
+       }
+       
        public AspGenerator (string pathToFile, ArrayList elements)
        {
                if (elements == null)
@@ -310,50 +363,86 @@ class AspGenerator
                this.className = filename.Replace ('.', '_'); // Overridden by @ Page classname
                this.className = className.Replace ('-', '_'); 
                this.className = className.Replace (' ', '_');
+               Options ["ClassName"] = this.className;
                this.fullPath = Path.GetFullPath (pathToFile);
-               /*
-               if (IsUserControl) {
-                       this.parent = "System.Web.UI.UserControl"; // Overriden by @ Control Inherits
-                       this.interfaces = "";
-               } else {
-                       this.parent = "System.Web.UI.Page"; // Overriden by @ Page Inherits
-                       this.interfaces = enableSessionStateLiteral;
-               }
-               //
-               //*/
+
                this.has_form_tag = false;
+               AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
+               privateBinPath = setup.PrivateBinPath;
+               // This is a hack until we can run stuff in different domains
+               if (privateBinPath == null || privateBinPath.Length == 0)
+                       privateBinPath = "bin";
+                       
+               if (!Path.IsPathRooted (privateBinPath))
+                       privateBinPath = Path.Combine (setup.ApplicationBase, privateBinPath);
+               
                Init ();
        }
 
-       public string BaseType
-       {
-               get {
-                       return parent;
-               }
+       public string BaseType {
+               get { return basetype; }
 
                set {
-                       parent = value;
+                       if (parent == null)
+                               parent = value;
+
+                       basetype = value;
+                       isUserControl = (basetype == "System.Web.UI.UserControl");
+                       isPage = (basetype == "System.Web.UI.Page");
+                       isApplication = (basetype == "System.Web.HttpApplication");
                }
        }
 
-       public bool IsUserControl
-       {
+       public bool IsUserControl {
+               get { return isUserControl; }
+       }
+       
+       public bool IsPage {
+               get { return isPage; }
+       }
+       
+       public bool IsApplication {
+               get { return isApplication; }
+       }
+
+       public string Interfaces {
+               get { return interfaces; }
+       }
+
+       public Hashtable Options {
                get {
-                       return (BaseType == typeof (UserControl).ToString ());
+                       if (options == null)
+                               options = new Hashtable ();
+
+                       return options;
                }
        }
        
-       public string Interfaces 
+       internal HttpContext Context {
+               get { return context; }
+               set { context = value; }
+       }
+       
+       bool AddUsing (string nspace)
        {
-               get {
-                       return interfaces;
+               string _using = "using " + nspace + ";";
+               if (prolog.ToString ().IndexOf (_using) == -1) {
+                       prolog.AppendFormat ("\t{0}\n", _using);
+                       return true;
                }
+
+               return false;
        }
 
+       void AddInterface (Type type)
+       {
+               AddInterface (type.ToString ());
+       }
+       
        public void AddInterface (string iface)
        {
-               if (interfaces == "") {
-                       interfaces = iface;
+               if (interfaces == null) {
+                       interfaces = ", " + iface;
                } else {
                        string s = ", " + iface;
                        if (interfaces.IndexOf (s) == -1)
@@ -381,20 +470,18 @@ class AspGenerator
                constructor = new StringBuilder ();
                init_funcs = new StringBuilder ();
                epilog = new StringBuilder ();
-               buildOptions = new StringBuilder ();
 
                current_function = new StringBuilder ();
                functions = new Stack ();
                functions.Push (current_function);
 
-               parts = new Object [7];
-               parts [0] = buildOptions;
-               parts [1] = prolog;
-               parts [2] = declarations;
-               parts [3] = script;
-               parts [4] = constructor;
-               parts [5] = init_funcs;
-               parts [6] = epilog;
+               parts = new Object [6];
+               parts [0] = prolog;
+               parts [1] = declarations;
+               parts [2] = script;
+               parts [3] = constructor;
+               parts [4] = init_funcs;
+               parts [5] = epilog;
 
                prolog.Append ("namespace ASP {\n" +
                              "\tusing System;\n" + 
@@ -446,6 +533,9 @@ class AspGenerator
        // Regex.Escape () make some illegal escape sequences for a C# source.
        private string Escape (string input)
        {
+               if (input == null)
+                       return String.Empty;
+
                string output = input.Replace ("\\", "\\\\");
                output = output.Replace ("\"", "\\\"");
                output = output.Replace ("\t", "\\t");
@@ -455,41 +545,133 @@ class AspGenerator
                return output;
        }
        
+       bool AddProtectedField (Type type, string fieldName)
+       {
+               if (parentType == null) {
+                       declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
+                       return true;
+               }
+
+               FieldInfo field = parentType.GetField (fieldName, BindingFlags.Public |
+                                                                 BindingFlags.NonPublic |
+                                                                 BindingFlags.Instance |
+                                                                 BindingFlags.Static);
+
+               if (field == null || (!field.IsPublic && !field.IsFamily)) {
+                       declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
+                       return true;
+               }
+
+               if (!field.FieldType.IsAssignableFrom (type)) {
+                       string message = String.Format ("The base class includes the field '{0}', but its " +
+                                                       "type '{1}' is not compatible with {2}",
+                                                       fieldName, field.FieldType, type);
+
+                       throw new ApplicationException (message);
+               }
+
+               return false;
+       }
+       
+       private Type LoadParentType (string typeName)
+       {
+               // First try loaded assemblies, then try assemblies in Bin directory.
+               // By now i do this 'by hand' but may be this is a runtime/gac task.
+               Type type = null;
+               Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
+               foreach (Assembly ass in assemblies) {
+                       type = ass.GetType (typeName);
+                       if (type != null)
+                               return type;
+               }
+
+               Assembly assembly;
+               string [] binDlls = Directory.GetFiles (privateBinPath, "*.dll");
+               foreach (string dll in binDlls) {
+                       string dllPath = Path.Combine (privateBinPath, dll);
+                       assembly = null;
+                       try {
+                               assembly = Assembly.LoadFrom (dllPath);
+                               type = assembly.GetType (typeName);
+                       } catch (Exception e) {
+                               if (assembly != null) {
+                                       Console.WriteLine ("ASP.NET Warning: assembly {0} loaded", dllPath);
+                                       Console.WriteLine ("ASP.NET Warning: but type {0} not found", typeName);
+                               } else {
+                                       Console.WriteLine ("ASP.NET Warning: unable to load type {0} from {1}",
+                                                          typeName, dllPath);
+                               }
+                               Console.WriteLine ("ASP.NET Warning: error was: {0}", e.Message);
+                       }
+
+                       if (type != null)
+                               return type;
+               }
+
+               return null;
+       }
+
        private void PageDirective (TagAttributes att)
        {
                if (att ["ClassName"] != null){
                        this.className = (string) att ["ClassName"];
+                       Options ["ClassName"] = className;
                }
 
                if (att ["EnableSessionState"] != null){
+                       if (!IsPage)
+                               throw new ApplicationException ("EnableSessionState not allowed here.");
+                       
                        string est = (string) att ["EnableSessionState"];
                        if (0 == String.Compare (est, "false", true))
-                               interfaces = interfaces.Replace (enableSessionStateLiteral, "");
-                       else if (0 != String.Compare (est, "true", true))
+                               sessionState = SessionState.Disabled;
+                       else if (0 == String.Compare (est, "true", true))
+                               sessionState = SessionState.Enabled;
+                       else if (0 == String.Compare (est, "readonly", true))
+                               sessionState = SessionState.ReadOnly;
+                       else
                                throw new ApplicationException ("EnableSessionState in Page directive not set to " +
                                                                "a correct value: " + est);
                }
 
-               /*
-               if (att ["Inherits"] != null){
+               if (att ["Inherits"] != null) {
                        parent = (string) att ["Inherits"];
-                       string source_file = att ["Src"] as string;
-                       if (source_file != null)
-                               buildOptions.AppendFormat ("//<compileandreference src=\"{0}\"/>\n", source_file);
-                       else
-                               buildOptions.AppendFormat ("//<reference dll=\"{0}\"/>\n", parent);
-
+                       parentType = LoadParentType (parent);
+                       if (parentType == null)
+                               throw new ApplicationException ("The class " + parent + " cannot be found.");
                }
-               */
 
-               if (att ["CompilerOptions"] != null){
-                       string compilerOptions = (string) att ["CompilerOptions"];
-                       buildOptions.AppendFormat ("//<compileroptions options=\"{0}\"/>\n", compilerOptions);
+               if (att ["CompilerOptions"] != null)
+                       Options ["CompilerOptions"] = (string) att ["CompilerOptions"];
+
+               if (att ["AutoEventWireup"] != null) {
+                       if (options ["AutoEventWireup"] != null)
+                               throw new ApplicationException ("Already have an AutoEventWireup attribute");
+                       
+                       bool autoevent = true;
+                       string v = att ["AutoEventWireup"] as string;
+                       try {
+                               autoevent = Convert.ToBoolean (v);
+                       } catch (Exception) {
+                               throw new ApplicationException ("'" + v + "' is not a valid value for AutoEventWireup");
+                       }
+                       options ["AutoEventWireup"] = autoevent;
                }
 
                //FIXME: add support for more attributes.
        }
 
+       void AddReference (string dll)
+       {
+               string references = Options ["References"] as string;
+               if (references == null)
+                       references = dll;
+               else
+                       references = references + " " + dll;
+
+               Options ["References"] = references;
+       }
+
        private void RegisterDirective (TagAttributes att)
        {
                string tag_prefix = (string) (att ["tagprefix"] == null ?  "" : att ["tagprefix"]);
@@ -502,10 +684,16 @@ class AspGenerator
                        if (tag_name != "" || src != "")
                                throw new ApplicationException ("Invalid attributes for @ Register: " +
                                                                att.ToString ());
-                       prolog.AppendFormat ("\tusing {0};\n", name_space);
-                       string dll = "output" + Path.DirectorySeparatorChar + assembly_name + ".dll";
+
+                       AddUsing (name_space);
+                       string dll = privateBinPath + Path.DirectorySeparatorChar + assembly_name + ".dll";
+                       // Hack: it should use assembly.load semantics...
+                       // may be when we don't run mcs as a external program...
+                       if (!File.Exists (dll))
+                               dll = assembly_name;
+
                        Foundry.RegisterFoundry (tag_prefix, dll, name_space);
-                       buildOptions.AppendFormat ("//<reference dll=\"{0}\"/>\n", dll);
+                       AddReference (dll);
                        return;
                }
 
@@ -518,27 +706,15 @@ class AspGenerator
                                throw new ApplicationException ("Source file extension for controls " + 
                                                                "must be .ascx");
 
-                       string pathToFile = Path.GetDirectoryName (src);
-                       if (pathToFile == "") {
-                               pathToFile = Path.GetDirectoryName (fullPath);
-                       } else if (!Path.IsPathRooted (pathToFile)) {
-                               pathToFile = Path.Combine  (Path.GetDirectoryName (fullPath), pathToFile);
-                       }
-
-                       string srcLocation = pathToFile + Path.DirectorySeparatorChar + Path.GetFileName (src);
-                       UserControlData data = GenerateUserControl (srcLocation);
+                       UserControlData data = GenerateUserControl (src, Context);
                        switch (data.result) {
                        case UserControlResult.OK:
-                               prolog.AppendFormat ("\tusing {0};\n", "ASP");
-                               string dll = "output" + Path.DirectorySeparatorChar + data.assemblyName + ".dll";
-                               Foundry.RegisterFoundry (tag_prefix, data.assemblyName, "ASP", data.className);
-                               buildOptions.AppendFormat ("//<reference dll=\"{0}\"/>\n", data.assemblyName);
+                               AddUsing ("ASP");
+                               Foundry.RegisterFoundry (tag_prefix, tag_name, data.assemblyName, "ASP", data.className);
+                               AddReference (data.assemblyName);
                                break;
                        case UserControlResult.FileNotFound:
                                throw new ApplicationException ("File '" + src + "' not found.");
-                       case UserControlResult.XspFailed:
-                               //TODO
-                               throw new NotImplementedException ();
                        case UserControlResult.CompilationFailed:
                                //TODO: should say where the generated .cs file is for the server to
                                //show the source and the compiler error
@@ -558,33 +734,88 @@ class AspGenerator
                if (att == null)
                        return;
 
+               string value;
                string id = directive.TagID.ToUpper ();
                switch (id){
+               case "APPLICATION":
+                       if (main_directive != null)
+                               throw new ApplicationException (id + " not allowed after " + main_directive);
+
+                       if (!IsApplication)
+                               throw new ApplicationException ("@Application not allowed.");
+
+                       string inherits = att ["inherits"] as string;
+                       if (inherits != null)
+                               Options ["Inherits"] = inherits;
+
+                       main_directive = directive.TagID;
+                       break;
                case "PAGE":
                case "CONTROL":
+                       if (main_directive != null)
+                               throw new ApplicationException (id + " not allowed after " + main_directive);
+
                        if (IsUserControl && id != "CONTROL")
-                               throw new ApplicationException ("@Page not allowed if --control specified.");
-                       else if (!IsUserControl && id != "PAGE")
-                               throw new ApplicationException ("@Control not allowed here.");
+                               throw new ApplicationException ("@Page not allowed for user controls.");
+                       else if (IsPage && id != "PAGE")
+                               throw new ApplicationException ("@Control not allowed here. This is a page!");
+
                        PageDirective (att);
+                       main_directive = directive.TagID;
                        break;
                case "IMPORT":
-                       foreach (string key in att.Keys){
-                               if (0 == String.Compare (key, "NAMESPACE", true)){
-                                       string _using = "using " + (string) att [key] + ";";
-                                       if (prolog.ToString ().IndexOf (_using) == -1)
-                                               prolog.AppendFormat ("\tusing {0};\n", (string) att [key]);
-                                       break;
+                       value = att ["namespace"] as string;
+                       if (value == null || att.Count > 1)
+                               throw new ApplicationException ("Wrong syntax in Import directive.");
+
+                       string _using = "using " + value + ";";
+                       if (AddUsing (value) == true) {
+                               string imports = Options ["Import"] as string;
+                               if (imports == null) {
+                                       imports = value;
+                               } else {
+                                       imports += "," + value;
                                }
+
+                               Options ["Import"] = imports;
                        }
                        break;
                case "IMPLEMENTS":
+                       if (IsApplication)
+                               throw new ApplicationException ("@ Implements not allowed in an application file.");
+
                        string iface = (string) att ["interface"];
-                       interfaces += ", " + iface;
+                       AddInterface (iface);
                        break;
                case "REGISTER":
+                       if (IsApplication)
+                               throw new ApplicationException ("@ Register not allowed in an application file.");
+
                        RegisterDirective (att);
                        break;
+               case "ASSEMBLY":
+                       if (att.Count > 1)
+                               throw new ApplicationException ("Wrong syntax in Assembly directive.");
+
+                       string name = att ["name"] as string;
+                       string src = att ["src"] as string;
+
+                       if (name == null && src == null)
+                               throw new ApplicationException ("Wrong syntax in Assembly directive.");
+
+                       if (IsApplication && src != null)
+                               throw new ApplicationException ("'name' attribute expected.");
+
+                       value = (name == null) ? src : name;
+                       string assemblies = Options ["Assembly"] as string;
+                       if (assemblies == null) {
+                               assemblies = value;
+                       } else {
+                               assemblies += "," + value;
+                       }
+
+                       Options ["Assembly"] = assemblies;
+                       break;
                }
        }
 
@@ -592,10 +823,16 @@ class AspGenerator
        {
                PlainText asis = (PlainText) elements.Current;
                string trimmed = asis.Text.Trim ();
-               if (trimmed == "" && controls.SpaceBetweenTags == true)
+               if (trimmed == String.Empty && controls.SpaceBetweenTags == true)
                        return;
 
-               if (trimmed != "" && controls.PeekChildKind () != ChildrenKind.CONTROLS){
+               if (IsApplication) {
+                       if (trimmed != String.Empty)
+                               throw new ApplicationException (app_file_wrong);
+                       return;
+               }
+
+               if (trimmed != String.Empty && controls.PeekChildKind () != ChildrenKind.CONTROLS){
                        string tag_id = controls.PeekTagID ();
                        throw new ApplicationException ("Literal content not allowed for " + tag_id);
                }
@@ -654,6 +891,16 @@ class AspGenerator
                        throw new ApplicationException ("Inside " + controls.PeekTagID () + " only " + 
                                                        "System.Web.UI.WebControls.ListItem " + 
                                                        "objects are allowed");
+               else if (prev_children_kind == ChildrenKind.HTMLROW &&
+                        control_type != typeof (System.Web.UI.HtmlControls.HtmlTableRow))
+                       throw new ApplicationException ("Inside " + controls.PeekTagID () + " only " + 
+                                                       "System.Web.UI.HtmlControls.HtmlTableRow " + 
+                                                       "objects are allowed");
+               else if (prev_children_kind == ChildrenKind.HTMLCELL &&
+                        control_type != typeof (System.Web.UI.HtmlControls.HtmlTableCell))
+                       throw new ApplicationException ("Inside " + controls.PeekTagID () + " only " + 
+                                                       "System.Web.UI.HtmlControls.HtmlTableCell " + 
+                                                       "objects are allowed");
        
                                        
                StringBuilder func_code = new StringBuilder ();
@@ -667,24 +914,26 @@ class AspGenerator
                controls.Push (control_type, control_id, tag_id, children_kind, defaultPropertyName);
                bool is_generic = control_type ==  typeof (System.Web.UI.HtmlControls.HtmlGenericControl);
                functions.Push (current_function);
-               if (control_type != typeof (System.Web.UI.WebControls.ListItem))
+               if (control_type != typeof (System.Web.UI.WebControls.ListItem) &&
+                   prev_children_kind != ChildrenKind.DBCOLUMNS) {
                        current_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_" +
                                                        "{0} ()\n\t\t{{\n\t\t\t{1} __ctrl;\n\n\t\t\t__ctrl" +
                                                        " = new {1} ({2});\n\t\t\tthis.{0} = __ctrl;\n",
                                                        control_id, control_type,
                                                        (is_generic? "\"" + tag_id + "\"" : ""));
-               else
+               } else {
                        current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ()\n\t\t{{" +
                                                        "\n\t\t\t{1} __ctrl;\n\t\t\t__ctrl = new {1} ();" +
                                                        "\n\t\t\tthis.{0} = __ctrl;\n",
                                                        control_id, control_type);
+               }
 
                if (children_kind == ChildrenKind.CONTROLS || children_kind == ChildrenKind.OPTION)
                        current_function.Append ("\t\t\tSystem.Web.UI.IParserAccessor __parser = " + 
                                                 "(System.Web.UI.IParserAccessor) __ctrl;\n");
        }
        
-       private void DataBoundProperty (string varName, string value)
+       private void DataBoundProperty (Type target, string varName, string value)
        {
                if (value == "")
                        throw new ApplicationException ("Empty data binding tag.");
@@ -692,7 +941,13 @@ class AspGenerator
                string control_id = controls.PeekControlID ();
                string control_type_string = controls.PeekType ().ToString ();
                StringBuilder db_function = controls.DataBindFunction;
-               string container = "System.Web.UI.Control";
+               string container;
+               if (controls.Container == null || !typeof (INamingContainer).IsAssignableFrom (controls.Container))
+                       container = "System.Web.UI.Control";
+               else {
+                       container = controls.Container.ToString ();
+               }
+
                if (db_function.Length == 0)
                        db_function.AppendFormat ("\t\tpublic void __DataBind_{0} (object sender, " + 
                                                  "System.EventArgs e) {{\n" +
@@ -707,39 +962,42 @@ class AspGenerator
                real_value = real_value.Remove (real_value.Length - 2, 2);
                real_value = real_value.Trim ();
 
-               db_function.AppendFormat ("\t\t\ttarget.{0} = System.Convert.ToString ({1});\n",
-                                         varName, real_value);
+               if (target == typeof (string))
+                       db_function.AppendFormat ("\t\t\ttarget.{0} = System.Convert.ToString ({1});\n",
+                                                 varName, real_value);
+               else
+                       db_function.AppendFormat ("\t\t\ttarget.{0} = ({1}) ({2});\n",
+                                                 varName, target, real_value);
        }
 
        /*
         * Returns true if it generates some code for the specified property
         */
-       private void AddPropertyCode (Type prop_type, string var_name, string att, bool isDataBound)
+       private void AddCodeForPropertyOrField (Type type, string var_name, string att, bool isDataBound)
        {
                /* FIXME: should i check for this or let the compiler fail?
                 * if (!prop.CanWrite)
                 *    ....
                 */
-               if (prop_type == typeof (string)){
+               if (isDataBound) {
+                       DataBoundProperty (type, var_name, att);
+               }
+               else if (type == typeof (string)){
                        if (att == null)
                                throw new ApplicationException ("null value for attribute " + var_name );
 
-                       if (isDataBound)
-                               DataBoundProperty (var_name, att);
-                       else
-                               current_function.AppendFormat ("\t\t\t__ctrl.{0} = \"{1}\";\n", var_name,
-                                                               Escape (att)); // FIXME: really Escape this?
-                               
+                       current_function.AppendFormat ("\t\t\t__ctrl.{0} = \"{1}\";\n", var_name,
+                                                       Escape (att)); // FIXME: really Escape this?
                } 
-               else if (prop_type.IsEnum){
+               else if (type.IsEnum){
                        if (att == null)
                                throw new ApplicationException ("null value for attribute " + var_name );
 
-                       string enum_value = EnumValueNameToString (prop_type, att);
+                       string enum_value = EnumValueNameToString (type, att);
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, enum_value);
                } 
-               else if (prop_type == typeof (bool)){
+               else if (type == typeof (bool)){
                        string value;
                        if (att == null)
                                value = "true"; //FIXME: is this ok for non Style properties?
@@ -752,7 +1010,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (System.Web.UI.WebControls.Unit)){
+               else if (type == typeof (System.Web.UI.WebControls.Unit)){
                         //FIXME: should use the culture specified in Page
                        try {
                                Unit value = Unit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
@@ -764,7 +1022,7 @@ class AspGenerator
                                                        "System.Globalization.CultureInfo.InvariantCulture);\n", 
                                                        var_name, att);
                }
-               else if (prop_type == typeof (System.Web.UI.WebControls.FontUnit)){
+               else if (type == typeof (System.Web.UI.WebControls.FontUnit)){
                         //FIXME: should use the culture specified in Page
                        try {
                                FontUnit value = FontUnit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
@@ -776,9 +1034,7 @@ class AspGenerator
                                                        "System.Globalization.CultureInfo.InvariantCulture);\n", 
                                                        var_name, att);
                }
-               else if (prop_type == typeof (Int16) ||
-                        prop_type == typeof (Int32) ||
-                        prop_type == typeof (Int64)){
+               else if (type == typeof (Int16) || type == typeof (Int32) || type == typeof (Int64)) {
                        long value;
                        try {
                                value = Int64.Parse (att); //FIXME: should use the culture specified in Page
@@ -789,9 +1045,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (UInt16) ||
-                        prop_type == typeof (UInt32) ||
-                        prop_type == typeof (UInt64)){
+               else if (type == typeof (UInt16) || type == typeof (UInt32) || type == typeof (UInt64)) {
                        ulong value;
                        try {
                                value = UInt64.Parse (att); //FIXME: should use the culture specified in Page
@@ -802,7 +1056,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (float)){
+               else if (type == typeof (float)) {
                        float value;
                        try {
                                value = Single.Parse (att);
@@ -813,7 +1067,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (double)){
+               else if (type == typeof (double)){
                        double value;
                        try {
                                value = Double.Parse (att);
@@ -824,7 +1078,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (System.Drawing.Color)){
+               else if (type == typeof (System.Drawing.Color)){
                        Color c;
                        try {
                                c = (Color) TypeDescriptor.GetConverter (typeof (Color)).ConvertFromString (att);
@@ -843,33 +1097,49 @@ class AspGenerator
                                                               "FromArgb ({1}, {2}, {3}, {4});\n",
                                                               var_name, c.A, c.R, c.G, c.B);
                        }
-               }       
-               else {
+               }
+               else if (type == typeof (string [])) {
+                       string [] subStrings = att.Split (',');
+                       current_function.AppendFormat ("\t\t\t__ctrl.{0} = new String [] {{\n", var_name);
+                       int end = subStrings.Length;
+                       for (int i = 0; i < end; i++) {
+                               string s = subStrings [i].Trim ();
+                               current_function.AppendFormat ("\t\t\t\t\"{0}\"", s);
+                               if (i == end - 1)
+                                       current_function.Append ("\t\t\t\t};\n");
+                               else
+                                       current_function.Append (",\n");
+                       }
+               } else {
                        throw new ApplicationException ("Unsupported type in property: " + 
-                                                       prop_type.ToString ());
+                                                       type.ToString ());
                }
        }
 
-       private bool ProcessProperties (PropertyInfo prop, string id, TagAttributes att)
+       private bool ProcessPropertiesAndFields (MemberInfo member, string id, TagAttributes att)
        {
                int hyphen = id.IndexOf ('-');
 
-               if (hyphen == -1 && prop.CanWrite == false)
-                       return false;
+               bool isPropertyInfo = (member is PropertyInfo);
 
                bool is_processed = false;
                bool isDataBound = att.IsDataBound ((string) att [id]);
-               Type type = prop.PropertyType;
-               Type style = typeof (System.Web.UI.WebControls.Style);
-               Type fontinfo = typeof (System.Web.UI.WebControls.FontInfo);
+               Type type;
+               if (isPropertyInfo) {
+                       type = ((PropertyInfo) member).PropertyType;
+                       if (hyphen == -1 && ((PropertyInfo) member).CanWrite == false)
+                               return false;
+               } else {
+                       type = ((FieldInfo) member).FieldType;
+               }
 
-               if (0 == String.Compare (prop.Name, id, true)){
-                       AddPropertyCode (type, prop.Name, (string) att [id], isDataBound);
+               if (0 == String.Compare (member.Name, id, true)){
+                       AddCodeForPropertyOrField (type, member.Name, (string) att [id], isDataBound);
                        is_processed = true;
-               } else if ((type == fontinfo || type == style || type.IsSubclassOf (style)) && hyphen != -1){
+               } else if (hyphen != -1 && (type == fontinfoType || type == styleType || type.IsSubclassOf (styleType))){
                        string prop_field = id.Replace ("-", ".");
                        string [] parts = prop_field.Split (new char [] {'.'});
-                       if (parts.Length != 2 || 0 != String.Compare (prop.Name, parts [0], true))
+                       if (parts.Length != 2 || 0 != String.Compare (member.Name, parts [0], true))
                                return false;
 
                        PropertyInfo [] subprops = type.GetProperties ();
@@ -881,19 +1151,19 @@ class AspGenerator
                                        return false;
 
                                bool is_bool = subprop.PropertyType == typeof (bool);
-                               if (!is_bool && att == null){
+                               if (!is_bool && att [id] == null){
                                        att [id] = ""; // Font-Size -> Font-Size="" as html
                                        return false;
                                }
 
                                string value;
-                               if (att == null && is_bool)
+                               if (att [id] == null && is_bool)
                                        value = "true"; // Font-Bold <=> Font-Bold="true"
                                else
                                        value = (string) att [id];
 
-                               AddPropertyCode (subprop.PropertyType,
-                                                prop.Name + "." + subprop.Name,
+                               AddCodeForPropertyOrField (subprop.PropertyType,
+                                                member.Name + "." + subprop.Name,
                                                 value, isDataBound);
                                is_processed = true;
                        }
@@ -906,6 +1176,7 @@ class AspGenerator
        {
                EventInfo [] ev_info = type.GetEvents ();
                PropertyInfo [] prop_info = type.GetProperties ();
+               FieldInfo [] field_info = type.GetFields ();
                bool is_processed = false;
                ArrayList processed = new ArrayList ();
 
@@ -932,11 +1203,19 @@ class AspGenerator
                        } 
 
                        foreach (PropertyInfo prop in prop_info){
-                               is_processed = ProcessProperties (prop, id, att);
+                               is_processed = ProcessPropertiesAndFields (prop, id, att);
                                if (is_processed)
                                        break;
                        }
 
+                       if (!is_processed) {
+                               foreach (FieldInfo field in field_info){
+                                       is_processed = ProcessPropertiesAndFields (field, id, att);
+                                       if (is_processed)
+                                               break;
+                               }
+                       }
+
                        if (is_processed){
                                is_processed = false;
                                continue;
@@ -948,6 +1227,11 @@ class AspGenerator
                }
        }
        
+       private void AddCodeRenderControl (StringBuilder function)
+       {
+               AddCodeRenderControl (function, controls.CodeRenderIndex);
+       }
+
        private void AddCodeRenderControl (StringBuilder function, int index)
        {
                function.AppendFormat ("\t\t\tparameterContainer.Controls [{0}]." + 
@@ -998,6 +1282,7 @@ class AspGenerator
 
                string control_id = controls.PeekControlID ();
                Type control_type = controls.PeekType ();
+               ChildrenKind child_kind = controls.PeekChildKind ();
 
                bool hasDataBindFunction = controls.HasDataBindFunction ();
                if (hasDataBindFunction)
@@ -1026,7 +1311,8 @@ class AspGenerator
                        old_function.Append ("\n\t\t}\n\n");
                        string parsed = "";
                        string ctrl_name = "ctrl";
-                       if (controls.Container == typeof (System.Web.UI.HtmlControls.HtmlSelect)){
+                       Type cont = controls.Container;
+                       if (cont == null || cont == typeof (System.Web.UI.HtmlControls.HtmlSelect)){
                                parsed = "ParsedSubObject";
                                ctrl_name = "parser";
                        }
@@ -1035,7 +1321,7 @@ class AspGenerator
                                                       "\t\t\t__{1}.Add{2} (this.{0});\n\n",
                                                       control_id, ctrl_name, parsed);
                }
-               else if (controls.PeekChildKind () == ChildrenKind.LISTITEM){
+               else if (child_kind == ChildrenKind.LISTITEM){
                        old_function.Append ("\n\t\t}\n\n");
                        init_funcs.Append (old_function); // Closes the BuildList function
                        old_function = (StringBuilder) functions.Pop ();
@@ -1048,8 +1334,40 @@ class AspGenerator
                        control_id = controls.PeekControlID ();
                        current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
                                                       "AddParsedSubObject (this.{0});\n\n", control_id);
-               }
-               else {
+               } else if (control_type == typeof (HtmlTableCell)) {
+                       old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
+                       object top = controls.Pop ();
+                       Type t = controls.PeekType ();
+                       controls.Push (top);
+                       string parsed = "";
+                       string ctrl_name = "ctrl";
+                       if (t != typeof (HtmlTableRow)) {
+                               parsed = "ParsedSubObject";
+                               ctrl_name = "parser";
+                       }
+
+                       current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n" +
+                                                      "\t\t\t__{1}.Add{2} (this.{0});\n\n",
+                                                      control_id, ctrl_name, parsed);
+               } else if (child_kind == ChildrenKind.HTMLROW || child_kind == ChildrenKind.HTMLCELL) {
+                       old_function.Append ("\n\t\t}\n\n");
+                       init_funcs.Append (old_function);
+                       old_function = (StringBuilder) functions.Pop ();
+                       current_function = (StringBuilder) functions.Peek ();
+                       old_function.AppendFormat ("\n\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n\t\t\t" +
+                                                  "return __ctrl;\n\t\t}}\n\n",
+                                                  control_id, controls.PeekDefaultPropertyName ());
+
+                       controls.Pop ();
+                       control_id = controls.PeekControlID ();
+                       current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n", control_id);
+                       if (child_kind == ChildrenKind.HTMLROW) {
+                               current_function.AppendFormat ("\t\t\t__parser.AddParsedSubObject ({0});\n",
+                                                               control_id);
+                       } else {
+                               current_function.AppendFormat ("\t\t\t__ctrl.Add (this.{0});\n", control_id);
+                       }
+               } else {
                        old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
                        current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
                                                       "AddParsedSubObject (this.{0});\n\n", control_id);
@@ -1077,6 +1395,33 @@ class AspGenerator
                return true;
        }
 
+       private void NewTableElementFunction (HtmlControlTag ctrl)
+       {
+               string control_id = Tag.GetDefaultID ();
+               ChildrenKind child_kind;
+
+               Type t;
+               if (ctrl.ControlType == typeof (HtmlTable)) {
+                       t = typeof (HtmlTableRowCollection);
+                       child_kind = ChildrenKind.HTMLROW;
+               } else {
+                       t = typeof (HtmlTableCellCollection);
+                       child_kind = ChildrenKind.HTMLCELL;
+               }
+
+               controls.Push (ctrl.ControlType,
+                              control_id,
+                              ctrl.TagID,
+                              child_kind,
+                              ctrl.ParseChildren);
+
+
+               current_function = new StringBuilder ();
+               functions.Push (current_function);
+               current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ({1} __ctrl)\n" +
+                                               "\t\t{{\n", control_id, t);
+       }
+
        private void ProcessHtmlControlTag ()
        {
                HtmlControlTag html_ctrl = (HtmlControlTag) elements.Current;
@@ -1098,24 +1443,32 @@ class AspGenerator
                        if (elements.Current is CloseTag)
                                elements.MoveNext ();
                        return;
+               } else if (IsApplication) {
+                       throw new ApplicationException (app_file_wrong);
                }
                
                Type controlType = html_ctrl.ControlType;
-               declarations.AppendFormat ("\t\tprotected {0} {1};\n", controlType, html_ctrl.ControlID);
+               AddProtectedField (controlType, html_ctrl.ControlID);
 
                ChildrenKind children_kind;
-               if (0 != String.Compare (html_ctrl.TagID, "select", true))
+               if (0 == String.Compare (html_ctrl.TagID, "table", true))
+                       children_kind = ChildrenKind.HTMLROW;
+               else if (0 == String.Compare (html_ctrl.TagID, "tr", true))
+                       children_kind = ChildrenKind.HTMLCELL;
+               else if (0 != String.Compare (html_ctrl.TagID, "select", true))
                        children_kind = html_ctrl.IsContainer ? ChildrenKind.CONTROLS :
                                                                ChildrenKind.NONE;
                else
                        children_kind = ChildrenKind.OPTION;
 
-               NewControlFunction (html_ctrl.TagID, html_ctrl.ControlID, controlType, children_kind, null); 
+               NewControlFunction (html_ctrl.TagID, html_ctrl.ControlID, controlType, children_kind, html_ctrl.ParseChildren); 
 
                current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", html_ctrl.ControlID);
-
                AddCodeForAttributes (html_ctrl.ControlType, html_ctrl.Attributes);
 
+               if (children_kind == ChildrenKind.HTMLROW || children_kind == ChildrenKind.HTMLCELL)
+                       NewTableElementFunction (html_ctrl);
+
                if (!html_ctrl.SelfClosing)
                        JustDoIt ();
                else
@@ -1144,15 +1497,17 @@ class AspGenerator
        {
                AspComponent component = (AspComponent) elements.Current;
                Type component_type = component.ComponentType;
-               declarations.AppendFormat ("\t\tprotected {0} {1};\n", component_type, component.ControlID);
+               AddProtectedField (component_type, component.ControlID);
 
                NewControlFunction (component.TagID, component.ControlID, component_type,
                                    component.ChildrenKind, component.DefaultPropertyName); 
 
-               if (component_type.IsSubclassOf (typeof (System.Web.UI.UserControl)))
+               if (component_type == typeof (UserControl) ||
+                   component_type.IsSubclassOf (typeof (System.Web.UI.UserControl)))
                        current_function.Append ("\t\t\t__ctrl.InitializeAsUserControl (Page);\n");
 
-               if (component_type.IsSubclassOf (typeof (System.Web.UI.Control)))
+               if (component_type == typeof (Control) ||
+                   component_type.IsSubclassOf (typeof (System.Web.UI.Control)))
                        current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", component.ControlID);
 
                AddCodeForAttributes (component.ComponentType, component.Attributes);
@@ -1197,7 +1552,7 @@ class AspGenerator
 
                        bool is_processed = false;
                        foreach (PropertyInfo subprop in subprop_info){
-                               is_processed = ProcessProperties (subprop, id, att);
+                               is_processed = ProcessPropertiesAndFields (subprop, id, att);
                                if (is_processed){
                                        subprop_name = subprop.Name;
                                        break;
@@ -1277,7 +1632,7 @@ class AspGenerator
                current_function = new StringBuilder ();
                functions.Push (current_function);
                current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
-                                               "(System.Web.UI.WebControl.DataGridColumnCollection __ctrl)\n" +
+                                               "(System.Web.UI.WebControls.DataGridColumnCollection __ctrl)\n" +
                                                "\t\t{{\n", prop_id);
        }
 
@@ -1310,17 +1665,46 @@ class AspGenerator
 
                        string default_id = Tag.GetDefaultID ();
                        Type type = typeof (System.Web.UI.WebControls.ListItem);
-                       declarations.AppendFormat ("\t\tprotected {0} {1};\n", type, default_id);
+                       AddProtectedField (type, default_id);
                        NewControlFunction (tag.TagID, default_id, type, ChildrenKind.CONTROLS, null); 
                        return;
                }
 
-               if (child_kind == ChildrenKind.CONTROLS){
-                       elements.Current = new PlainText (((Tag) elements.Current).PlainHtml);
-                       ProcessPlainText ();
+               if (child_kind == ChildrenKind.CONTROLS) {
+                       ArrayList tag_elements = tag.GetElements ();
+                       foreach (Element e in tag_elements) {
+                               if (e is PlainText) {
+                                       elements.Current = e;
+                                       ProcessPlainText ();
+                               } else if (e is CodeRenderTag) {
+                                       elements.Current = e;
+                                       ProcessCodeRenderTag ();
+                               } else if (e is DataBindingTag) {
+                                       elements.Current = e;
+                                       ProcessDataBindingLiteral ();
+                               } else {
+                                       throw new ApplicationException (fullPath + ": unexpected tag type " + e.GetType ());
+                               }
+                       }
                        return;
                }
 
+               if (child_kind == ChildrenKind.HTMLROW) {
+                       if (0 == String.Compare (tag.TagID, "tr", true)) {
+                               elements.Current = new HtmlControlTag (tag);
+                               ProcessHtmlControlTag ();
+                               return;
+                       }
+               }
+
+               if (child_kind == ChildrenKind.HTMLCELL) {
+                       if (0 == String.Compare (tag.TagID, "td", true)) {
+                               elements.Current = new HtmlControlTag (tag);
+                               ProcessHtmlControlTag ();
+                               return;
+                       }
+               }
+
                // Now child_kind should be PROPERTIES, so only allow tag_id == property
                Type control_type = controls.PeekType ();
                PropertyInfo [] prop_info = control_type.GetProperties ();
@@ -1343,7 +1727,18 @@ class AspGenerator
        private Tag Map (Tag tag)
        {
                int pos = tag.TagID.IndexOf (":");
-               if (tag is CloseTag || 
+               if (pos == -1) {
+                       ChildrenKind child_kind = controls.PeekChildKind ();
+                       if (child_kind == ChildrenKind.HTMLROW && 0 == String.Compare (tag.TagID, "tr", true)) {
+                               tag.Attributes.Add ("runat", "server");
+                               return new HtmlControlTag (tag);
+                       } else if (child_kind == ChildrenKind.HTMLROW && 0 == String.Compare (tag.TagID, "tr", true)) {
+                               tag.Attributes.Add ("runat", "server");
+                               return new HtmlControlTag (tag);
+                       }
+               }
+
+               if (tag is CloseTag ||
                    ((tag.Attributes == null || 
                    !tag.Attributes.IsRunAtServer ()) && pos == -1))
                        return tag;
@@ -1351,6 +1746,7 @@ class AspGenerator
                if (pos == -1){
                        if (0 == String.Compare (tag.TagID, "object", true))
                                return new ServerObjectTag (tag);
+
                        return new HtmlControlTag (tag);
                }
 
@@ -1392,7 +1788,7 @@ class AspGenerator
                StringBuilder db_function = new StringBuilder ();
                string control_id = Tag.GetDefaultID ();
                string control_type_string = "System.Web.UI.DataBoundLiteralControl";
-               declarations.AppendFormat ("\t\tprotected {0} {1};\n", control_type_string, control_id);
+               AddProtectedField (typeof (System.Web.UI.DataBoundLiteralControl), control_id);
                // Build the control
                db_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_{0} ()\n" +
                                          "\t\t{{\n\t\t\t{1} __ctrl;\n\n" +
@@ -1419,6 +1815,8 @@ class AspGenerator
                init_funcs.Append (db_function);
                current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
                                               "AddParsedSubObject (this.{0});\n\n", control_id);
+
+               AddCodeRenderControl (controls.CodeRenderFunction);
        }
 
        private void ProcessCodeRenderTag ()
@@ -1451,19 +1849,32 @@ class AspGenerator
                        } else if (element is PlainText){
                                ProcessPlainText ();
                        } else if (element is DataBindingTag){
+                               if (IsApplication)
+                                       throw new ApplicationException (app_file_wrong);
                                ProcessDataBindingLiteral ();
                        } else if (element is CodeRenderTag){
+                               if (IsApplication)
+                                       throw new ApplicationException (app_file_wrong);
                                ProcessCodeRenderTag ();
                        } else {
                                elements.Current = Map ((Tag) element);
-                               if (elements.Current is HtmlControlTag)
+                               if (elements.Current is ServerObjectTag) {
+                                       ProcessServerObjectTag ();
+                                       continue;
+                               }
+
+                               if (elements.Current is HtmlControlTag) {
                                        ProcessHtmlControlTag ();
+                                       continue;
+                               }
+
+                               if (IsApplication)
+                                       throw new ApplicationException (app_file_wrong);
+
                                else if (elements.Current is AspComponent)
                                        ProcessComponent ();
                                else if (elements.Current is CloseTag)
                                        ProcessCloseTag ();
-                               else if (elements.Current is ServerObjectTag)
-                                       ProcessServerObjectTag ();
                                else if (elements.Current is Tag)
                                        ProcessHtmlTag ();
                                else
@@ -1472,21 +1883,45 @@ class AspGenerator
                }
        }
 
+       private string GetTemplateDirectory ()
+       {
+               string templatePath = Path.GetDirectoryName (fullPath);
+               string appPath = Path.GetDirectoryName (HttpRuntime.AppDomainAppPath);
+
+               if (templatePath == appPath)
+                       return "/";
+
+               templatePath = templatePath.Substring (appPath.Length);
+               if (Path.DirectorySeparatorChar != '/')
+                       templatePath = templatePath.Replace (Path.DirectorySeparatorChar, '/');
+                       
+               return templatePath;
+       }
+
        private void End ()
        {
-               buildOptions.AppendFormat ("//<class name=\"{0}\"/>\n", className);
-               buildOptions.Append ("\n");
+               if (isPage) {
+                       if (sessionState == SessionState.Enabled || sessionState == SessionState.ReadOnly)
+                               AddInterface (typeof (System.Web.SessionState.IRequiresSessionState));
+
+                       if (sessionState == SessionState.ReadOnly)
+                               AddInterface (typeof (System.Web.SessionState.IReadOnlySessionState));
+               }
+               
                classDecl = "\tpublic class " + className + " : " + parent + interfaces + " {\n"; 
                prolog.Append ("\n" + classDecl);
                declarations.Append ("\t\tprivate static bool __intialized = false;\n\n");
-               if (!IsUserControl)
+               if (IsPage)
                        declarations.Append ("\t\tprivate static ArrayList __fileDependencies;\n\n");
 
                // adds the constructor
-               constructor.AppendFormat ("\t\tpublic {0} ()\n\t\t{{\n" + 
-                                       "\t\t\tSystem.Collections.ArrayList dependencies;\n\n" +
-                                       "\t\t\tif (ASP.{0}.__intialized == false){{\n", className); 
-               if (!IsUserControl) {
+               constructor.AppendFormat ("\t\tpublic {0} ()\n\t\t{{\n", className);
+               if (!IsApplication)
+                       constructor.Append ("\t\t\tSystem.Collections.ArrayList dependencies;\n\n");
+                       
+               constructor.AppendFormat ("\t\t\tif (ASP.{0}.__intialized == false){{\n", className); 
+
+               if (IsPage) {
                        constructor.AppendFormat ("\t\t\t\tdependencies = new System.Collections.ArrayList ();\n" +
                                                "\t\t\t\tdependencies.Add (@\"{1}\");\n" +
                                                "\t\t\t\tASP.{0}.__fileDependencies = dependencies;\n",
@@ -1496,35 +1931,36 @@ class AspGenerator
                constructor.AppendFormat ("\t\t\t\tASP.{0}.__intialized = true;\n\t\t\t}}\n\t\t}}\n\n",
                                          className);
          
-               //FIXME: add AutoHandlers: don't know what for...yet!
-               constructor.AppendFormat (
-                       "\t\tprotected override int AutoHandlers\n\t\t{{\n" +
-                       "\t\t\tget {{ return ASP.{0}.__autoHandlers; }}\n" +
-                       "\t\t\tset {{ ASP.{0}.__autoHandlers = value; }}\n" +
-                       "\t\t}}\n\n", className);
-
-               //FIXME: add ApplicationInstance: don't know what for...yet!
-               constructor.Append (
-                       "\t\tprotected System.Web.HttpApplication ApplicationInstance\n\t\t{\n" +
-                       "\t\t\tget { return (System.Web.HttpApplication) this.Context.ApplicationInstance; }\n" +
-                       "\t\t}\n\n");
-               //FIXME: add TemplateSourceDirectory: don't know what for...yet!
-               //FIXME: it should be the path from the root where the file resides
-               constructor.Append (
-                       "\t\tpublic override string TemplateSourceDirectory\n\t\t{\n" +
-                       "\t\t\tget { return \"/dummypath\"; }\n" +
-                       "\t\t}\n\n");
-
-               epilog.Append ("\n\t\tprotected override void FrameworkInitialize ()\n\t\t{\n" +
-                               "\t\t\tthis.__BuildControlTree (this);\n");
-
-               if (!IsUserControl) {
-                       epilog.AppendFormat ("\t\t\tResponse.AddFileDependencies (ASP.{0}.__fileDependencies);\n" +
-                                               "\t\t\tthis.EnableViewStateMac = true;\n", className);
-               }
-               epilog.Append ("\t\t}\n\n");
-
-               if (!IsUserControl) {
+               if (!IsApplication) {
+                       //FIXME: add AutoHandlers: don't know what for...yet!
+                       constructor.AppendFormat (
+                               "\t\tprotected override int AutoHandlers\n\t\t{{\n" +
+                               "\t\t\tget {{ return ASP.{0}.__autoHandlers; }}\n" +
+                               "\t\t\tset {{ ASP.{0}.__autoHandlers = value; }}\n" +
+                               "\t\t}}\n\n", className);
+
+                       constructor.Append (
+                               "\t\tprotected System.Web.HttpApplication ApplicationInstance\n\t\t{\n" +
+                               "\t\t\tget { return (System.Web.HttpApplication) this.Context.ApplicationInstance; }\n" +
+                               "\t\t}\n\n");
+
+                       constructor.AppendFormat (
+                               "\t\tpublic override string TemplateSourceDirectory\n\t\t{{\n" +
+                               "\t\t\tget {{ return \"{0}\"; }}\n" +
+                               "\t\t}}\n\n", GetTemplateDirectory ());
+
+                       epilog.Append ("\n\t\tprotected override void FrameworkInitialize ()\n\t\t{\n" +
+                                       "\t\t\tthis.__BuildControlTree (this);\n");
+
+                       if (IsPage) {
+                               epilog.AppendFormat ("\t\t\tthis.FileDependencies = ASP.{0}.__fileDependencies;\n" +
+                                                       "\t\t\tthis.EnableViewStateMac = true;\n", className);
+                       }
+
+                       epilog.Append ("\t\t}\n\n");
+               }
+
+               if (IsPage) {
                        Random rnd = new Random ();
                        epilog.AppendFormat ("\t\tpublic override int GetTypeHashCode ()\n\t\t{{\n" +
                                             "\t\t\treturn {0};\n" +
@@ -1573,120 +2009,23 @@ class AspGenerator
                public string assemblyName;
        }
 
-       private static UserControlData GenerateUserControl (string src)
+       private static UserControlData GenerateUserControl (string src, HttpContext context)
        {
                UserControlData data = new UserControlData ();
                data.result = UserControlResult.OK;
 
-               if (!File.Exists (src)) {
-                       data.result = UserControlResult.FileNotFound;
-                       return data;
-               }
-
-               string noExt = Path.GetFileNameWithoutExtension (src);
-               string csName = "output" + dirSeparator + "xsp_ctrl_" + noExt + ".cs";
-               if (!Directory.Exists ("output"))
-                       Directory.CreateDirectory ("output");
-
-               if (Xsp (src, csName) == false) {
-                       data.result = UserControlResult.XspFailed;
+               UserControlCompiler compiler = new UserControlCompiler (new UserControlParser (src, context));
+               Type t = compiler.GetCompiledType ();
+               if (t == null) {
+                       data.result = UserControlResult.CompilationFailed;
                        return data;
                }
                
-               StreamReader fileReader = new StreamReader (File.Open (csName, FileMode.Open));
-               data.className = src.Replace ('.', '_');
-               
-               StringBuilder compilerOptions = new StringBuilder ("/r:System.Web.dll /r:System.Drawing.dll ");
-               compilerOptions.Append ("/target:library ");
-
-               string line;
-               while ((line = fileReader.ReadLine ()) != null && line != "") {
-                       if (line.StartsWith ("//<class ")) {
-                               data.className = GetAttributeValue (line, "name");
-                       } else if (line.StartsWith ("//<reference ")) {
-                               string dllName = GetAttributeValue (line, "dll");
-                               compilerOptions.AppendFormat ("/r:{0} ", dllName);
-                       } else if (line.StartsWith ("//<compileroptions ")) {
-                               string options = GetAttributeValue (line, "options");
-                               compilerOptions.Append (" " + options + " ");
-                       } else {
-                               Console.Error.WriteLine ("Ignoring build option: {0}", line);
-                       }
-               }
-               fileReader.Close ();
-
-               string dll = Path.ChangeExtension (csName, ".dll");
-               data.assemblyName = dll;
-               if (Compile (csName, dll, compilerOptions) == false) {
-                       data.result = UserControlResult.CompilationFailed;
-               }
+               data.className = t.Name;
+               data.assemblyName = compiler.TargetFile;
                
                return data;
        }
-
-       private static string GetAttributeValue (string line, string att)
-       {
-               string att_start = att + "=\"";
-               int begin = line.IndexOf (att_start);
-               int end = line.Substring (begin + att_start.Length).IndexOf ('"');
-               if (begin == -1 || end == -1)
-                       throw new ApplicationException ("Error in compilation option:\n" + line);
-
-               return line.Substring (begin + att_start.Length, end);
-       }
-               
-       private static bool Xsp (string fileName, string csFileName)
-       {
-#if MONO
-               return RunProcess ("mono", 
-                                  "xsp.exe --control " + fileName, 
-                                  csFileName,
-                                  "output" + dirSeparator + "xsp_ctrl_" + Path.GetFileName (fileName) + 
-                                  ".sh");
-#else
-               return RunProcess ("xsp", 
-                                  "--control " + fileName, 
-                                  csFileName,
-                                  "output" + dirSeparator + "xsp_ctrl_" + fileName + ".bat");
-#endif
-       }
-
-       private static bool Compile (string csName, string dllName, StringBuilder compilerOptions)
-       {
-               compilerOptions.AppendFormat ("/out:{0} ", dllName);
-               compilerOptions.Append (csName + " ");
-
-               string cmdline = compilerOptions.ToString ();
-               string noext = Path.GetFileNameWithoutExtension (csName);
-               string output_file = "output" + dirSeparator + "output_from_compilation_" + noext + ".txt";
-               string bat_file = "output" + dirSeparator + "last_compilation_" + noext + ".bat";
-               return RunProcess ("mcs", cmdline, output_file, bat_file);
-       }
-
-       private static bool RunProcess (string exe, string arguments, string output_file, string script_file)
-       {
-               Process proc = new Process ();
-
-               proc.StartInfo.FileName = exe;
-               proc.StartInfo.Arguments = arguments;
-               proc.StartInfo.UseShellExecute = false;
-               proc.StartInfo.RedirectStandardOutput = true;
-               proc.Start ();
-               string poutput = proc.StandardOutput.ReadToEnd();
-               proc.WaitForExit ();
-               int result = proc.ExitCode;
-               proc.Close ();
-
-               StreamWriter cmd_output = new StreamWriter (File.Create (output_file));
-               cmd_output.Write (poutput);
-               cmd_output.Close ();
-               StreamWriter bat_output = new StreamWriter (File.Create (script_file));
-               bat_output.Write (exe + " " + arguments);
-               bat_output.Close ();
-
-               return (result == 0);
-       }
-
 }
 
 }