2 // System.Web.UI.ControlBuilder.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (C) 2002, 2003 Ximian, Inc. (http://www.ximian.com)
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
32 using System.Configuration;
34 using System.Globalization;
35 using System.Reflection;
36 using System.Security.Permissions;
37 using System.Web.Compilation;
38 using System.Web.Configuration;
40 using System.Web.UI.WebControls;
42 namespace System.Web.UI {
45 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
46 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
47 public class ControlBuilder
49 internal static readonly BindingFlags FlagsNoCase = BindingFlags.Public |
50 BindingFlags.Instance |
52 BindingFlags.IgnoreCase;
54 ControlBuilder myNamingContainer;
55 TemplateParser parser;
57 ControlBuilder parentBuilder;
60 string originalTagName;
65 bool childrenAsProperties;
66 bool isIParserAccessor = true;
68 ControlBuilder defaultPropertyBuilder;
70 ArrayList templateChildren;
73 bool haveParserVariable;
74 CodeMemberMethod method;
75 CodeStatementCollection methodStatements;
76 CodeMemberMethod renderMethod;
82 public ControlBuilder ()
86 internal ControlBuilder (TemplateParser parser,
87 ControlBuilder parentBuilder,
93 string sourceFileName)
97 this.parserType = parser != null ? parser.GetType () : null;
98 this.parentBuilder = parentBuilder;
100 this.tagName = tagName;
102 this.attribs = attribs;
104 this.fileName = sourceFileName;
107 internal void EnsureOtherTags ()
109 if (otherTags == null)
110 otherTags = new ArrayList ();
113 internal ControlBuilder ParentBuilder {
114 get { return parentBuilder; }
117 internal IDictionary Attributes {
118 get { return attribs; }
123 set { line = value; }
126 internal string FileName {
127 get { return fileName; }
128 set { fileName = value; }
131 internal ControlBuilder DefaultPropertyBuilder {
132 get { return defaultPropertyBuilder; }
135 internal bool HaveParserVariable {
136 get { return haveParserVariable; }
137 set { haveParserVariable = value; }
140 internal CodeMemberMethod Method {
141 get { return method; }
142 set { method = value; }
145 internal CodeStatementCollection MethodStatements {
146 get { return methodStatements; }
147 set { methodStatements = value; }
150 internal CodeMemberMethod RenderMethod {
151 get { return renderMethod; }
152 set { renderMethod = value; }
155 internal int RenderIndex {
156 get { return renderIndex; }
159 internal bool IsProperty {
160 get { return isProperty; }
163 internal ILocation Location {
164 get { return location; }
165 set { location = value; }
168 internal ArrayList OtherTags {
169 get { return otherTags; }
172 public Type ControlType {
176 protected bool FChildrenAsProperties {
177 get { return childrenAsProperties; }
180 protected bool FIsNonParserAccessor {
181 get { return !isIParserAccessor; }
184 public bool HasAspCode {
185 get { return hasAspCode; }
193 internal ArrayList Children {
194 get { return children; }
197 internal ArrayList TemplateChildren {
198 get { return templateChildren; }
201 internal void SetControlType (Type t)
206 protected bool InDesigner {
207 get { return false; }
210 public Type NamingContainerType {
212 ControlBuilder cb = myNamingContainer;
215 return typeof (Control);
217 return cb.ControlType;
221 ControlBuilder MyNamingContainer {
223 if (myNamingContainer == null) {
224 Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
226 if (parentBuilder == null && controlType == null)
227 myNamingContainer = null;
228 else if (parentBuilder is TemplateBuilder)
229 myNamingContainer = parentBuilder;
230 else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
231 myNamingContainer = parentBuilder;
233 myNamingContainer = parentBuilder.MyNamingContainer;
236 return myNamingContainer;
245 Type BindingContainerType {
247 ControlBuilder cb = (this is TemplateBuilder && !(this is RootBuilder)) ? this : MyNamingContainer;
250 if (this is RootBuilder && parserType == typeof (PageParser))
251 return typeof (Page);
253 return typeof (Control);
257 if (cb != this && cb is ContentBuilderInternal && !typeof (INonBindingContainer).IsAssignableFrom (cb.BindingContainerType))
258 return cb.BindingContainerType;
262 if (cb is TemplateBuilder) {
263 ct = ((TemplateBuilder) cb).ContainerType;
264 if (typeof (INonBindingContainer).IsAssignableFrom (ct))
265 return MyNamingContainer.BindingContainerType;
272 return typeof (Control);
274 if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
275 return MyNamingContainer.BindingContainerType;
282 return typeof (Control);
284 if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
285 return MyNamingContainer.BindingContainerType;
287 return cb.ControlType;
291 internal TemplateBuilder ParentTemplateBuilder {
293 if (parentBuilder == null)
295 else if (parentBuilder is TemplateBuilder)
296 return (TemplateBuilder) parentBuilder;
298 return parentBuilder.ParentTemplateBuilder;
302 protected TemplateParser Parser {
303 get { return parser; }
306 public string TagName {
307 get { return tagName; }
310 internal string OriginalTagName {
312 if (originalTagName == null || originalTagName.Length == 0)
314 return originalTagName;
318 internal RootBuilder Root {
320 if (GetType () == typeof (RootBuilder))
321 return (RootBuilder) this;
323 return (RootBuilder) parentBuilder.Root;
327 internal bool ChildrenAsProperties {
328 get { return childrenAsProperties; }
331 internal string GetAttribute (string name)
336 return attribs [name] as string;
339 internal void IncreaseRenderIndex ()
344 void AddChild (object child)
346 if (children == null)
347 children = new ArrayList ();
349 children.Add (child);
350 ControlBuilder cb = child as ControlBuilder;
351 if (cb != null && cb is TemplateBuilder) {
352 if (templateChildren == null)
353 templateChildren = new ArrayList ();
354 templateChildren.Add (child);
361 string tag = cb != null ? cb.TagName : null;
362 if (String.IsNullOrEmpty (tag))
365 RootBuilder rb = Root;
366 AspComponentFoundry foundry = rb != null ? rb.Foundry : null;
369 AspComponent component = foundry.GetComponent (tag);
370 if (component == null || !component.FromConfig)
373 parser.AddImport (component.Namespace);
374 parser.AddDependency (component.Source);
378 public virtual bool AllowWhitespaceLiterals ()
383 public virtual void AppendLiteralString (string s)
385 if (s == null || s.Length == 0)
388 if (childrenAsProperties || !isIParserAccessor) {
389 if (defaultPropertyBuilder != null) {
390 defaultPropertyBuilder.AppendLiteralString (s);
391 } else if (s.Trim ().Length != 0) {
392 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
393 tagName, GetType (), s));
399 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
402 if (HtmlDecodeLiterals ())
403 s = HttpUtility.HtmlDecode (s);
408 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
410 subBuilder.OnAppendToParentBuilder (this);
412 subBuilder.parentBuilder = this;
413 if (childrenAsProperties) {
414 AppendToProperty (subBuilder);
418 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
419 AppendCode (subBuilder);
423 AddChild (subBuilder);
426 void AppendToProperty (ControlBuilder subBuilder)
428 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
429 throw new HttpException ("Code render not supported here.");
431 if (defaultPropertyBuilder != null) {
432 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
436 AddChild (subBuilder);
439 void AppendCode (ControlBuilder subBuilder)
441 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
442 throw new HttpException ("Code render not supported here.");
444 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
447 AddChild (subBuilder);
450 public virtual void CloseControl ()
455 static Type MapTagType (Type tagType)
460 PagesSection ps = WebConfigurationManager.GetWebApplicationSection ("system.web/pages") as PagesSection;
464 TagMapCollection tags = ps.TagMapping;
465 if (tags == null || tags.Count == 0)
468 string tagTypeName = tagType.ToString ();
469 Type mappedType, originalType;
470 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
474 foreach (TagMapInfo tmi in tags) {
479 originalTypeName = tmi.TagType;
480 originalType = HttpApplication.LoadType (originalTypeName);
481 if (originalType == null)
485 } catch (Exception ex) {
490 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
492 if (originalTypeName == tagTypeName) {
493 mappedTypeName = tmi.MappedTagType;
498 mappedType = HttpApplication.LoadType (mappedTypeName);
499 if (mappedType == null)
503 } catch (Exception ex) {
509 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
512 if (!mappedType.IsSubclassOf (originalType))
513 throw new ConfigurationErrorsException (
514 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
524 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
525 ControlBuilder parentBuilder,
531 string sourceFileName)
536 tagType = MapTagType (type);
540 ControlBuilder builder;
541 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
542 if (atts != null && atts.Length > 0) {
543 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
544 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
546 builder = new ControlBuilder ();
549 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
551 builder.fileName = sourceFileName;
555 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
560 public virtual bool HasBody ()
565 public virtual bool HtmlDecodeLiterals ()
570 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
575 if ((idx = propName.IndexOf (':')) >= 0)
576 propertyName = propName.Substring (idx + 1);
578 propertyName = propName;
580 PropertyInfo prop = type.GetProperty (propertyName, FlagsNoCase);
582 string msg = String.Format ("Property {0} not found in type {1}", propertyName, type);
583 throw new HttpException (msg);
586 Type propType = prop.PropertyType;
587 ControlBuilder builder = null;
588 if (typeof (ICollection).IsAssignableFrom (propType)) {
589 builder = new CollectionBuilder ();
590 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
591 builder = new TemplateBuilder (prop);
592 } else if (typeof (string) == propType) {
593 builder = new StringPropertyBuilder (prop.Name);
595 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
596 null, atts, line, fileName);
597 builder.isProperty = true;
599 builder.originalTagName = propName;
603 builder.Init (parser, this, null, prop.Name, null, atts);
604 builder.fileName = fileName;
606 builder.isProperty = true;
608 builder.originalTagName = propName;
612 public virtual void Init (TemplateParser parser,
613 ControlBuilder parentBuilder,
619 this.parser = parser;
621 this.location = parser.Location;
623 this.parentBuilder = parentBuilder;
625 this.tagName = tagName;
627 this.attribs = attribs;
631 if (this is TemplateBuilder)
634 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
636 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
637 isIParserAccessor = false;
638 childrenAsProperties = true;
639 } else if (atts.Length > 0) {
640 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
641 childrenAsProperties = att.ChildrenAsProperties;
642 if (childrenAsProperties && att.DefaultProperty.Length != 0)
643 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
648 public virtual bool NeedsTagInnerText ()
653 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
655 if (defaultPropertyBuilder == null)
658 ControlBuilder old = defaultPropertyBuilder;
659 defaultPropertyBuilder = null;
660 AppendSubBuilder (old);
663 internal void SetTagName (string name)
668 public virtual void SetTagInnerText (string text)
672 internal string GetNextID (string proposedID)
674 if (proposedID != null && proposedID.Trim ().Length != 0)
677 return "_bctrl_" + nextID++;
680 internal virtual ControlBuilder CreateSubBuilder (string tagid,
683 TemplateParser parser,
686 ControlBuilder childBuilder = null;
687 if (childrenAsProperties) {
688 if (defaultPropertyBuilder == null)
689 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
691 if (String.Compare (defaultPropertyBuilder.TagName, tagid, true, CultureInfo.InvariantCulture) == 0) {
692 // The child tag is the same what our default property name. Act as if there was
693 // no default property builder, or otherwise we'll end up with invalid nested
695 defaultPropertyBuilder = null;
696 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
698 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
706 if (String.Compare (tagName, tagid, true, CultureInfo.InvariantCulture) == 0)
709 childType = GetChildControlType (tagid, atts);
710 if (childType == null)
713 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
714 location.BeginLine, location.Filename);
719 internal virtual object CreateInstance ()
721 // HtmlGenericControl, HtmlTableCell...
722 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
723 object [] args = null;
724 if (atts != null && atts.Length > 0) {
725 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
727 args = new object [] {tagName};
730 return Activator.CreateInstance (type, args);
733 internal virtual void CreateChildren (object parent)
735 if (children == null || children.Count == 0)
738 IParserAccessor parser = parent as IParserAccessor;
742 foreach (object o in children) {
744 parser.AddParsedSubObject (new LiteralControl ((string) o));
746 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
751 [MonoTODO ("unsure, lack documentation")]
752 public virtual object BuildObject ()
754 return CreateInstance ();
758 public virtual void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,
759 CodeTypeDeclaration baseType,
760 CodeTypeDeclaration derivedType,
761 CodeMemberMethod buildMethod,
762 CodeMemberMethod dataBindingMethod)
764 throw new NotImplementedException ();
767 internal void ResetState()
770 haveParserVariable = false;
772 if (Children != null) {
773 foreach (object child in Children) {
774 ControlBuilder cb = child as ControlBuilder;