Merge pull request #2700 from akoeplinger/monotouch-mobile-static
[mono.git] / mcs / class / System.Web / System.Web.UI / ControlBuilder.cs
index eff0abd90e91a3af87be1afb9a2eb3fb377ef6c7..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 {
 
@@ -45,38 +50,42 @@ namespace System.Web.UI {
        [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public class ControlBuilder
        {
-               internal static BindingFlags flagsNoCase = BindingFlags.Public |
+               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;
-#if NET_2_0
-               static string privateBinPath;
-#endif
+
+               int localVariableCount = 0;
+               bool? isTemplate;
                
                public ControlBuilder ()
                {
@@ -93,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;
@@ -107,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; }
                }
@@ -137,6 +211,10 @@ namespace System.Web.UI {
                        get { return children; }
                }
 
+               internal ArrayList TemplateChildren {
+                       get { return templateChildren; }
+               }
+               
                internal void SetControlType (Type t)
                {
                        type = t;
@@ -157,6 +235,28 @@ namespace System.Web.UI {
                        }
                }
 
+               internal bool IsNamingContainer {
+                       get {
+                               if (type == null)
+                                       return false;
+
+                               return typeof (INamingContainer).IsAssignableFrom (type);
+                       }
+               }
+
+               internal bool IsTemplate {
+                       get {
+                               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) {
@@ -164,6 +264,8 @@ namespace System.Web.UI {
                                        
                                        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
@@ -174,22 +276,46 @@ namespace System.Web.UI {
                        }
                }
                        
-#if NET_2_0
-               public virtual
-#else
-               internal
-#endif
-               Type BindingContainerType {
+               public virtual Type BindingContainerType {
                        get {
-                               ControlBuilder cb = MyNamingContainer;
-                               if (cb == null)
+                               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 NET_2_0
-                               if (cb is ContentBuilderInternal)
+                               if (cb != this && cb is ContentBuilderInternal && !typeof (INonBindingContainer).IsAssignableFrom (cb.BindingContainerType))
                                        return cb.BindingContainerType;
-#endif
 
+                               Type ct;
+                               if (cb is TemplateBuilder) {
+                                       ct = ((TemplateBuilder) cb).ContainerType;
+                                       if (typeof (INonBindingContainer).IsAssignableFrom (ct))
+                                               return MyNamingContainer.BindingContainerType;
+                                       
+                                       if (ct != null)
+                                               return ct;
+
+                                       ct = cb.ControlType;
+                                       if (ct == null)
+                                               return typeof (Control);
+                                       
+                                       if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
+                                               return MyNamingContainer.BindingContainerType;
+
+                                       return ct;
+                               }
+
+                               ct = cb.ControlType;
+                               if (ct == null)
+                                       return typeof (Control);
+                               
+                               if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
+                                       return MyNamingContainer.BindingContainerType;
+                               
                                return cb.ControlType;
                        }
                }
@@ -213,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;
@@ -225,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 ()
                {
@@ -253,10 +432,7 @@ namespace System.Web.UI {
                        if (HtmlDecodeLiterals ())
                                s = HttpUtility.HtmlDecode (s);
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (s);
+                       AddChild (s);
                }
 
                public virtual void AppendSubBuilder (ControlBuilder subBuilder)
@@ -274,10 +450,7 @@ namespace System.Web.UI {
                                return;
                        }
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (subBuilder);
+                       AddChild (subBuilder);
                }
 
                void AppendToProperty (ControlBuilder subBuilder)
@@ -290,10 +463,7 @@ namespace System.Web.UI {
                                return;
                        }
 
-                       if (children == null)
-                               children = new ArrayList ();
-
-                       children.Add (subBuilder);
+                       AddChild (subBuilder);
                }
 
                void AppendCode (ControlBuilder subBuilder)
@@ -304,17 +474,13 @@ 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 ()
                {
                }
 
-#if NET_2_0            
                static Type MapTagType (Type tagType)
                {
                        if (tagType == null)
@@ -382,7 +548,6 @@ namespace System.Web.UI {
                        
                        return tagType;
                }
-#endif
 
                public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
                                                                    ControlBuilder parentBuilder,
@@ -393,14 +558,8 @@ namespace System.Web.UI {
                                                                    int line,
                                                                    string sourceFileName)
                {
-
-                       Type tagType;
-#if NET_2_0
-                       tagType = MapTagType (type);
-#else
-                       tagType = type;
-#endif
-                       ControlBuilder  builder;
+                       Type tagType = MapTagType (type);
+                       ControlBuilder builder;
                        object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
                        if (atts != null && atts.Length > 0) {
                                ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
@@ -429,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);
                        }
 
@@ -450,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;
                        }
 
@@ -457,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;
                }
                
@@ -469,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;
@@ -528,8 +701,14 @@ namespace System.Web.UI {
                        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)
@@ -539,21 +718,29 @@ namespace System.Web.UI {
                                if (defaultPropertyBuilder == null)
                                        childBuilder = CreatePropertyBuilder (tagid, parser, atts);
                                else {
-                                       if (defaultPropertyBuilder.TagName == tagid) {
+                                       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
-                                               childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
-                                                                                                       null, parser,
-                                                                                                       location);
+                                       } 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 (tagName == tagid)
+                       if (String.Compare (tagName, tagid, true, Helpers.InvariantCulture) == 0)
                                return null;
                        
                        childType = GetChildControlType (tagid, atts);
@@ -597,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()
                {
@@ -617,6 +813,5 @@ namespace System.Web.UI {
                                }
                        }
                }
-#endif
        }
 }