Merge pull request #2700 from akoeplinger/monotouch-mobile-static
[mono.git] / mcs / class / System.Web / System.Web.UI / ControlBuilder.cs
index 67066a364ceef021dc310646e9209cb26ca8e730..c250639cbd28c56862de52b7b3e93437630c9aeb 100644 (file)
@@ -4,9 +4,10 @@
 // Authors:
 //     Duncan Mak  (duncan@ximian.com)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//      Marek Habersack <mhabersack@novell.com>
 //
 // (C) 2002, 2003 Ximian, Inc. (http://www.ximian.com)
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 //
 
 using System.Collections;
+using System.Configuration;
 using System.CodeDom;
+using System.Globalization;
 using System.Reflection;
 using System.Security.Permissions;
 using System.Web.Compilation;
+using System.Web.Configuration;
+using System.IO;
+using System.Web.UI.WebControls;
+using System.Web.Util;
+
+using _Location = System.Web.Compilation.Location;
 
 namespace System.Web.UI {
 
@@ -41,35 +50,43 @@ namespace System.Web.UI {
        [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public class ControlBuilder
        {
-               internal static BindingFlags flagsNoCase = BindingFlags.Public |
-                                                          BindingFlags.Instance |
-                                                          BindingFlags.Static |
-                                                          BindingFlags.IgnoreCase;
+               internal static readonly BindingFlags FlagsNoCase = BindingFlags.Public |
+                       BindingFlags.Instance |
+                       BindingFlags.Static |
+                       BindingFlags.IgnoreCase;
 
+               ControlBuilder myNamingContainer;
                TemplateParser parser;
-               internal ControlBuilder parentBuilder;
+               Type parserType;
+               ControlBuilder parentBuilder;
                Type type;             
                string tagName;
+               string originalTagName;
                string id;
-               internal IDictionary attribs;
-               internal int line;
-               internal string fileName;
+               IDictionary attribs;
+               int line;
+               string fileName;
                bool childrenAsProperties;
                bool isIParserAccessor = true;
                bool hasAspCode;
-               internal ControlBuilder defaultPropertyBuilder;
+               ControlBuilder defaultPropertyBuilder;
                ArrayList children;
+               ArrayList templateChildren;
                static int nextID;
 
-               internal bool haveParserVariable;
-               internal CodeMemberMethod method;
-               internal CodeStatementCollection methodStatements;
-               internal CodeMemberMethod renderMethod;
-               internal int renderIndex;
-               internal bool isProperty;
-               internal ILocation location;
+               bool haveParserVariable;
+               CodeMemberMethod method;
+               CodeStatementCollection methodStatements;
+               CodeMemberMethod renderMethod;
+               int renderIndex;
+               bool isProperty;
+               bool isPropertyWritable;
+               ILocation location;
                ArrayList otherTags;
 
+               int localVariableCount = 0;
+               bool? isTemplate;
+               
                public ControlBuilder ()
                {
                }
@@ -85,6 +102,7 @@ namespace System.Web.UI {
 
                {
                        this.parser = parser;
+                       this.parserType = parser != null ? parser.GetType () : null;
                        this.parentBuilder = parentBuilder;
                        this.type = type;
                        this.tagName = tagName;
@@ -99,7 +117,71 @@ namespace System.Web.UI {
                        if (otherTags == null)
                                otherTags = new ArrayList ();
                }
+
+               internal ControlBuilder ParentBuilder {
+                       get { return parentBuilder; }
+               }
+
+               internal IDictionary Attributes {
+                       get { return attribs; }
+               }
+
+               internal int Line {
+                       get { return line; }
+                       set { line = value; }
+               }
+
+               internal string FileName {
+                       get { return fileName; }
+                       set { fileName = value; }
+               }
+
+               internal ControlBuilder DefaultPropertyBuilder {
+                       get { return defaultPropertyBuilder; }
+               }
+
+               internal bool HaveParserVariable {
+                       get { return haveParserVariable; }
+                       set { haveParserVariable = value; }
+               }
+
+               internal CodeMemberMethod Method {
+                       get { return method; }
+                       set { method = value; }
+               }
+
+               internal CodeMemberMethod DataBindingMethod {
+                       get;
+                       set;
+               }
+                       
+               internal CodeStatementCollection MethodStatements {
+                       get { return methodStatements; }
+                       set { methodStatements = value; }
+               }
+
+               internal CodeMemberMethod RenderMethod {
+                       get { return renderMethod; }
+                       set { renderMethod = value; }
+               }
+
+               internal int RenderIndex {
+                       get { return renderIndex; }
+               }
+
+               internal bool IsProperty {
+                       get { return isProperty; }
+               }
+
+               internal bool IsPropertyWritable {
+                       get { return isPropertyWritable; }
+               }
                
+               internal ILocation Location {
+                       get { return location; }
+                       set { location = new _Location (value); }
+               }
+       
                internal ArrayList OtherTags {
                        get { return otherTags; }
                }
@@ -129,6 +211,10 @@ namespace System.Web.UI {
                        get { return children; }
                }
 
+               internal ArrayList TemplateChildren {
+                       get { return templateChildren; }
+               }
+               
                internal void SetControlType (Type t)
                {
                        type = t;
@@ -140,41 +226,97 @@ namespace System.Web.UI {
 
                public Type NamingContainerType {
                        get {
-                               if (parentBuilder == null)
+                               ControlBuilder cb = myNamingContainer;
+                               
+                               if (cb == null)
                                        return typeof (Control);
 
-                               Type ptype = parentBuilder.ControlType;
-                               if (ptype == null)
-                                       return parentBuilder.NamingContainerType;
+                               return cb.ControlType;
+                       }
+               }
 
-                               if (!typeof (INamingContainer).IsAssignableFrom (ptype))
-                                       return parentBuilder.NamingContainerType;
+               internal bool IsNamingContainer {
+                       get {
+                               if (type == null)
+                                       return false;
 
-                               return ptype;
+                               return typeof (INamingContainer).IsAssignableFrom (type);
                        }
                }
 
-#if NET_2_0
-               public virtual
-#else
-               internal
-#endif
-               Type BindingContainerType {
+               internal bool IsTemplate {
                        get {
-                               if (parentBuilder == null)
+                               if (isTemplate == null)
+                                       isTemplate = (typeof (TemplateBuilder).IsAssignableFrom (GetType ()));
+
+                               return isTemplate.Value;
+                       }
+               }
+               
+               internal bool PropertyBuilderShouldReturnValue {
+                       get { return isProperty && isPropertyWritable && RenderMethod == null && !IsTemplate && !(this is CollectionBuilder) && !(this is RootBuilder); }
+               }
+               
+               ControlBuilder MyNamingContainer {
+                       get {
+                               if (myNamingContainer == null) {
+                                       Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
+                                       
+                                       if (parentBuilder == null && controlType == null)
+                                               myNamingContainer = null;
+                                       else if (parentBuilder is TemplateBuilder)
+                                               myNamingContainer = parentBuilder;
+                                       else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
+                                               myNamingContainer = parentBuilder;
+                                       else
+                                               myNamingContainer = parentBuilder.MyNamingContainer;
+                               }
+
+                               return myNamingContainer;
+                       }
+               }
+                       
+               public virtual Type BindingContainerType {
+                       get {
+                               ControlBuilder cb = (this is TemplateBuilder && !(this is RootBuilder)) ? this : MyNamingContainer;
+                               
+                               if (cb == null) {
+                                       if (this is RootBuilder && parserType == typeof (PageParser)) 
+                                               return typeof (Page);
+                                       
                                        return typeof (Control);
+                               }
+
+                               if (cb != this && cb is ContentBuilderInternal && !typeof (INonBindingContainer).IsAssignableFrom (cb.BindingContainerType))
+                                       return cb.BindingContainerType;
+
+                               Type ct;
+                               if (cb is TemplateBuilder) {
+                                       ct = ((TemplateBuilder) cb).ContainerType;
+                                       if (typeof (INonBindingContainer).IsAssignableFrom (ct))
+                                               return MyNamingContainer.BindingContainerType;
                                        
-                               if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
-                                       return ((TemplateBuilder)parentBuilder).ContainerType;
+                                       if (ct != null)
+                                               return ct;
 
-                               Type ptype = parentBuilder.ControlType;
-                               if (ptype == null)
-                                       return parentBuilder.BindingContainerType;
+                                       ct = cb.ControlType;
+                                       if (ct == null)
+                                               return typeof (Control);
+                                       
+                                       if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
+                                               return MyNamingContainer.BindingContainerType;
 
-                               if (!typeof (INamingContainer).IsAssignableFrom (ptype))
-                                       return parentBuilder.BindingContainerType;
+                                       return ct;
+                               }
 
-                               return ptype;
+                               ct = cb.ControlType;
+                               if (ct == null)
+                                       return typeof (Control);
+                               
+                               if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
+                                       return MyNamingContainer.BindingContainerType;
+                               
+                               return cb.ControlType;
                        }
                }
 
@@ -197,9 +339,17 @@ namespace System.Web.UI {
                        get { return tagName; }
                }
 
+               internal string OriginalTagName {
+                       get {
+                               if (originalTagName == null || originalTagName.Length == 0)
+                                       return TagName;
+                               return originalTagName;
+                       }
+               }
+               
                internal RootBuilder Root {
                        get {
-                               if (GetType () == typeof (RootBuilder))
+                               if (typeof (RootBuilder).IsAssignableFrom (GetType ()))
                                        return (RootBuilder) this;
 
                                return (RootBuilder) parentBuilder.Root;
@@ -209,6 +359,51 @@ namespace System.Web.UI {
                internal bool ChildrenAsProperties {
                        get { return childrenAsProperties; }
                }
+
+               internal string GetAttribute (string name)
+               {
+                       if (attribs == null)
+                               return null;
+
+                       return attribs [name] as string;
+               }
+
+               internal void IncreaseRenderIndex ()
+               {
+                       renderIndex++;
+               }
+               
+               void AddChild (object child)
+               {
+                       if (children == null)
+                               children = new ArrayList ();
+
+                       children.Add (child);
+                       ControlBuilder cb = child as ControlBuilder;
+                       if (cb != null && cb is TemplateBuilder) {
+                               if (templateChildren == null)
+                                       templateChildren = new ArrayList ();
+                               templateChildren.Add (child);
+                       }
+
+                       if (parser == null)
+                               return;
+                       
+                       string tag = cb != null ? cb.TagName : null;
+                       if (String.IsNullOrEmpty (tag))
+                               return;
+
+                       RootBuilder rb = Root;
+                       AspComponentFoundry foundry = rb != null ? rb.Foundry : null;
+                       if (foundry == null)
+                               return;
+                       AspComponent component = foundry.GetComponent (tag);
+                       if (component == null || !component.FromConfig)
+                               return;
+                       
+                       parser.AddImport (component.Namespace);
+                       parser.AddDependency (component.Source);
+               }
                
                public virtual bool AllowWhitespaceLiterals ()
                {
@@ -217,30 +412,27 @@ namespace System.Web.UI {
 
                public virtual void AppendLiteralString (string s)
                {
-                       if (s == null || s == "")
+                       if (s == null || s.Length == 0)
                                return;
 
                        if (childrenAsProperties || !isIParserAccessor) {
                                if (defaultPropertyBuilder != null) {
                                        defaultPropertyBuilder.AppendLiteralString (s);
-                               } else if (s.Trim () != "") {
-                                       throw new HttpException ("Literal content not allowed for " + tagName + " " +
-                                                               GetType () + " \"" + s + "\"");
+                               } else if (s.Trim ().Length != 0) {
+                                       throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
+                                                                               tagName, GetType (), s));
                                }
 
                                return;
                        }
                        
-                       if (!AllowWhitespaceLiterals () && s.Trim () == "")
+                       if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
                                return;
 
                        if (HtmlDecodeLiterals ())
                                s = HttpUtility.HtmlDecode (s);
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (s);
+                       AddChild (s);
                }
 
                public virtual void AppendSubBuilder (ControlBuilder subBuilder)
@@ -258,10 +450,7 @@ namespace System.Web.UI {
                                return;
                        }
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (subBuilder);
+                       AddChild (subBuilder);
                }
 
                void AppendToProperty (ControlBuilder subBuilder)
@@ -274,10 +463,7 @@ namespace System.Web.UI {
                                return;
                        }
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (subBuilder);
+                       AddChild (subBuilder);
                }
 
                void AppendCode (ControlBuilder subBuilder)
@@ -288,16 +474,81 @@ namespace System.Web.UI {
                        if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
                                hasAspCode = true;
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (subBuilder);
+                       AddChild (subBuilder);
                }
 
                public virtual void CloseControl ()
                {
                }
 
+               static Type MapTagType (Type tagType)
+               {
+                       if (tagType == null)
+                               return null;
+                       
+                       PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
+                       if (ps == null)
+                               return tagType;
+
+                       TagMapCollection tags = ps.TagMapping;
+                       if (tags == null || tags.Count == 0)
+                               return tagType;
+                       
+                       string tagTypeName = tagType.ToString ();
+                       Type mappedType, originalType;
+                       string originalTypeName = String.Empty, mappedTypeName = String.Empty;
+                       bool missingType;
+                       Exception error;
+                       
+                       foreach (TagMapInfo tmi in tags) {
+                               error = null;
+                               originalType = null;
+                               
+                               try {
+                                       originalTypeName = tmi.TagType;
+                                       originalType = HttpApplication.LoadType (originalTypeName);
+                                       if (originalType == null)
+                                               missingType = true;
+                                       else
+                                               missingType = false;
+                               } catch (Exception ex) {
+                                       missingType = true;
+                                       error = ex;
+                               }
+                               if (missingType)
+                                       throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
+                               
+                               if (originalTypeName == tagTypeName) {
+                                       mappedTypeName = tmi.MappedTagType;
+                                       error = null;
+                                       mappedType = null;
+                                       
+                                       try {
+                                               mappedType = HttpApplication.LoadType (mappedTypeName);
+                                               if (mappedType == null)
+                                                       missingType = true;
+                                               else
+                                                       missingType = false;
+                                       } catch (Exception ex) {
+                                               missingType = true;
+                                               error = ex;
+                                       }
+
+                                       if (missingType)
+                                               throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
+                                                                        error);
+                                       
+                                       if (!mappedType.IsSubclassOf (originalType))
+                                               throw new ConfigurationErrorsException (
+                                                       String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
+
+                                       return mappedType;
+                               }
+                       }
+                       
+                       return tagType;
+               }
+
                public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
                                                                    ControlBuilder parentBuilder,
                                                                    Type type,
@@ -307,8 +558,9 @@ namespace System.Web.UI {
                                                                    int line,
                                                                    string sourceFileName)
                {
-                       ControlBuilder  builder;
-                       object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
+                       Type tagType = MapTagType (type);
+                       ControlBuilder builder;
+                       object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
                        if (atts != null && atts.Length > 0) {
                                ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
                                builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
@@ -316,7 +568,7 @@ namespace System.Web.UI {
                                builder = new ControlBuilder ();
                        }
 
-                       builder.Init (parser, parentBuilder, type, tagName, id, attribs);
+                       builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
                        builder.line = line;
                        builder.fileName = sourceFileName;
                        return builder;
@@ -336,12 +588,20 @@ namespace System.Web.UI {
                {
                        return false;
                }
-
+               
                ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
                {
-                       PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
+                       int idx;
+                       string propertyName;
+                       
+                       if ((idx = propName.IndexOf (':')) >= 0)
+                               propertyName = propName.Substring (idx + 1);
+                       else
+                               propertyName = propName;
+                       
+                       PropertyInfo prop = type.GetProperty (propertyName, FlagsNoCase);
                        if (prop == null) {
-                               string msg = String.Format ("Property {0} not found in type {1}", propName, type);
+                               string msg = String.Format ("Property {0} not found in type {1}", propertyName, type);
                                throw new HttpException (msg);
                        }
 
@@ -357,6 +617,9 @@ namespace System.Web.UI {
                                builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
                                                                 null, atts, line, fileName);
                                builder.isProperty = true;
+                               builder.isPropertyWritable = prop.CanWrite;
+                               if (idx >= 0)
+                                       builder.originalTagName = propName;
                                return builder;
                        }
 
@@ -364,6 +627,9 @@ namespace System.Web.UI {
                        builder.fileName = fileName;
                        builder.line = line;
                        builder.isProperty = true;
+                       builder.isPropertyWritable = prop.CanWrite;
+                       if (idx >= 0)
+                               builder.originalTagName = propName;
                        return builder;
                }
                
@@ -376,7 +642,7 @@ namespace System.Web.UI {
                {
                        this.parser = parser;
                        if (parser != null)
-                               this.location = parser.Location;
+                               this.Location = parser.Location;
 
                        this.parentBuilder = parentBuilder;
                        this.type = type;
@@ -397,10 +663,9 @@ namespace System.Web.UI {
                        } else if (atts.Length > 0) {
                                ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
                                childrenAsProperties = att.ChildrenAsProperties;
-                               if (childrenAsProperties && att.DefaultProperty != "") {
+                               if (childrenAsProperties && att.DefaultProperty.Length != 0)
                                        defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
                                                                                        parser, null);
-                               }
                        }
                }
 
@@ -430,29 +695,54 @@ namespace System.Web.UI {
 
                internal string GetNextID (string proposedID)
                {
-                       if (proposedID != null && proposedID.Trim () != "")
+                       if (proposedID != null && proposedID.Trim ().Length != 0)
                                return proposedID;
 
                        return "_bctrl_" + nextID++;
                }
 
+               internal string GetNextLocalVariableName (string baseName)
+               {
+                       localVariableCount++;
+                       return baseName + localVariableCount.ToString ();
+               }
+               
                internal virtual ControlBuilder CreateSubBuilder (string tagid,
-                                                                 Hashtable atts,
+                                                                 IDictionary atts,
                                                                  Type childType,
                                                                  TemplateParser parser,
                                                                  ILocation location)
                {
                        ControlBuilder childBuilder = null;
                        if (childrenAsProperties) {
-                               if (defaultPropertyBuilder == null) {
+                               if (defaultPropertyBuilder == null)
                                        childBuilder = CreatePropertyBuilder (tagid, parser, atts);
-                               } else {
-                                       childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
-                                                                                       null, parser, location);
+                               else {
+                                       if (String.Compare (defaultPropertyBuilder.TagName, tagid, true, Helpers.InvariantCulture) == 0) {
+                                               // The child tag is the same what our default property name. Act as if there was
+                                               // no default property builder, or otherwise we'll end up with invalid nested
+                                               // builder call.
+                                               defaultPropertyBuilder = null;
+                                               childBuilder = CreatePropertyBuilder (tagid, parser, atts);
+                                       } else {
+                                               Type ct = ControlType;
+                                               MemberInfo[] mems = ct != null ? ct.GetMember (tagid, MemberTypes.Property, FlagsNoCase) : null;
+                                               PropertyInfo prop = mems != null && mems.Length > 0 ? mems [0] as PropertyInfo : null;
+
+                                               if (prop != null && typeof (ITemplate).IsAssignableFrom (prop.PropertyType)) {
+                                                       childBuilder = CreatePropertyBuilder (tagid, parser, atts);
+                                                       defaultPropertyBuilder = null;
+                                               } else
+                                                       childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts, null, parser, location);
+                                       }
                                }
+
                                return childBuilder;
                        }
 
+                       if (String.Compare (tagName, tagid, true, Helpers.InvariantCulture) == 0)
+                               return null;
+                       
                        childType = GetChildControlType (tagid, atts);
                        if (childType == null)
                                return null;
@@ -494,12 +784,21 @@ namespace System.Web.UI {
                                }
                        }
                }
-#if NET_2_0
+
                [MonoTODO ("unsure, lack documentation")]
                public virtual object BuildObject ()
                {
                        return CreateInstance ();
                }
+               
+               public virtual void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,
+                                                        CodeTypeDeclaration baseType,
+                                                        CodeTypeDeclaration derivedType,
+                                                        CodeMemberMethod buildMethod,
+                                                        CodeMemberMethod dataBindingMethod)
+               {
+                       // nothing to do
+               }
 
                internal void ResetState()
                {
@@ -514,6 +813,5 @@ namespace System.Web.UI {
                                }
                        }
                }
-#endif
        }
 }