New tests.
[mono.git] / mcs / class / System.Web / System.Web.Compilation / TemplateControlCompiler.cs
index 59a99dcaea93b639dbcc1ebf24d8ffd4a3ecfbff..4b01ccd67853f2ae263f23f390a1cc351188cde5 100644 (file)
@@ -3,9 +3,10 @@
 //
 // Authors:
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//     Marek Habersack (mhabersack@novell.com)
 //
 // (C) 2003 Ximian, Inc (http://www.ximian.com)
-//
+// (C) 2004-2008 Novell, Inc (http://novell.com)
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
@@ -31,22 +32,21 @@ using System;
 using System.CodeDom;
 using System.Collections;
 using System.ComponentModel;
+using System.Configuration;
+using System.Collections.Specialized;
+using System.Collections.Generic;
 using System.Drawing;
 using System.Globalization;
 using System.Reflection;
+using System.Resources;
 using System.Text;
 using System.Web;
+using System.Web.Configuration;
 using System.Web.UI;
 using System.Web.UI.WebControls;
 using System.Web.Util;
 using System.ComponentModel.Design.Serialization;
-#if NET_2_0
-using System.Configuration;
-using System.Collections.Specialized;
-using System.Collections.Generic;
 using System.Text.RegularExpressions;
-using System.Web.Configuration;
-#endif
 
 namespace System.Web.Compilation
 {
@@ -54,7 +54,8 @@ namespace System.Web.Compilation
        {
                static BindingFlags noCaseFlags = BindingFlags.Public | BindingFlags.NonPublic |
                                                  BindingFlags.Instance | BindingFlags.IgnoreCase;
-
+               static Type monoTypeType = Type.GetType ("System.MonoType");
+               
                TemplateControlParser parser;
                int dataBoundAtts;
                internal ILocation currentLocation;
@@ -63,10 +64,21 @@ namespace System.Web.Compilation
 
                internal static CodeVariableReferenceExpression ctrlVar = new CodeVariableReferenceExpression ("__ctrl");
                
-#if NET_2_0
-               static Regex bindRegex = new Regex (@"Bind\s*\(""(.*?)""\)\s*%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
-               static Regex bindRegexInValue = new Regex (@"Bind\s*\(""(.*?)""\)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
-#endif
+               List <string> masterPageContentPlaceHolders;
+               static Regex startsWithBindRegex = new Regex (@"^Bind\s*\(", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+               // When modifying those, make sure to look at the SanitizeBindCall to make sure it
+               // picks up correct groups.
+               static Regex bindRegex = new Regex (@"Bind\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)\s*%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+               static Regex bindRegexInValue = new Regex (@"Bind\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+               static Regex evalRegexInValue = new Regex (@"(.*)Eval\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)(.*)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+               List <string> MasterPageContentPlaceHolders {
+                       get {
+                               if (masterPageContentPlaceHolders == null)
+                                       masterPageContentPlaceHolders = new List <string> ();
+                               return masterPageContentPlaceHolders;
+                       }
+               }
 
                public TemplateControlCompiler (TemplateControlParser parser)
                        : base (parser)
@@ -84,26 +96,23 @@ namespace System.Web.Compilation
                {
                        if (builder == null || builder.ID == null || builder.ControlType == null)
                                return;
-#if NET_2_0
+
                        if (partialNameOverride [builder.ID] != null)
                                return;
-#endif
 
                        MemberAttributes ma = MemberAttributes.Family;
-                       currentLocation = builder.location;
+                       currentLocation = builder.Location;
                        if (check && CheckBaseFieldOrProperty (builder.ID, builder.ControlType, ref ma))
                                return; // The field or property already exists in a base class and is accesible.
 
                        CodeMemberField field;
                        field = new CodeMemberField (builder.ControlType.FullName, builder.ID);
                        field.Attributes = ma;
-#if NET_2_0
                        field.Type.Options |= CodeTypeReferenceOptions.GlobalReference;
 
                        if (partialClass != null)
                                partialClass.Members.Add (AddLinePragma (field, builder));
                        else
-#endif
                                mainClass.Members.Add (AddLinePragma (field, builder));
                }
 
@@ -127,15 +136,8 @@ namespace System.Web.Compilation
                                return false;
 
                        if (!other.IsAssignableFrom (type)) {
-#if NET_2_0
                                ma |= MemberAttributes.New;
                                return false;
-#else
-                               string msg = String.Format ("The base class includes the field '{0}', but its " +
-                                                           "type '{1}' is not compatible with {2}",
-                                                           id, other, type);
-                               throw new ParseException (currentLocation, msg);
-#endif
                        }
 
                        return true;
@@ -143,29 +145,29 @@ namespace System.Web.Compilation
                
                void AddParsedSubObjectStmt (ControlBuilder builder, CodeExpression expr) 
                {
-                       if (!builder.haveParserVariable) {
+                       if (!builder.HaveParserVariable) {
                                CodeVariableDeclarationStatement p = new CodeVariableDeclarationStatement();
                                p.Name = "__parser";
                                p.Type = new CodeTypeReference (typeof (IParserAccessor));
                                p.InitExpression = new CodeCastExpression (typeof (IParserAccessor), ctrlVar);
-                               builder.methodStatements.Add (p);
-                               builder.haveParserVariable = true;
+                               builder.MethodStatements.Add (p);
+                               builder.HaveParserVariable = true;
                        }
 
                        CodeVariableReferenceExpression var = new CodeVariableReferenceExpression ("__parser");
                        CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (var, "AddParsedSubObject");
                        invoke.Parameters.Add (expr);
-                       builder.methodStatements.Add (AddLinePragma (invoke, builder));
+                       builder.MethodStatements.Add (AddLinePragma (invoke, builder));
                }
                
                void InitMethod (ControlBuilder builder, bool isTemplate, bool childrenAsProperties)
                {
-                       currentLocation = builder.location;
+                       currentLocation = builder.Location;
                        
                        string tailname = ((builder is RootBuilder) ? "Tree" : ("_" + builder.ID));
                        CodeMemberMethod method = new CodeMemberMethod ();
-                       builder.method = method;
-                       builder.methodStatements = method.Statements;
+                       builder.Method = method;
+                       builder.MethodStatements = method.Statements;
 
                        method.Name = "__BuildControl" + tailname;
                        method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
@@ -175,15 +177,13 @@ namespace System.Web.Compilation
                         * method, allow subclasses to insert control
                         * specific code. */
                        if (builder is RootBuilder) {
-#if NET_2_0
                                SetCustomAttributes (method);
-#endif
                                AddStatementsToInitMethod (method);
                        }
                        
                        if (builder.HasAspCode) {
                                CodeMemberMethod renderMethod = new CodeMemberMethod ();
-                               builder.renderMethod = renderMethod;
+                               builder.RenderMethod = renderMethod;
                                renderMethod.Name = "__Render" + tailname;
                                renderMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
                                CodeParameterDeclarationExpression arg1 = new CodeParameterDeclarationExpression ();
@@ -202,11 +202,12 @@ namespace System.Web.Compilation
                                if (builder is RootBuilder)
                                        typeString = parser.ClassName;
                                else {
-                                       if (builder.ControlType != null && builder.isProperty &&
+                                       if (builder.ControlType != null && builder.IsProperty &&
                                            !typeof (ITemplate).IsAssignableFrom (builder.ControlType))
                                                typeString = builder.ControlType.FullName;
                                        else 
                                                typeString = "System.Web.UI.Control";
+                                       ProcessTemplateChildren (builder);
                                }
 
                                method.Parameters.Add (new CodeParameterDeclarationExpression (typeString, "__ctrl"));
@@ -254,7 +255,6 @@ namespace System.Web.Compilation
                                        method.Statements.Add (initAsControl);
                                }
 
-#if NET_2_0
                                if (builder.ParentTemplateBuilder is System.Web.UI.WebControls.ContentBuilderInternal) {
                                        PropertyInfo pi;
 
@@ -283,11 +283,10 @@ namespace System.Web.Compilation
                                // CreateAssignStatementsFromAttributes
                                // below.
                                // 
-                               if (builder.attribs != null) {
-                                       string skinid = builder.attribs ["skinid"] as string;
-                                       if (skinid != null)
-                                               CreateAssignStatementFromAttribute (builder, "skinid");
-                               }
+                               string skinid = builder.GetAttribute ("skinid");
+                               if (!String.IsNullOrEmpty (skinid))
+                                       CreateAssignStatementFromAttribute (builder, "skinid");
+
                                if (typeof (WebControl).IsAssignableFrom (type)) {
                                        CodeMethodInvokeExpression applyStyleSheetSkin = new CodeMethodInvokeExpression (ctrlVar, "ApplyStyleSheetSkin");
                                        if (typeof (Page).IsAssignableFrom (parser.BaseType))
@@ -296,35 +295,38 @@ namespace System.Web.Compilation
                                                applyStyleSheetSkin.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
                                        method.Statements.Add (applyStyleSheetSkin);
                                }
-#endif
+
+                               // Process template children before anything else
+                               ProcessTemplateChildren (builder);
 
                                // process ID here. It should be set before any other attributes are
                                // assigned, since the control code may rely on ID being set. We
                                // skip ID in CreateAssignStatementsFromAttributes
-                               if (builder.attribs != null) {
-                                       string ctl_id = builder.attribs ["id"] as string;
-                                       if (ctl_id != null && ctl_id != String.Empty)
-                                               CreateAssignStatementFromAttribute (builder, "id");
-                               }
-#if NET_2_0
+                               string ctl_id = builder.GetAttribute ("id");
+                               if (ctl_id != null && ctl_id.Length != 0)
+                                       CreateAssignStatementFromAttribute (builder, "id");
+                               
                                if (typeof (ContentPlaceHolder).IsAssignableFrom (type)) {
-                                       CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders");
-                                       CodeMethodInvokeExpression addPlaceholder = new CodeMethodInvokeExpression (prop, "Add");
-                                       addPlaceholder.Parameters.Add (ctrlVar);
-                                       method.Statements.Add (addPlaceholder);
-
+                                       List <string> placeHolderIds = MasterPageContentPlaceHolders;
+                                       string cphID = builder.ID;
+                                       
+                                       if (!placeHolderIds.Contains (cphID))
+                                               placeHolderIds.Add (cphID);
 
                                        CodeConditionStatement condStatement;
 
                                        // Add the __Template_* field
-                                       CodeMemberField fld = new CodeMemberField (typeof (ITemplate), "__Template_" + builder.ID);
+                                       string templateField = "__Template_" + cphID;
+                                       CodeMemberField fld = new CodeMemberField (typeof (ITemplate), templateField);
                                        fld.Attributes = MemberAttributes.Private;
                                        mainClass.Members.Add (fld);
 
                                        CodeFieldReferenceExpression templateID = new CodeFieldReferenceExpression ();
                                        templateID.TargetObject = thisRef;
-                                       templateID.FieldName = "__Template_" + builder.ID;
+                                       templateID.FieldName = templateField;
 
+                                       CreateContentPlaceHolderTemplateProperty (templateField, "Template_" + cphID);
+                                       
                                        // if ((this.ContentTemplates != null)) {
                                        //      this.__Template_$builder.ID = ((System.Web.UI.ITemplate)(this.ContentTemplates["$builder.ID"]));
                                        // }
@@ -335,7 +337,7 @@ namespace System.Web.Compilation
 
                                        CodeIndexerExpression indexer = new CodeIndexerExpression ();
                                        indexer.TargetObject = new CodePropertyReferenceExpression (thisRef, "ContentTemplates");
-                                       indexer.Indices.Add (new CodePrimitiveExpression (builder.ID));
+                                       indexer.Indices.Add (new CodePrimitiveExpression (cphID));
 
                                        assign = new CodeAssignStatement ();
                                        assign.Left = templateID;
@@ -370,15 +372,28 @@ namespace System.Web.Compilation
                                        method.Statements.Add (condStatement);
 
                                        // this is the bit that causes the following stuff to end up in the else { }
-                                       builder.methodStatements = condStatement.FalseStatements;
+                                       builder.MethodStatements = condStatement.FalseStatements;
                                }
-#endif
                        }
                        
                        mainClass.Members.Add (method);
                }
 
-#if NET_2_0
+               void ProcessTemplateChildren (ControlBuilder builder)
+               {
+                       ArrayList templates = builder.TemplateChildren;
+                       if (templates != null && templates.Count > 0) {
+                               foreach (TemplateBuilder tb in templates) {
+                                       CreateControlTree (tb, true, false);
+                                       if (tb.BindingDirection == BindingDirection.TwoWay) {
+                                               string extractMethod = CreateExtractValuesMethod (tb);
+                                               AddBindableTemplateInvocation (builder, tb.TagName, tb.Method.Name, extractMethod);
+                                       } else
+                                               AddTemplateInvocation (builder, tb.TagName, tb.Method.Name);
+                               }
+                       }
+               }
+               
                void SetCustomAttribute (CodeMemberMethod method, UnknownAttributeDescriptor uad)
                {
                        CodeAssignStatement assign = new CodeAssignStatement ();
@@ -403,7 +418,6 @@ namespace System.Web.Compilation
                        foreach (UnknownAttributeDescriptor uad in attrs)
                                SetCustomAttribute (method, uad);
                }
-#endif
                
                protected virtual void AddStatementsToInitMethod (CodeMemberMethod method)
                {
@@ -422,35 +436,77 @@ namespace System.Web.Compilation
 
                                CodeMethodInvokeExpression expr;
                                expr = new CodeMethodInvokeExpression (methodRef, new CodePrimitiveExpression (str));
-                               builder.renderMethod.Statements.Add (AddLinePragma (expr, builder));
+                               builder.RenderMethod.Statements.Add (expr);
                        }
                }
 
-               string TrimDB (string value)
+               string TrimDB (string value, bool trimTail)
                {
                        string str = value.Trim ();
-                       str = str.Substring (3);
-                       return str.Substring (0, str.Length - 2);
+                       int len = str.Length;
+                       int idx = str.IndexOf ('#', 2) + 1;
+                       if (idx >= len)
+                               return String.Empty;
+                       if (trimTail)
+                               len -= 2;
+                       
+                       return str.Substring (idx, len - idx).Trim ();
+               }
+
+               CodeExpression CreateEvalInvokeExpression (Regex regex, string value, bool isBind)
+               {
+                       Match match = regex.Match (value);
+                       if (!match.Success) {
+                               if (isBind)
+                                       throw new HttpParseException ("Bind invocation wasn't formatted properly.");
+                               return null;
+                       }
+                       
+                       string sanitizedSnippet;
+                       if (isBind)
+                               sanitizedSnippet = SanitizeBindCall (match);
+                       else
+                               sanitizedSnippet = value;
+                       
+                       return new CodeSnippetExpression (sanitizedSnippet);
                }
 
+               string SanitizeBindCall (Match match)
+               {
+                       GroupCollection groups = match.Groups;
+                       StringBuilder sb = new StringBuilder ("Eval(\"" + groups [1] + "\"");
+                       Group second = groups [4];
+                       if (second != null) {
+                               string v = second.Value;
+                               if (v != null && v.Length > 0)
+                                       sb.Append (",\"" + second + "\"");
+                       }
+                       
+                       sb.Append (")");
+                       return sb.ToString ();
+               }
+               
                string DataBoundProperty (ControlBuilder builder, Type type, string varName, string value)
                {
-                       value = TrimDB (value);
+                       value = TrimDB (value, true);
                        CodeMemberMethod method;
-                       string dbMethodName = builder.method.Name + "_DB_" + dataBoundAtts++;
-#if NET_2_0
-                       bool need_if = false;
+                       string dbMethodName = builder.Method.Name + "_DB_" + dataBoundAtts++;
+                       CodeExpression valueExpression = null;
                        value = value.Trim ();
-                       if (StrUtils.StartsWith (value, "Bind", true)) {
-                               Match match = bindRegexInValue.Match (value);
-                               if (match.Success) {
-                                       value = "Eval" + value.Substring (4);
+                       
+                       bool need_if = false;
+                       if (startsWithBindRegex.Match (value).Success) {
+                               valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, true);
+                               if (valueExpression != null)
                                        need_if = true;
-                               }
-                       }
-#endif
-                       method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
+                       } else
+                               if (StrUtils.StartsWith (value, "Eval", true))
+                                       valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, false);
+                       
+                       if (valueExpression == null)
+                               valueExpression = new CodeSnippetExpression (value);
                        
+                       method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
                        CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
 
                        // This should be a CodePropertyReferenceExpression for properties... but it works anyway
@@ -461,24 +517,19 @@ namespace System.Web.Compilation
                                CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
                                CodeTypeReferenceExpression conv = new CodeTypeReferenceExpression (typeof (Convert));
                                tostring.Method = new CodeMethodReferenceExpression (conv, "ToString");
-                               tostring.Parameters.Add (new CodeSnippetExpression (value));
+                               tostring.Parameters.Add (valueExpression);
                                expr = tostring;
-                       } else {
-                               CodeSnippetExpression snippet = new CodeSnippetExpression (value);
-                               expr = new CodeCastExpression (type, snippet);
-                       }
+                       } else
+                               expr = new CodeCastExpression (type, valueExpression);
 
                        CodeAssignStatement assign = new CodeAssignStatement (field, expr);
-#if NET_2_0
                        if (need_if) {
                                CodeExpression page = new CodePropertyReferenceExpression (thisRef, "Page");
                                CodeExpression left = new CodeMethodInvokeExpression (page, "GetDataItem");
                                CodeBinaryOperatorExpression ce = new CodeBinaryOperatorExpression (left, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
                                CodeConditionStatement ccs = new CodeConditionStatement (ce, assign);
                                method.Statements.Add (ccs);
-                       }
-                       else
-#endif
+                       } else
                                method.Statements.Add (assign);
 
                        mainClass.Members.Add (method);
@@ -487,79 +538,96 @@ namespace System.Web.Compilation
 
                void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, MemberInfo member, bool isDataBound, bool isExpression)
                {
-                       CodeMemberMethod method = builder.method;
+                       CodeMemberMethod method = builder.Method;
                        bool isWritable = IsWritablePropertyOrField (member);
+                       
                        if (isDataBound && isWritable) {
                                string dbMethodName = DataBoundProperty (builder, type, var_name, att);
                                AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
                                return;
-                       }
-#if NET_2_0
-                       else if (isExpression && isWritable) {
+                       } else if (isExpression && isWritable) {
                                AddExpressionAssign (method, builder, member, type, var_name, att);
                                return;
                        }
-#endif
 
                        CodeAssignStatement assign = new CodeAssignStatement ();
                        assign.Left = new CodePropertyReferenceExpression (ctrlVar, var_name);
-                       currentLocation = builder.location;
+                       currentLocation = builder.Location;
                        assign.Right = GetExpressionFromString (type, att, member);
 
                        method.Statements.Add (AddLinePragma (assign, builder));
                }
 
-               bool IsDataBound (string value)
+               bool IsDirective (string value, char directiveChar)
                {
-                       if (value == null || value == "")
+                       if (value == null || value == String.Empty)
+                               return false;
+                       
+                       value = value.Trim ();
+                       if (!StrUtils.StartsWith (value, "<%") || !StrUtils.EndsWith (value, "%>"))
                                return false;
 
-                       string str = value.Trim ();
-                       return (StrUtils.StartsWith (str, "<%#") && StrUtils.EndsWith (str, "%>"));
+                       int dcIndex = value.IndexOf (directiveChar, 2);
+                       if (dcIndex == -1)
+                               return false;
+
+                       if (dcIndex == 2)
+                               return true;
+                       dcIndex--;
+                       
+                       while (dcIndex >= 2) {
+                               if (!Char.IsWhiteSpace (value [dcIndex]))
+                                       return false;
+                               dcIndex--;
+                       }
+
+                       return true;
+               }
+               
+               bool IsDataBound (string value)
+               {
+                       return IsDirective (value, '#');
                }
 
-#if NET_2_0
                bool IsExpression (string value)
                {
-                       if (value == null || value == "")
-                               return false;
-                       string str = value.Trim ();
-                       return (StrUtils.StartsWith (str, "<%$") && StrUtils.EndsWith (str, "%>"));
+                       return IsDirective (value, '$');
                }               
 
                void RegisterBindingInfo (ControlBuilder builder, string propName, ref string value)
                {
-                       string str = value.Trim ();
-                       str = str.Substring (3).Trim ();        // eats "<%#"
+                       string str = TrimDB (value, false);
                        if (StrUtils.StartsWith (str, "Bind", true)) {
                                Match match = bindRegex.Match (str);
                                if (match.Success) {
                                        string bindingName = match.Groups [1].Value;
-                                       
                                        TemplateBuilder templateBuilder = builder.ParentTemplateBuilder;
-                                       if (templateBuilder == null || templateBuilder.BindingDirection == BindingDirection.OneWay)
+                                       
+                                       if (templateBuilder == null)
                                                throw new HttpException ("Bind expression not allowed in this context.");
-                                               
-                                       string id = builder.attribs ["ID"] as string;
-                                       if (id == null)
+
+                                       if (templateBuilder.BindingDirection == BindingDirection.OneWay)
+                                               return;
+                                       
+                                       string id = builder.GetAttribute ("ID");
+                                       if (String.IsNullOrEmpty (id))
                                                throw new HttpException ("Control of type '" + builder.ControlType + "' using two-way binding on property '" + propName + "' must have an ID.");
                                        
                                        templateBuilder.RegisterBoundProperty (builder.ControlType, propName, id, bindingName);
                                }
                        }
                }
-#endif
 
                /*
                static bool InvariantCompare (string a, string b)
                {
-                       return (0 == String.Compare (a, b, false, CultureInfo.InvariantCulture));
+                       return (0 == String.Compare (a, b, false, Helpers.InvariantCulture));
                }
                */
 
                static bool InvariantCompareNoCase (string a, string b)
                {
-                       return (0 == String.Compare (a, b, true, CultureInfo.InvariantCulture));
+                       return (0 == String.Compare (a, b, true, Helpers.InvariantCulture));
                }
 
                static MemberInfo GetFieldOrProperty (Type type, string name)
@@ -583,7 +651,7 @@ namespace System.Web.Compilation
                {
                        PropertyInfo pi = member as PropertyInfo;
                        if (pi != null)
-                               return pi.CanWrite;
+                               return pi.GetSetMethod (false) != null;
                        FieldInfo fi = member as FieldInfo;
                        if (fi != null)
                                return !fi.IsInitOnly;
@@ -596,11 +664,7 @@ namespace System.Web.Compilation
                        int hyphen = id.IndexOf ('-');
                        bool isPropertyInfo = (member is PropertyInfo);
                        bool isDataBound = IsDataBound (attValue);
-#if NET_2_0
                        bool isExpression = !isDataBound && IsExpression (attValue);
-#else
-                       bool isExpression = false;
-#endif
                        Type type;
                        if (isPropertyInfo) {
                                type = ((PropertyInfo) member).PropertyType;
@@ -609,11 +673,9 @@ namespace System.Web.Compilation
                        }
 
                        if (InvariantCompareNoCase (member.Name, id)) {
-#if NET_2_0
                                if (isDataBound)
-                                       RegisterBindingInfo (builder, member.Name, ref attValue);
-                               
-#endif
+                                       RegisterBindingInfo (builder, member.Name, ref attValue);                               
+
                                if (!IsWritablePropertyOrField (member))
                                        return false;
                                
@@ -624,7 +686,7 @@ namespace System.Web.Compilation
                        if (hyphen == -1)
                                return false;
 
-                       string prop_field = id.Replace ("-", ".");
+                       string prop_field = id.Replace ('-', '.');
                        string [] parts = prop_field.Split (new char [] {'.'});
                        int length = parts.Length;
                        
@@ -656,10 +718,10 @@ namespace System.Web.Compilation
                        string val = attValue;
                        if (attValue == null && is_bool)
                                val = "true"; // Font-Bold <=> Font-Bold="true"
-#if NET_2_0
+
                        if (isDataBound)
                                RegisterBindingInfo (builder, prefix + member.Name + "." + subprop.Name, ref attValue);
-#endif
+
                        AddCodeForPropertyOrField (builder, subprop.PropertyType,
                                                   prefix + member.Name + "." + subprop.Name,
                                                   val, subprop, isDataBound, isExpression);
@@ -667,47 +729,38 @@ namespace System.Web.Compilation
                        return true;
                }
 
-#if NET_2_0
-               void AddExpressionAssign (CodeMemberMethod method, ControlBuilder builder, MemberInfo member, Type type, string name, string value)
+               CodeExpression CompileExpression (MemberInfo member, Type type, string value, bool useSetAttribute)
                {
-                       CodeAssignStatement assign = new CodeAssignStatement ();
-                       assign.Left = new CodePropertyReferenceExpression (ctrlVar, name);
-
                        // First let's find the correct expression builder
                        value = value.Substring (3, value.Length - 5).Trim ();
                        int colon = value.IndexOf (':');
                        if (colon == -1)
-                               return;
+                               return null;
                        string prefix = value.Substring (0, colon).Trim ();
                        string expr = value.Substring (colon + 1).Trim ();
                        
-                       System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration ("");
-                       if (config == null)
-                               return;
-                       CompilationSection cs = (CompilationSection)config.GetSection ("system.web/compilation");
+                       CompilationSection cs = (CompilationSection)WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
                        if (cs == null)
-                               return;
+                               return null;
+                       
                        if (cs.ExpressionBuilders == null || cs.ExpressionBuilders.Count == 0)
-                               return;
+                               return null;
 
                        System.Web.Configuration.ExpressionBuilder ceb = cs.ExpressionBuilders[prefix];
                        if (ceb == null)
-                               return;
+                               return null;
+                       
                        string builderType = ceb.Type;
-
                        Type t;
+                       
                        try {
                                t = HttpApplication.LoadType (builderType, true);
                        } catch (Exception e) {
-                               throw new HttpException (
-                                       String.Format ("Failed to load expression builder type `{0}'", builderType), e);
+                               throw new HttpException (String.Format ("Failed to load expression builder type `{0}'", builderType), e);
                        }
 
                        if (!typeof (System.Web.Compilation.ExpressionBuilder).IsAssignableFrom (t))
-                               throw new HttpException (
-                                       String.Format (
-                                               "Type {0} is not descendant from System.Web.Compilation.ExpressionBuilder",
-                                               builderType));
+                               throw new HttpException (String.Format ("Type {0} is not descendant from System.Web.Compilation.ExpressionBuilder", builderType));
 
                        System.Web.Compilation.ExpressionBuilder eb = null;
                        object parsedData;
@@ -718,31 +771,72 @@ namespace System.Web.Compilation
                                ctx = new ExpressionBuilderContext (HttpContext.Current.Request.FilePath);
                                parsedData = eb.ParseExpression (expr, type, ctx);
                        } catch (Exception e) {
-                               throw new HttpException (
-                                       String.Format ("Failed to create an instance of type `{0}'", builderType), e);
+                               throw new HttpException (String.Format ("Failed to create an instance of type `{0}'", builderType), e);
                        }
                        
-                       BoundPropertyEntry bpe = CreateBoundPropertyEntry (member as PropertyInfo, prefix, expr);
-                       assign.Right = eb.GetCodeExpression (bpe, parsedData, ctx);
+                       BoundPropertyEntry bpe = CreateBoundPropertyEntry (member as PropertyInfo, prefix, expr, useSetAttribute);
+                       return eb.GetCodeExpression (bpe, parsedData, ctx);
+               }
+               
+               void AddExpressionAssign (CodeMemberMethod method, ControlBuilder builder, MemberInfo member, Type type, string name, string value)
+               {
+                       CodeExpression expr = CompileExpression (member, type, value, false);
+
+                       if (expr == null)
+                               return;
                        
-                       builder.method.Statements.Add (AddLinePragma (assign, builder));
+                       CodeAssignStatement assign = new CodeAssignStatement ();
+                       assign.Left = new CodePropertyReferenceExpression (ctrlVar, name);
+                       assign.Right = expr;
+                       
+                       builder.Method.Statements.Add (AddLinePragma (assign, builder));
                }
 
-               BoundPropertyEntry CreateBoundPropertyEntry (PropertyInfo pi, string prefix, string expr)
+               BoundPropertyEntry CreateBoundPropertyEntry (PropertyInfo pi, string prefix, string expr, bool useSetAttribute)
                {
-                       if (pi == null)
-                               return null;
-
                        BoundPropertyEntry ret = new BoundPropertyEntry ();
                        ret.Expression = expr;
                        ret.ExpressionPrefix = prefix;
                        ret.Generated = false;
-                       ret.Name = pi.Name;
-                       ret.PropertyInfo = pi;
-                       ret.Type = pi.PropertyType;
-
+                       if (pi != null) {
+                               ret.Name = pi.Name;
+                               ret.PropertyInfo = pi;
+                               ret.Type = pi.PropertyType;
+                       }
+                       ret.UseSetAttribute = useSetAttribute;
+                       
                        return ret;
                }
+
+               bool ResourceProviderHasObject (string key)
+               {
+                       IResourceProvider rp = HttpContext.GetResourceProvider (InputVirtualPath.Absolute, true);
+                       if (rp == null)
+                               return false;
+
+                       IResourceReader rr = rp.ResourceReader;
+                       if (rr == null)
+                               return false;
+
+                       try {
+                               IDictionaryEnumerator ide = rr.GetEnumerator ();
+                               if (ide == null)
+                                       return false;
+                       
+                               string dictKey;
+                               while (ide.MoveNext ()) {
+                                       dictKey = ide.Key as string;
+                                       if (String.IsNullOrEmpty (dictKey))
+                                               continue;
+                                       if (String.Compare (key, dictKey, StringComparison.Ordinal) == 0)
+                                               return true;
+                               }
+                       } finally {
+                               rr.Close ();
+                       }
+                       
+                       return false;
+               }
                
                void AssignPropertyFromResources (ControlBuilder builder, MemberInfo mi, string attvalue)
                {
@@ -752,9 +846,16 @@ namespace System.Web.Compilation
                        if (!isProperty && !isField || !IsWritablePropertyOrField (mi))
                                return;                 
 
+                       object[] attrs = mi.GetCustomAttributes (typeof (LocalizableAttribute), true);
+                       if (attrs != null && attrs.Length > 0 && !((LocalizableAttribute)attrs [0]).IsLocalizable)
+                               return;
+                       
                        string memberName = mi.Name;
                        string resname = String.Concat (attvalue, ".", memberName);
 
+                       if (!ResourceProviderHasObject (resname))
+                               return;
+                       
                        // __ctrl.Text = System.Convert.ToString(HttpContext.GetLocalResourceObject("ButtonResource1.Text"));
                        string inputFile = parser.InputFile;
                        string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
@@ -775,13 +876,12 @@ namespace System.Web.Compilation
                        if (!isProperty && !isField)
                                return; // an "impossible" case
                        
-                       Type declaringType = mi.DeclaringType;
                        CodeAssignStatement assign = new CodeAssignStatement ();
                        
                        assign.Left = new CodePropertyReferenceExpression (ctrlVar, memberName);
                        assign.Right = ResourceExpressionBuilder.CreateGetLocalResourceObject (mi, resname);
                        
-                       builder.method.Statements.Add (AddLinePragma (assign, builder));
+                       builder.Method.Statements.Add (AddLinePragma (assign, builder));
                }
 
                void AssignPropertiesFromResources (ControlBuilder builder, Type controlType, string attvalue)
@@ -812,7 +912,6 @@ namespace System.Web.Compilation
 
                        AssignPropertiesFromResources (builder, controlType, attvalue);
                }
-#endif
                
                void AddEventAssign (CodeMemberMethod method, ControlBuilder builder, string name, Type type, string value)
                {
@@ -831,15 +930,15 @@ namespace System.Web.Compilation
                        EventInfo [] ev_info = null;
                        Type type = builder.ControlType;
                        
-                       string attvalue = builder.attribs [id] as string;
-                       if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){
+                       string attvalue = builder.GetAttribute (id);
+                       if (id.Length > 2 && String.Compare (id.Substring (0, 2), "ON", true, Helpers.InvariantCulture) == 0){
                                if (ev_info == null)
                                        ev_info = type.GetEvents ();
 
                                string id_as_event = id.Substring (2);
                                foreach (EventInfo ev in ev_info){
                                        if (InvariantCompareNoCase (ev.Name, id_as_event)){
-                                               AddEventAssign (builder.method,
+                                               AddEventAssign (builder.Method,
                                                                builder,
                                                                ev.Name,
                                                                ev.EventHandlerType,
@@ -851,12 +950,10 @@ namespace System.Web.Compilation
 
                        }
 
-#if NET_2_0
-                       if (id.ToLower () == "meta:resourcekey") {
+                       if (String.Compare (id, "meta:resourcekey", StringComparison.OrdinalIgnoreCase) == 0) {
                                AssignPropertiesFromResources (builder, attvalue);
                                return;
                        }
-#endif
                        
                        int hyphen = id.IndexOf ('-');
                        string alt_id = id;
@@ -870,22 +967,25 @@ namespace System.Web.Compilation
                        }
 
                        if (!typeof (IAttributeAccessor).IsAssignableFrom (type))
-                               throw new ParseException (builder.location, "Unrecognized attribute: " + id);
-
-                       string val;
-                       CodeMemberMethod method = builder.method;
-                       bool databound = IsDataBound (attvalue);
-
-                       if (databound) {
-                               val = attvalue.Substring (3, attvalue.Length - 5).Trim ();
-#if NET_2_0
-                               if (StrUtils.StartsWith (val, "Bind", true)) {
-                                       Match match = bindRegexInValue.Match (val);
-                                       if (match.Success)
-                                               val = "Eval" + val.Substring (4);
-                               }
-#endif
-                               CreateDBAttributeMethod (builder, id, val);
+                               throw new ParseException (builder.Location, "Unrecognized attribute: " + id);
+
+                       CodeMemberMethod method = builder.Method;
+                       bool isDatabound = IsDataBound (attvalue);
+                       bool isExpression = !isDatabound && IsExpression (attvalue);
+
+                       if (isDatabound) {
+                               string value = attvalue.Substring (3, attvalue.Length - 5).Trim ();
+                               CodeExpression valueExpression = null;
+                               if (startsWithBindRegex.Match (value).Success)
+                                       valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, true);
+                               else
+                                       if (StrUtils.StartsWith (value, "Eval", true))
+                                               valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, false);
+                               
+                               if (valueExpression == null && value != null && value.Trim () != String.Empty)
+                                       valueExpression = new CodeSnippetExpression (value);
+                               
+                               CreateDBAttributeMethod (builder, id, valueExpression);
                        } else {
                                CodeCastExpression cast;
                                CodeMethodReferenceExpression methodExpr;
@@ -895,16 +995,23 @@ namespace System.Web.Compilation
                                methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
                                expr = new CodeMethodInvokeExpression (methodExpr);
                                expr.Parameters.Add (new CodePrimitiveExpression (id));
-                               expr.Parameters.Add (new CodePrimitiveExpression (attvalue));
+
+                               CodeExpression valueExpr = null;
+                               if (isExpression)
+                                       valueExpr = CompileExpression (null, typeof (string), attvalue, true);
+
+                               if (valueExpr == null)
+                                       valueExpr = new CodePrimitiveExpression (attvalue);
+                               
+                               expr.Parameters.Add (valueExpr);
                                method.Statements.Add (AddLinePragma (expr, builder));
                        }
-
                }
 
                protected void CreateAssignStatementsFromAttributes (ControlBuilder builder)
                {
                        this.dataBoundAtts = 0;
-                       IDictionary atts = builder.attribs;
+                       IDictionary atts = builder.Attributes;
                        if (atts == null || atts.Count == 0)
                                return;
                        
@@ -913,31 +1020,31 @@ namespace System.Web.Compilation
                                        continue;
                                // ID is assigned in BuildControltree
                                if (InvariantCompareNoCase (id, "id"))
-                                       continue;
-                               
-#if NET_2_0
+                                       continue;                               
+
                                /* we skip SkinID here as it's assigned in BuildControlTree */
                                if (InvariantCompareNoCase (id, "skinid"))
                                        continue;
                                if (InvariantCompareNoCase (id, "meta:resourcekey"))
                                        continue; // ignore, this one's processed at the very end of
                                                  // the method
-#endif
                                CreateAssignStatementFromAttribute (builder, id);
                        }
                }
 
-               void CreateDBAttributeMethod (ControlBuilder builder, string attr, string code)
+               void CreateDBAttributeMethod (ControlBuilder builder, string attr, CodeExpression code)
                {
-                       if (code == null || code.Trim () == "")
+                       if (code == null)
                                return;
 
                        string id = builder.GetNextID (null);
                        string dbMethodName = "__DataBind_" + id;
-                       CodeMemberMethod method = builder.method;
+                       CodeMemberMethod method = builder.Method;
                        AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
 
                        method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
+                       builder.DataBindingMethod = method;
+
                        CodeCastExpression cast;
                        CodeMethodReferenceExpression methodExpr;
                        CodeMethodInvokeExpression expr;
@@ -951,7 +1058,7 @@ namespace System.Web.Compilation
                        tostring.Method = new CodeMethodReferenceExpression (
                                                        new CodeTypeReferenceExpression (typeof (Convert)),
                                                        "ToString");
-                       tostring.Parameters.Add (new CodeSnippetExpression (code));
+                       tostring.Parameters.Add (code);
                        expr.Parameters.Add (tostring);
                        method.Statements.Add (expr);
                        mainClass.Members.Add (method);
@@ -964,12 +1071,12 @@ namespace System.Web.Compilation
                                                        new CodeArgumentReferenceExpression ("parameterContainer"),
                                                        "Controls");
                                                        
-                       indexer.Indices.Add (new CodePrimitiveExpression (builder.renderIndex));
+                       indexer.Indices.Add (new CodePrimitiveExpression (builder.RenderIndex));
                        
                        CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (indexer, "RenderControl");
                        invoke.Parameters.Add (new CodeArgumentReferenceExpression ("__output"));
-                       builder.renderMethod.Statements.Add (invoke);
-                       builder.renderIndex++;
+                       builder.RenderMethod.Statements.Add (invoke);
+                       builder.IncreaseRenderIndex ();
                }
 
                protected void AddChildCall (ControlBuilder parent, ControlBuilder child)
@@ -977,13 +1084,14 @@ namespace System.Web.Compilation
                        if (parent == null || child == null)
                                return;
                        
-                       CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.method.Name);
+                       CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.Method.Name);
                        CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (m);
 
                        object [] atts = null;
-                       
+
                        if (child.ControlType != null)
-                               child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true);
+                               atts = child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true);
+                       
                        if (atts != null && atts.Length > 0) {
                                PartialCachingAttribute pca = (PartialCachingAttribute) atts [0];
                                CodeTypeReferenceExpression cc = new CodeTypeReferenceExpression("System.Web.UI.StaticPartialCachingControl");
@@ -1003,35 +1111,35 @@ namespace System.Web.Compilation
                                build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByCustom));
                                build.Parameters.Add (new CodeDelegateCreateExpression (
                                                              new CodeTypeReference (typeof (System.Web.UI.BuildMethod)),
-                                                             thisRef, child.method.Name));
+                                                             thisRef, child.Method.Name));
 
-                               parent.methodStatements.Add (AddLinePragma (build, parent));
+                               parent.MethodStatements.Add (AddLinePragma (build, parent));
                                if (parent.HasAspCode)
                                        AddRenderControl (parent);
                                return;
                        }
                                 
-                       if (child.isProperty || parent.ChildrenAsProperties) {
+                       if (child.IsProperty || parent.ChildrenAsProperties) {
                                expr.Parameters.Add (new CodeFieldReferenceExpression (ctrlVar, child.TagName));
-                               parent.methodStatements.Add (AddLinePragma (expr, parent));
+                               parent.MethodStatements.Add (AddLinePragma (expr, parent));
                                return;
                        }
 
-                       parent.methodStatements.Add (AddLinePragma (expr, parent));
+                       parent.MethodStatements.Add (AddLinePragma (expr, parent));
                        CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, child.ID);
                        if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType))
                                AddParsedSubObjectStmt (parent, field);
                        else {
                                CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (ctrlVar, "Add");
                                invoke.Parameters.Add (field);
-                               parent.methodStatements.Add (AddLinePragma (invoke, parent));
+                               parent.MethodStatements.Add (AddLinePragma (invoke, parent));
                        }
                                
                        if (parent.HasAspCode)
                                AddRenderControl (parent);
                }
 
-               void AddTemplateInvocation (CodeMemberMethod method, string name, string methodName)
+               void AddTemplateInvocation (ControlBuilder builder, string name, string methodName)
                {
                        CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
 
@@ -1042,11 +1150,10 @@ namespace System.Web.Compilation
                        newCompiled.Parameters.Add (newBuild);
 
                        CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
-                       method.Statements.Add (assign);
+                       builder.Method.Statements.Add (AddLinePragma (assign, builder));
                }
 
-#if NET_2_0
-               void AddBindableTemplateInvocation (CodeMemberMethod method, string name, string methodName, string extractMethodName)
+               void AddBindableTemplateInvocation (ControlBuilder builder, string name, string methodName, string extractMethodName)
                {
                        CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
 
@@ -1061,7 +1168,7 @@ namespace System.Web.Compilation
                        newCompiled.Parameters.Add (newExtract);
                        
                        CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
-                       method.Statements.Add (assign);
+                       builder.Method.Statements.Add (AddLinePragma (assign, builder));
                }
                
                string CreateExtractValuesMethod (TemplateBuilder builder)
@@ -1138,7 +1245,6 @@ namespace System.Web.Compilation
 
                        method.Statements.Add (AddLinePragma (invoke, cbuilder));
                }
-#endif
 
                void AddCodeRender (ControlBuilder parent, CodeRenderBuilder cr)
                {
@@ -1147,7 +1253,7 @@ namespace System.Web.Compilation
 
                        if (!cr.IsAssign) {
                                CodeSnippetStatement code = new CodeSnippetStatement (cr.Code);
-                               parent.renderMethod.Statements.Add (AddLinePragma (code, cr));
+                               parent.RenderMethod.Statements.Add (AddLinePragma (code, cr));
                                return;
                        }
 
@@ -1156,51 +1262,25 @@ namespace System.Web.Compilation
                                                        new CodeArgumentReferenceExpression ("__output"),
                                                        "Write");
 
-                       expr.Parameters.Add (new CodeSnippetExpression (cr.Code));
-                       parent.renderMethod.Statements.Add (AddLinePragma (expr, cr));
+                       expr.Parameters.Add (GetWrappedCodeExpression (cr));
+                       parent.RenderMethod.Statements.Add (AddLinePragma (expr, cr));
                }
 
-               static PropertyInfo GetContainerProperty (Type type, string[] propNames)
+               CodeExpression GetWrappedCodeExpression (CodeRenderBuilder cr)
                {
-                       PropertyInfo prop;
-                       
-                       foreach (string name in propNames) {
-                               prop = type.GetProperty (name, noCaseFlags & ~BindingFlags.NonPublic);
-                               if (prop != null)
-                                       return prop;
-                       }
-
-                       return null;
+                       var ret = new CodeSnippetExpression (cr.Code);
+#if NET_4_0
+                       if (cr.HtmlEncode) {
+                               var encodeRef = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (HttpUtility)), "HtmlEncode");
+                               return new CodeMethodInvokeExpression (encodeRef, new CodeExpression[] { ret });
+                       } else
+#endif
+                               return ret;
                }
-
-               static string[] containerPropNames = {"Items", "Rows"};
                
                static Type GetContainerType (ControlBuilder builder)
                {
-                       TemplateBuilder tb = builder as TemplateBuilder;
-                       if (tb != null && tb.ContainerType != null)
-                               return tb.ContainerType;
-
-                       Type type = builder.BindingContainerType;
-
-#if NET_2_0
-                       if (typeof (IDataItemContainer).IsAssignableFrom (type))
-                               return type;
-#endif
-                       
-                       PropertyInfo prop = GetContainerProperty (type, containerPropNames);
-                       if (prop == null)
-                               return type;
-
-                       Type ptype = prop.PropertyType;
-                       if (!typeof (ICollection).IsAssignableFrom (ptype))
-                               return type;
-
-                       prop = ptype.GetProperty ("Item", noCaseFlags & ~BindingFlags.NonPublic);
-                       if (prop == null)
-                               return type;
-
-                       return prop.PropertyType;
+                       return builder.BindingContainerType;
                }
                
                CodeMemberMethod CreateDBMethod (ControlBuilder builder, string name, Type container, Type target)
@@ -1250,12 +1330,13 @@ namespace System.Web.Compilation
                        string dbMethodName = "__DataBind_" + db.ID;
                        // Add the method that builds the DataBoundLiteralControl
                        InitMethod (db, false, false);
-                       CodeMemberMethod method = db.method;
+                       CodeMemberMethod method = db.Method;
                        AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
                        method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
 
                        // Add the DataBind handler
                        method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl));
+                       builder.DataBindingMethod = method;
 
                        CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
                        CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
@@ -1271,7 +1352,7 @@ namespace System.Web.Compilation
                        method.Statements.Add (AddLinePragma (invoke, builder));
                        
                        mainClass.Members.Add (method);
-
+                       
                        AddChildCall (builder, db);
                }
 
@@ -1291,16 +1372,14 @@ namespace System.Web.Compilation
                        if (!isTemplate && !inTemplate) {
                                CreateField (builder, true);
                        } else if (!isTemplate) {
-                               bool doCheck = false;
-                               
-#if NET_2_0
+                               bool doCheck = false;                           
                                bool singleInstance = false;
-                               ControlBuilder pb = builder.parentBuilder;
+                               ControlBuilder pb = builder.ParentBuilder;
                                TemplateBuilder tpb;
                                while (pb != null) {
                                        tpb = pb as TemplateBuilder;
                                        if (tpb == null) {
-                                               pb = pb.parentBuilder;
+                                               pb = pb.ParentBuilder;
                                                continue;
                                        }
                                        
@@ -1310,12 +1389,10 @@ namespace System.Web.Compilation
                                }
                                
                                if (!singleInstance)
-#endif
                                        builder.ID = builder.GetNextID (null);
-#if NET_2_0
                                else
                                        doCheck = true;
-#endif
+
                                CreateField (builder, doCheck);
                        }
 
@@ -1324,8 +1401,6 @@ namespace System.Web.Compilation
                                CreateAssignStatementsFromAttributes (builder);
 
                        if (builder.Children != null && builder.Children.Count > 0) {
-                               ArrayList templates = null;
-
                                StringBuilder sb = new StringBuilder ();
                                foreach (object b in builder.Children) {
                                        if (b is string) {
@@ -1336,113 +1411,103 @@ namespace System.Web.Compilation
                                        FlushText (builder, sb);
                                        if (b is ObjectTagBuilder) {
                                                ProcessObjectTag ((ObjectTagBuilder) b);
-                                               continue;
-                                       }
-
-                                       StringPropertyBuilder pb = b as StringPropertyBuilder;
-                                       if (pb != null){
+                                       } else if (b is StringPropertyBuilder) {
+                                               StringPropertyBuilder pb = b as StringPropertyBuilder;
                                                if (pb.Children != null && pb.Children.Count > 0) {
                                                        StringBuilder asb = new StringBuilder ();
                                                        foreach (string s in pb.Children)
                                                                asb.Append (s);
-                                                       CodeMemberMethod method = builder.method;
+                                                       CodeMemberMethod method = builder.Method;
                                                        CodeAssignStatement assign = new CodeAssignStatement ();
                                                        assign.Left = new CodePropertyReferenceExpression (ctrlVar, pb.PropertyName);
                                                        assign.Right = new CodePrimitiveExpression (asb.ToString ());
                                                        method.Statements.Add (AddLinePragma (assign, builder));
                                                }
-                                               continue;
-                                       }
-
-#if NET_2_0
-                                       if (b is ContentBuilderInternal) {
+                                       } else if (b is ContentBuilderInternal) {
                                                ContentBuilderInternal cb = (ContentBuilderInternal) b;
                                                CreateControlTree (cb, false, true);
-                                               AddContentTemplateInvocation (cb, builder.method, cb.method.Name);
-                                               continue;
-                                       }
-#endif
-
-                                       if (b is TemplateBuilder) {
-                                               if (templates == null)
-                                                       templates = new ArrayList ();
-
-                                               templates.Add (b);
+                                               AddContentTemplateInvocation (cb, builder.Method, cb.Method.Name);
                                                continue;
                                        }
 
-                                       if (b is CodeRenderBuilder) {
+                                       // Ignore TemplateBuilders - they are processed in InitMethod
+                                       else if (b is TemplateBuilder) {
+                                       } else if (b is CodeRenderBuilder) {
                                                AddCodeRender (builder, (CodeRenderBuilder) b);
-                                               continue;
-                                       }
-
-                                       if (b is DataBindingBuilder) {
+                                       } else if (b is DataBindingBuilder) {
                                                AddDataBindingLiteral (builder, (DataBindingBuilder) b);
-                                               continue;
-                                       }
-                                       
-                                       if (b is ControlBuilder) {
+                                       } else if (b is ControlBuilder) {
                                                ControlBuilder child = (ControlBuilder) b;
                                                CreateControlTree (child, inTemplate, builder.ChildrenAsProperties);
                                                AddChildCall (builder, child);
                                                continue;
-                                       }
+                                       } else
+                                               throw new Exception ("???");
 
-                                       throw new Exception ("???");
+                                       ControlBuilder bldr = b as ControlBuilder;
+                                       bldr.ProcessGeneratedCode (CompileUnit, BaseType, DerivedType, bldr.Method, bldr.DataBindingMethod);
                                }
 
                                FlushText (builder, sb);
-
-                               if (templates != null) {
-                                       foreach (TemplateBuilder b in templates) {
-                                               CreateControlTree (b, true, false);
-#if NET_2_0
-                                               if (b.BindingDirection == BindingDirection.TwoWay) {
-                                                       string extractMethod = CreateExtractValuesMethod (b);
-                                                       AddBindableTemplateInvocation (builder.method, b.TagName, b.method.Name, extractMethod);
-                                               }
-                                               else
-#endif
-                                               AddTemplateInvocation (builder.method, b.TagName, b.method.Name);
-                                       }
-                               }
-
                        }
 
-                       if (builder.defaultPropertyBuilder != null) {
-                               ControlBuilder b = builder.defaultPropertyBuilder;
-                               CreateControlTree (b, false, true);
-                               AddChildCall (builder, b);
+                       ControlBuilder defaultPropertyBuilder = builder.DefaultPropertyBuilder;
+                       if (defaultPropertyBuilder != null) {
+                               CreateControlTree (defaultPropertyBuilder, false, true);
+                               AddChildCall (builder, defaultPropertyBuilder);
                        }
-
+                       
                        if (builder.HasAspCode) {
+                               CodeMemberMethod renderMethod = builder.RenderMethod;
                                CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
                                m.TargetObject = thisRef;
-                               m.MethodName = builder.renderMethod.Name;
+                               m.MethodName = renderMethod.Name;
 
                                CodeDelegateCreateExpression create = new CodeDelegateCreateExpression ();
                                create.DelegateType = new CodeTypeReference (typeof (RenderMethod));
                                create.TargetObject = thisRef;
-                               create.MethodName = builder.renderMethod.Name;
+                               create.MethodName = renderMethod.Name;
 
                                CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
                                invoke.Method = new CodeMethodReferenceExpression (ctrlVar, "SetRenderMethodDelegate");
                                invoke.Parameters.Add (create);
 
-                               builder.methodStatements.Add (invoke);
+                               builder.MethodStatements.Add (invoke);
                        }
 
-#if NET_2_0
                        if (builder is RootBuilder)
                                if (!String.IsNullOrEmpty (parser.MetaResourceKey))
                                        AssignPropertiesFromResources (builder, parser.BaseType, parser.MetaResourceKey);
                        
-                       if ((!isTemplate || builder is RootBuilder) && builder.attribs != null && builder.attribs ["meta:resourcekey"] != null)
+                       if ((!isTemplate || builder is RootBuilder) && !String.IsNullOrEmpty (builder.GetAttribute ("meta:resourcekey")))
                                CreateAssignStatementFromAttribute (builder, "meta:resourcekey");
-#endif
 
                        if (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType))
-                               builder.method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
+                               builder.Method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
+
+                       builder.ProcessGeneratedCode (CompileUnit, BaseType, DerivedType, builder.Method, builder.DataBindingMethod);
+               }
+
+               protected override void AddStatementsToConstructor (CodeConstructor ctor)
+               {
+                       if (masterPageContentPlaceHolders == null || masterPageContentPlaceHolders.Count == 0)
+                               return;
+                       
+                       var ilist = new CodeVariableDeclarationStatement ();
+                       ilist.Name = "__contentPlaceHolders";
+                       ilist.Type = new CodeTypeReference (typeof (IList));
+                       ilist.InitExpression = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders");
+                       
+                       var ilistRef = new CodeVariableReferenceExpression ("__contentPlaceHolders");
+                       CodeStatementCollection statements = ctor.Statements;
+                       statements.Add (ilist);
+
+                       CodeMethodInvokeExpression mcall;
+                       foreach (string id in masterPageContentPlaceHolders) {
+                               mcall = new CodeMethodInvokeExpression (ilistRef, "Add");
+                               mcall.Parameters.Add (new CodePrimitiveExpression (id.ToLowerInvariant ()));
+                               statements.Add (mcall);
+                       }
                }
                
                protected internal override void CreateMethods ()
@@ -1454,13 +1519,31 @@ namespace System.Web.Compilation
                        CreateFrameworkInitializeMethod ();
                }
 
+               protected override void InitializeType ()
+               {
+                       List <string> registeredTagNames = parser.RegisteredTagNames;
+                       RootBuilder rb = parser.RootBuilder;
+                       if (rb == null || registeredTagNames == null || registeredTagNames.Count == 0)
+                               return;
+
+                       AspComponent component;
+                       foreach (string tagName in registeredTagNames) {
+                               component = rb.Foundry.GetComponent (tagName);
+                               if (component == null || component.Type == null) // unlikely
+                                       throw new HttpException ("Custom control '" + tagName + "' cannot be found.");
+                               if (!(typeof (UserControl).IsAssignableFrom (component.Type)))
+                                       throw new ParseException (parser.Location, "Type '" + component.Type.ToString () + "' does not derive from 'System.Web.UI.UserControl'.");
+                               AddReferencedAssembly (component.Type.Assembly);
+                       }
+               }
+               
                void CallBaseFrameworkInitialize (CodeMemberMethod method)
                {
                        CodeBaseReferenceExpression baseRef = new CodeBaseReferenceExpression ();
                        CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (baseRef, "FrameworkInitialize");
                        method.Statements.Add (invoke);
                }
-
+               
                void CallSetStringResourcePointer (CodeMemberMethod method)
                {
                        CodeFieldReferenceExpression stringResource = GetMainClassFieldReferenceExpression ("__stringResource");
@@ -1478,9 +1561,7 @@ namespace System.Web.Compilation
                        method.Name = "FrameworkInitialize";
                        method.Attributes = MemberAttributes.Family | MemberAttributes.Override;
                        PrependStatementsToFrameworkInitialize (method);
-#if NET_2_0
                        CallBaseFrameworkInitialize (method);
-#endif
                        CallSetStringResourcePointer (method);
                        AppendStatementsToFrameworkInitialize (method);
                        mainClass.Members.Add (method);
@@ -1558,14 +1639,41 @@ namespace System.Web.Compilation
 
                        CodeCastExpression cast = new CodeCastExpression (appType.FullName, propRef);
                        prop.GetStatements.Add (new CodeMethodReturnStatement (cast));
-#if NET_2_0
                        if (partialClass != null)
                                partialClass.Members.Add (prop);
                        else
-#endif
-                       mainClass.Members.Add (prop);
+                               mainClass.Members.Add (prop);
                }
 
+               void CreateContentPlaceHolderTemplateProperty (string backingField, string name)
+               {
+                       CodeMemberProperty prop = new CodeMemberProperty ();
+                       prop.Type = new CodeTypeReference (typeof (ITemplate));
+                       prop.Name = name;
+                       prop.Attributes = MemberAttributes.Public;
+
+                       var ret = new CodeMethodReturnStatement ();
+                       var fldRef = new CodeFieldReferenceExpression (thisRef, backingField);
+                       ret.Expression = fldRef;
+                       prop.GetStatements.Add (ret);
+                       prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
+
+                       prop.CustomAttributes.Add (new CodeAttributeDeclaration ("TemplateContainer", new CodeAttributeArgument [] {
+                                               new CodeAttributeArgument (new CodeTypeOfExpression (new CodeTypeReference (typeof (MasterPage))))
+                                       }
+                               )
+                       );
+
+                       var enumValueRef = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof (TemplateInstance)), "Single");
+                       prop.CustomAttributes.Add (new CodeAttributeDeclaration ("TemplateInstanceAttribute", new CodeAttributeArgument [] {
+                                               new CodeAttributeArgument (enumValueRef)
+                                       }
+                               )
+                       );
+
+                       mainClass.Members.Add (prop);
+               }
+               
                void CreateAutoHandlers ()
                {
                        // Create AutoHandlers property
@@ -1581,11 +1689,8 @@ namespace System.Web.Compilation
                        prop.GetStatements.Add (ret);
                        prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
 
-#if NET_2_0
                        CodeAttributeDeclaration attr = new CodeAttributeDeclaration ("System.Obsolete");
-                       prop.CustomAttributes.Add (attr);
-#endif
-                       
+                       prop.CustomAttributes.Add (attr);                       
                        mainClass.Members.Add (prop);
 
                        // Add the __autoHandlers field
@@ -1605,66 +1710,36 @@ namespace System.Web.Compilation
                        mainClass.Members.Add (prop);
                }
 
-#if NET_2_0
                protected virtual string HandleUrlProperty (string str, MemberInfo member)
                {
                        return str;
                }
-#endif
 
                TypeConverter GetConverterForMember (MemberInfo member)
                {
-                       TypeConverterAttribute tca = null;
-                       object[] attrs;
-                       
-#if NET_2_0
-                       attrs = member.GetCustomAttributes (typeof (TypeConverterAttribute), true);
-                       if (attrs.Length > 0)
-                               tca = attrs [0] as TypeConverterAttribute;
-#else
-                       attrs = member.GetCustomAttributes (true);
-                       
-                       foreach (object attr in attrs) {
-                               tca = attr as TypeConverterAttribute;
-                               
-                               if (tca != null)
-                                       break;
-                       }
-#endif
-
-                       if (tca == null)
+                       TypeDescriptionProvider prov = TypeDescriptor.GetProvider (member.ReflectedType);
+                       if (prov == null)
                                return null;
 
-                       string typeName = tca.ConverterTypeName;
-                       if (typeName == null || typeName.Length == 0)
-                               return null;
+                       ICustomTypeDescriptor desc = prov.GetTypeDescriptor (member.ReflectedType);
+                       PropertyDescriptorCollection coll = desc != null ? desc.GetProperties () : null;
 
-                       Type t = null;
-                       try {
-                               t = HttpApplication.LoadType (typeName);
-                       } catch (Exception) {
-                               // ignore
-                       }
+                       if (coll == null || coll.Count == 0)
+                               return null;
 
-                       if (t == null)
+                       PropertyDescriptor pd = coll.Find (member.Name, false);
+                       if (pd == null)
                                return null;
 
-                       return (TypeConverter) Activator.CreateInstance (t);
+                       return pd.Converter;
                }
                
                CodeExpression CreateNullableExpression (Type type, CodeExpression inst, bool nullable)
                {
-#if NET_2_0
                        if (!nullable)
                                return inst;
                        
-                       return new CodeObjectCreateExpression (
-                               type,
-                               new CodeExpression[] {inst}
-                       );
-#else
-                       return inst;
-#endif
+                       return new CodeObjectCreateExpression (type, new CodeExpression[] {inst});
                }
 
                bool SafeCanConvertFrom (Type type, TypeConverter cvt)
@@ -1690,7 +1765,7 @@ namespace System.Web.Compilation
                        TypeConverter cvt = GetConverterForMember (member);
                        if (cvt != null && !SafeCanConvertFrom (typeof (string), cvt))
                                cvt = null;
-
+                       
                        object convertedFromAttr = null;
                        bool preConverted = false;
                        if (cvt != null && str != null) {
@@ -1703,30 +1778,25 @@ namespace System.Web.Compilation
 
                        bool wasNullable = false;
                        Type originalType = type;
-#if NET_2_0
+
                        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
                                Type[] types = type.GetGenericArguments();
                                originalType = type;
                                type = types[0]; // we're interested only in the first type here
                                wasNullable = true;
                        }
-#endif
-                       
+
                        if (type == typeof (string)) {
-                               if (preConverted)
+                               object[] urlAttr = member.GetCustomAttributes (typeof (UrlPropertyAttribute), true);
+                               if (urlAttr.Length != 0)
+                                       str = HandleUrlProperty ((preConverted && convertedFromAttr is string) ? (string)convertedFromAttr : str, member);
+                               else if (preConverted)
                                        return CreateNullableExpression (originalType,
                                                                         new CodePrimitiveExpression ((string) convertedFromAttr),
                                                                         wasNullable);
-                               
-#if NET_2_0
-                               object[] urlAttr = member.GetCustomAttributes (typeof (UrlPropertyAttribute), true);
-                               if (urlAttr.Length != 0)
-                                       str = HandleUrlProperty (str, member);
-#endif
-                               return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
-                       }
 
-                       if (type == typeof (bool)) {
+                               return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
+                       } else if (type == typeof (bool)) {
                                if (preConverted)
                                        return CreateNullableExpression (originalType,
                                                                         new CodePrimitiveExpression ((bool) convertedFromAttr),
@@ -1736,14 +1806,13 @@ namespace System.Web.Compilation
                                        return CreateNullableExpression (originalType, new CodePrimitiveExpression (true), wasNullable);
                                else if (InvariantCompareNoCase (str, "false"))
                                        return CreateNullableExpression (originalType, new CodePrimitiveExpression (false), wasNullable);
-#if NET_2_0
                                else if (wasNullable && InvariantCompareNoCase(str, "null"))
                                        return new CodePrimitiveExpression (null);
-#endif
                                else
                                        throw new ParseException (currentLocation,
                                                        "Value '" + str  + "' is not a valid boolean.");
-                       }
+                       } else if (type == monoTypeType)
+                               type = typeof (System.Type);
                        
                        if (str == null)
                                return new CodePrimitiveExpression (null);
@@ -1752,7 +1821,7 @@ namespace System.Web.Compilation
                                return CreateNullableExpression (originalType,
                                                                 new CodePrimitiveExpression (
                                                                         Convert.ChangeType (preConverted ? convertedFromAttr : str,
-                                                                                            type, CultureInfo.InvariantCulture)),
+                                                                                            type, Helpers.InvariantCulture)),
                                                                 wasNullable);
 
                        if (type == typeof (string [])) {
@@ -1837,10 +1906,34 @@ namespace System.Web.Compilation
                                }
                        }
 
-                       TypeConverter converter = preConverted ? cvt :
-                               wasNullable ? TypeDescriptor.GetConverter (type) :
-                               TypeDescriptor.GetProperties (member.DeclaringType) [member.Name].Converter;
+                       TypeConverter converter = preConverted ? cvt : wasNullable ? TypeDescriptor.GetConverter (type) : null;
+                       if (converter == null) {
+                               PropertyDescriptor pdesc = TypeDescriptor.GetProperties (member.DeclaringType) [member.Name];
+                               if (pdesc != null)
+                                       converter = pdesc.Converter;
+                               else {
+                                       Type memberType;
+                                       switch (member.MemberType) {
+                                               case MemberTypes.Field:
+                                                       memberType = ((FieldInfo)member).FieldType;
+                                                       break;
+
+                                               case MemberTypes.Property:
+                                                       memberType = ((PropertyInfo)member).PropertyType;
+                                                       break;
+
+                                               default:
+                                                       memberType = null;
+                                                       break;
+                                       }
+
+                                       if (memberType == null)
+                                               return null;
 
+                                       converter = TypeDescriptor.GetConverter (memberType);
+                               }
+                       }
+                       
                        if (preConverted || (converter != null && SafeCanConvertFrom (typeof (string), converter))) {
                                object value = preConverted ? convertedFromAttr : converter.ConvertFromInvariantString (str);
 
@@ -1849,8 +1942,12 @@ namespace System.Web.Compilation
                                        if (wasNullable)
                                                return CreateNullableExpression (originalType, GenerateInstance (idesc, true),
                                                                                 wasNullable);
-                                       
-                                       return new CodeCastExpression (type, GenerateInstance (idesc, true));
+
+                                       CodeExpression instance = GenerateInstance (idesc, true);
+                                       if (type.IsPublic)
+                                               return new CodeCastExpression (type, instance);
+                                       else
+                                               return instance;
                                }
 
                                CodeExpression exp = GenerateObjectInstance (value, false);
@@ -1869,7 +1966,7 @@ namespace System.Web.Compilation
 
                                if (wasNullable)
                                        return CreateNullableExpression (originalType, invoke, wasNullable);
-                               
+
                                return new CodeCastExpression (type, invoke);
                        }
 
@@ -1953,6 +2050,10 @@ namespace System.Web.Compilation
                {
                        if (value is System.Web.UI.WebControls.Unit) {
                                System.Web.UI.WebControls.Unit s = (System.Web.UI.WebControls.Unit) value;
+                               if (s.IsEmpty) {
+                                       FieldInfo f = typeof (Unit).GetField ("Empty");
+                                       return new InstanceDescriptor (f, null);
+                               }
                                ConstructorInfo c = typeof(System.Web.UI.WebControls.Unit).GetConstructor (
                                        BindingFlags.Instance | BindingFlags.Public,
                                        null,
@@ -1964,6 +2065,10 @@ namespace System.Web.Compilation
                        
                        if (value is System.Web.UI.WebControls.FontUnit) {
                                System.Web.UI.WebControls.FontUnit s = (System.Web.UI.WebControls.FontUnit) value;
+                               if (s.IsEmpty) {
+                                       FieldInfo f = typeof (FontUnit).GetField ("Empty");
+                                       return new InstanceDescriptor (f, null);
+                               }
 
                                Type cParamType = null;
                                object cParam = null;
@@ -1991,6 +2096,21 @@ namespace System.Web.Compilation
                        }
                        return null;
                }
+
+#if DEBUG
+               CodeMethodInvokeExpression CreateConsoleWriteLineCall (string format, params CodeExpression[] parms)
+               {
+                       CodeMethodReferenceExpression cwl = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (System.Console)), "WriteLine");
+                       CodeMethodInvokeExpression cwlCall = new CodeMethodInvokeExpression (cwl);
+
+                       cwlCall.Parameters.Add (new CodePrimitiveExpression (format));
+                       if (parms != null && parms.Length > 0)
+                               foreach (CodeExpression expr in parms)
+                                       cwlCall.Parameters.Add (expr);
+
+                       return cwlCall;
+               }
+#endif
        }
 }