Implemented a few missing properties
[mono.git] / mcs / class / System.Web / System.Web.UI / ControlBuilder.cs
old mode 100755 (executable)
new mode 100644 (file)
index 7acb206..67066a3
@@ -1,36 +1,87 @@
 //
 // System.Web.UI.ControlBuilder.cs
 //
-// Duncan Mak  (duncan@ximian.com)
+// Authors:
+//     Duncan Mak  (duncan@ximian.com)
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //
-// (C) Ximian, Inc.
+// (C) 2002, 2003 Ximian, Inc. (http://www.ximian.com)
+// Copyright (C) 2005 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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.Collections;
+using System.CodeDom;
+using System.Reflection;
+using System.Security.Permissions;
+using System.Web.Compilation;
 
 namespace System.Web.UI {
 
+       // CAS
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public class ControlBuilder
        {
+               internal static BindingFlags flagsNoCase = BindingFlags.Public |
+                                                          BindingFlags.Instance |
+                                                          BindingFlags.Static |
+                                                          BindingFlags.IgnoreCase;
+
                TemplateParser parser;
-               ControlBuilder parentBuilder;
+               internal ControlBuilder parentBuilder;
                Type type;             
                string tagName;
                string id;
-               IDictionary attribs;
-               int line;
-               string fileName;
+               internal IDictionary attribs;
+               internal int line;
+               internal string fileName;
+               bool childrenAsProperties;
+               bool isIParserAccessor = true;
+               bool hasAspCode;
+               internal ControlBuilder defaultPropertyBuilder;
+               ArrayList children;
+               static int nextID;
+
+               internal bool haveParserVariable;
+               internal CodeMemberMethod method;
+               internal CodeStatementCollection methodStatements;
+               internal CodeMemberMethod renderMethod;
+               internal int renderIndex;
+               internal bool isProperty;
+               internal ILocation location;
+               ArrayList otherTags;
 
                public ControlBuilder ()
                {
                }
 
-
-               internal ControlBuilder (
-                       TemplateParser parser, ControlBuilder parentBuilder,
-                       Type type, string tagName, string id,
-                       IDictionary attribs, int line, string sourceFileName)
+               internal ControlBuilder (TemplateParser parser,
+                                        ControlBuilder parentBuilder,
+                                        Type type,
+                                        string tagName,
+                                        string id,
+                                        IDictionary attribs,
+                                        int line,
+                                        string sourceFileName)
 
                {
                        this.parser = parser;
@@ -43,39 +94,99 @@ namespace System.Web.UI {
                        this.fileName = sourceFileName;
                }
 
+               internal void EnsureOtherTags ()
+               {
+                       if (otherTags == null)
+                               otherTags = new ArrayList ();
+               }
+               
+               internal ArrayList OtherTags {
+                       get { return otherTags; }
+               }
+
                public Type ControlType {
                        get { return type; }
                }
 
-               [MonoTODO]
-               public bool FChildrenAsProperties {
-                       get { return false; }
+               protected bool FChildrenAsProperties {
+                       get { return childrenAsProperties; }
                }
 
-               [MonoTODO]
-               public bool FIsNonParserAccessor {
-                       get { return false; }
+               protected bool FIsNonParserAccessor {
+                       get { return !isIParserAccessor; }
                }
 
-               [MonoTODO]
                public bool HasAspCode {
-                       get { return false; }
+                       get { return hasAspCode; }
                }
 
                public string ID {
                        get { return id; }
-
                        set { id = value; }
                }
 
-               [MonoTODO]
-               public bool InDesigner {
+               internal ArrayList Children {
+                       get { return children; }
+               }
+
+               internal void SetControlType (Type t)
+               {
+                       type = t;
+               }
+               
+               protected bool InDesigner {
                        get { return false; }
                }
 
-               [MonoTODO]
                public Type NamingContainerType {
-                       get { return null; }
+                       get {
+                               if (parentBuilder == null)
+                                       return typeof (Control);
+
+                               Type ptype = parentBuilder.ControlType;
+                               if (ptype == null)
+                                       return parentBuilder.NamingContainerType;
+
+                               if (!typeof (INamingContainer).IsAssignableFrom (ptype))
+                                       return parentBuilder.NamingContainerType;
+
+                               return ptype;
+                       }
+               }
+
+#if NET_2_0
+               public virtual
+#else
+               internal
+#endif
+               Type BindingContainerType {
+                       get {
+                               if (parentBuilder == null)
+                                       return typeof (Control);
+                                       
+                               if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
+                                       return ((TemplateBuilder)parentBuilder).ContainerType;
+
+                               Type ptype = parentBuilder.ControlType;
+                               if (ptype == null)
+                                       return parentBuilder.BindingContainerType;
+
+                               if (!typeof (INamingContainer).IsAssignableFrom (ptype))
+                                       return parentBuilder.BindingContainerType;
+
+                               return ptype;
+                       }
+               }
+
+               internal TemplateBuilder ParentTemplateBuilder {
+                       get {
+                               if (parentBuilder == null)
+                                       return null;
+                               else if (parentBuilder is TemplateBuilder)
+                                       return (TemplateBuilder) parentBuilder;
+                               else
+                                       return parentBuilder.ParentTemplateBuilder;
+                       }
                }
 
                protected TemplateParser Parser {
@@ -86,82 +197,323 @@ namespace System.Web.UI {
                        get { return tagName; }
                }
 
-               [MonoTODO]
+               internal RootBuilder Root {
+                       get {
+                               if (GetType () == typeof (RootBuilder))
+                                       return (RootBuilder) this;
+
+                               return (RootBuilder) parentBuilder.Root;
+                       }
+               }
+
+               internal bool ChildrenAsProperties {
+                       get { return childrenAsProperties; }
+               }
+               
                public virtual bool AllowWhitespaceLiterals ()
                {
-                       return false;
+                       return true;
                }
 
-               [MonoTODO]
                public virtual void AppendLiteralString (string s)
                {
-                       throw new NotImplementedException ();
+                       if (s == null || s == "")
+                               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 + "\"");
+                               }
+
+                               return;
+                       }
+                       
+                       if (!AllowWhitespaceLiterals () && s.Trim () == "")
+                               return;
+
+                       if (HtmlDecodeLiterals ())
+                               s = HttpUtility.HtmlDecode (s);
+
+                       if (children == null)
+                               children = new ArrayList ();
+
+                       children.Add (s);
                }
 
-               [MonoTODO]
                public virtual void AppendSubBuilder (ControlBuilder subBuilder)
                {
-                       throw new NotImplementedException ();
+                       subBuilder.OnAppendToParentBuilder (this);
+                       
+                       subBuilder.parentBuilder = this;
+                       if (childrenAsProperties) {
+                               AppendToProperty (subBuilder);
+                               return;
+                       }
+
+                       if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
+                               AppendCode (subBuilder);
+                               return;
+                       }
+
+                       if (children == null)
+                               children = new ArrayList ();
+
+                       children.Add (subBuilder);
+               }
+
+               void AppendToProperty (ControlBuilder subBuilder)
+               {
+                       if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
+                               throw new HttpException ("Code render not supported here.");
+
+                       if (defaultPropertyBuilder != null) {
+                               defaultPropertyBuilder.AppendSubBuilder (subBuilder);
+                               return;
+                       }
+
+                       if (children == null)
+                               children = new ArrayList ();
+
+                       children.Add (subBuilder);
+               }
+
+               void AppendCode (ControlBuilder subBuilder)
+               {
+                       if (type != null && !(typeof (Control).IsAssignableFrom (type)))
+                               throw new HttpException ("Code render not supported here.");
+
+                       if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
+                               hasAspCode = true;
+
+                       if (children == null)
+                               children = new ArrayList ();
+
+                       children.Add (subBuilder);
                }
 
-               [MonoTODO]
                public virtual void CloseControl ()
                {
                }
 
-               [MonoTODO]
-               public static ControlBuilder CreateBuilderFromType (
-                       TemplateParser parser, ControlBuilder parentBuilder,
-                       Type type, string tagName, string id,
-                       IDictionary attribs, int line, string sourceFileName)
+               public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
+                                                                   ControlBuilder parentBuilder,
+                                                                   Type type,
+                                                                   string tagName,
+                                                                   string id,
+                                                                   IDictionary attribs,
+                                                                   int line,
+                                                                   string sourceFileName)
                {
-                       return new ControlBuilder (parser, parentBuilder, type,
-                                                  tagName, id, attribs, line, sourceFileName);
+                       ControlBuilder  builder;
+                       object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
+                       if (atts != null && atts.Length > 0) {
+                               ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
+                               builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
+                       } else {
+                               builder = new ControlBuilder ();
+                       }
+
+                       builder.Init (parser, parentBuilder, type, tagName, id, attribs);
+                       builder.line = line;
+                       builder.fileName = sourceFileName;
+                       return builder;
                }
 
-               [MonoTODO]
                public virtual Type GetChildControlType (string tagName, IDictionary attribs)
                {
-                       return attribs [tagName] as Type;
+                       return null;
                }
 
-               [MonoTODO]
                public virtual bool HasBody ()
                {
-                       return false;
+                       return true;
                }
 
-               [MonoTODO]
                public virtual bool HtmlDecodeLiterals ()
                {
                        return false;
                }
 
-               [MonoTODO]
-               public virtual void Init (
-                       TemplateParser parser, ControlBuilder parentBuilder,
-                       Type type, string tagName, string id, IDictionary attribs)
+               ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
+               {
+                       PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
+                       if (prop == null) {
+                               string msg = String.Format ("Property {0} not found in type {1}", propName, type);
+                               throw new HttpException (msg);
+                       }
+
+                       Type propType = prop.PropertyType;
+                       ControlBuilder builder = null;
+                       if (typeof (ICollection).IsAssignableFrom (propType)) {
+                               builder = new CollectionBuilder ();
+                       } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
+                               builder = new TemplateBuilder (prop);
+                       } else if (typeof (string) == propType) {
+                               builder = new StringPropertyBuilder (prop.Name);
+                       } else {
+                               builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
+                                                                null, atts, line, fileName);
+                               builder.isProperty = true;
+                               return builder;
+                       }
+
+                       builder.Init (parser, this, null, prop.Name, null, atts);
+                       builder.fileName = fileName;
+                       builder.line = line;
+                       builder.isProperty = true;
+                       return builder;
+               }
+               
+               public virtual void Init (TemplateParser parser,
+                                         ControlBuilder parentBuilder,
+                                         Type type,
+                                         string tagName,
+                                         string id,
+                                         IDictionary attribs)
                {
-                       throw new NotImplementedException ();
+                       this.parser = parser;
+                       if (parser != null)
+                               this.location = parser.Location;
+
+                       this.parentBuilder = parentBuilder;
+                       this.type = type;
+                       this.tagName = tagName;
+                       this.id = id;
+                       this.attribs = attribs;
+                       if (type == null)
+                               return;
+
+                       if (this is TemplateBuilder)
+                               return;
+
+                       object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
+                       
+                       if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
+                               isIParserAccessor = false;
+                               childrenAsProperties = true;
+                       } else if (atts.Length > 0) {
+                               ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
+                               childrenAsProperties = att.ChildrenAsProperties;
+                               if (childrenAsProperties && att.DefaultProperty != "") {
+                                       defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
+                                                                                       parser, null);
+                               }
+                       }
                }
 
-               [MonoTODO]
                public virtual bool NeedsTagInnerText ()
                {
-                       throw new NotImplementedException ();
+                       return false;
                }
 
-               [MonoTODO]
-               public virtual void OnAppendToParentBuilder ()
+               public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
                {
-                       throw new NotImplementedException ();
+                       if (defaultPropertyBuilder == null)
+                               return;
+
+                       ControlBuilder old = defaultPropertyBuilder;
+                       defaultPropertyBuilder = null;
+                       AppendSubBuilder (old);
                }
 
-               [MonoTODO]
+               internal void SetTagName (string name)
+               {
+                       tagName = name;
+               }
+               
                public virtual void SetTagInnerText (string text)
                {
-                       throw new NotImplementedException ();
                }
+
+               internal string GetNextID (string proposedID)
+               {
+                       if (proposedID != null && proposedID.Trim () != "")
+                               return proposedID;
+
+                       return "_bctrl_" + nextID++;
+               }
+
+               internal virtual ControlBuilder CreateSubBuilder (string tagid,
+                                                                 Hashtable atts,
+                                                                 Type childType,
+                                                                 TemplateParser parser,
+                                                                 ILocation location)
+               {
+                       ControlBuilder childBuilder = null;
+                       if (childrenAsProperties) {
+                               if (defaultPropertyBuilder == null) {
+                                       childBuilder = CreatePropertyBuilder (tagid, parser, atts);
+                               } else {
+                                       childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
+                                                                                       null, parser, location);
+                               }
+                               return childBuilder;
+                       }
+
+                       childType = GetChildControlType (tagid, atts);
+                       if (childType == null)
+                               return null;
+
+                       childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
+                                                             location.BeginLine, location.Filename);
+
+                       return childBuilder;
+               }
+
+               internal virtual object CreateInstance ()
+               {
+                       // HtmlGenericControl, HtmlTableCell...
+                       object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
+                       object [] args = null;
+                       if (atts != null && atts.Length > 0) {
+                               ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
+                               if (att.NeedsTag)
+                                       args = new object [] {tagName};
+                       }
+
+                       return Activator.CreateInstance (type, args);
+               }
+
+               internal virtual void CreateChildren (object parent) 
+               {
+                       if (children == null || children.Count == 0)
+                               return;
+
+                       IParserAccessor parser = parent as IParserAccessor;
+                       if (parser == null)
+                               return;
+
+                       foreach (object o in children) {
+                               if (o is string) {
+                                       parser.AddParsedSubObject (new LiteralControl ((string) o));
+                               } else {
+                                       parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
+                               }
+                       }
+               }
+#if NET_2_0
+               [MonoTODO ("unsure, lack documentation")]
+               public virtual object BuildObject ()
+               {
+                       return CreateInstance ();
+               }
+
+               internal void ResetState()
+               {
+                       renderIndex = 0;
+                       haveParserVariable = false;
+
+                       if (Children != null) {
+                               foreach (object child in Children) {
+                                       ControlBuilder cb = child as ControlBuilder;
+                                       if (cb != null)
+                                               cb.ResetState ();
+                               }
+                       }
+               }
+#endif
        }
 }
-