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;
271 if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
272 return MyNamingContainer.BindingContainerType;
278 if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
279 return MyNamingContainer.BindingContainerType;
281 return cb.ControlType;
285 internal TemplateBuilder ParentTemplateBuilder {
287 if (parentBuilder == null)
289 else if (parentBuilder is TemplateBuilder)
290 return (TemplateBuilder) parentBuilder;
292 return parentBuilder.ParentTemplateBuilder;
296 protected TemplateParser Parser {
297 get { return parser; }
300 public string TagName {
301 get { return tagName; }
304 internal string OriginalTagName {
306 if (originalTagName == null || originalTagName.Length == 0)
308 return originalTagName;
312 internal RootBuilder Root {
314 if (GetType () == typeof (RootBuilder))
315 return (RootBuilder) this;
317 return (RootBuilder) parentBuilder.Root;
321 internal bool ChildrenAsProperties {
322 get { return childrenAsProperties; }
325 internal string GetAttribute (string name)
330 return attribs [name] as string;
333 internal void IncreaseRenderIndex ()
338 void AddChild (object child)
340 if (children == null)
341 children = new ArrayList ();
343 children.Add (child);
344 ControlBuilder cb = child as ControlBuilder;
345 if (cb != null && cb is TemplateBuilder) {
346 if (templateChildren == null)
347 templateChildren = new ArrayList ();
348 templateChildren.Add (child);
355 string tag = cb != null ? cb.TagName : null;
356 if (String.IsNullOrEmpty (tag))
359 RootBuilder rb = Root;
360 AspComponentFoundry foundry = rb != null ? rb.Foundry : null;
363 AspComponent component = foundry.GetComponent (tag);
364 if (component == null || !component.FromConfig)
367 parser.AddImport (component.Namespace);
368 parser.AddDependency (component.Source);
372 public virtual bool AllowWhitespaceLiterals ()
377 public virtual void AppendLiteralString (string s)
379 if (s == null || s.Length == 0)
382 if (childrenAsProperties || !isIParserAccessor) {
383 if (defaultPropertyBuilder != null) {
384 defaultPropertyBuilder.AppendLiteralString (s);
385 } else if (s.Trim ().Length != 0) {
386 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
387 tagName, GetType (), s));
393 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
396 if (HtmlDecodeLiterals ())
397 s = HttpUtility.HtmlDecode (s);
402 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
404 subBuilder.OnAppendToParentBuilder (this);
406 subBuilder.parentBuilder = this;
407 if (childrenAsProperties) {
408 AppendToProperty (subBuilder);
412 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
413 AppendCode (subBuilder);
417 AddChild (subBuilder);
420 void AppendToProperty (ControlBuilder subBuilder)
422 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
423 throw new HttpException ("Code render not supported here.");
425 if (defaultPropertyBuilder != null) {
426 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
430 AddChild (subBuilder);
433 void AppendCode (ControlBuilder subBuilder)
435 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
436 throw new HttpException ("Code render not supported here.");
438 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
441 AddChild (subBuilder);
444 public virtual void CloseControl ()
449 static Type MapTagType (Type tagType)
454 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
458 TagMapCollection tags = ps.TagMapping;
459 if (tags == null || tags.Count == 0)
462 string tagTypeName = tagType.ToString ();
463 Type mappedType, originalType;
464 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
468 foreach (TagMapInfo tmi in tags) {
473 originalTypeName = tmi.TagType;
474 originalType = HttpApplication.LoadType (originalTypeName);
475 if (originalType == null)
479 } catch (Exception ex) {
484 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
486 if (originalTypeName == tagTypeName) {
487 mappedTypeName = tmi.MappedTagType;
492 mappedType = HttpApplication.LoadType (mappedTypeName);
493 if (mappedType == null)
497 } catch (Exception ex) {
503 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
506 if (!mappedType.IsSubclassOf (originalType))
507 throw new ConfigurationErrorsException (
508 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
518 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
519 ControlBuilder parentBuilder,
525 string sourceFileName)
530 tagType = MapTagType (type);
534 ControlBuilder builder;
535 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
536 if (atts != null && atts.Length > 0) {
537 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
538 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
540 builder = new ControlBuilder ();
543 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
545 builder.fileName = sourceFileName;
549 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
554 public virtual bool HasBody ()
559 public virtual bool HtmlDecodeLiterals ()
564 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
569 if ((idx = propName.IndexOf (':')) >= 0)
570 propertyName = propName.Substring (idx + 1);
572 propertyName = propName;
574 PropertyInfo prop = type.GetProperty (propertyName, FlagsNoCase);
576 string msg = String.Format ("Property {0} not found in type {1}", propertyName, type);
577 throw new HttpException (msg);
580 Type propType = prop.PropertyType;
581 ControlBuilder builder = null;
582 if (typeof (ICollection).IsAssignableFrom (propType)) {
583 builder = new CollectionBuilder ();
584 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
585 builder = new TemplateBuilder (prop);
586 } else if (typeof (string) == propType) {
587 builder = new StringPropertyBuilder (prop.Name);
589 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
590 null, atts, line, fileName);
591 builder.isProperty = true;
593 builder.originalTagName = propName;
597 builder.Init (parser, this, null, prop.Name, null, atts);
598 builder.fileName = fileName;
600 builder.isProperty = true;
602 builder.originalTagName = propName;
606 public virtual void Init (TemplateParser parser,
607 ControlBuilder parentBuilder,
613 this.parser = parser;
615 this.location = parser.Location;
617 this.parentBuilder = parentBuilder;
619 this.tagName = tagName;
621 this.attribs = attribs;
625 if (this is TemplateBuilder)
628 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
630 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
631 isIParserAccessor = false;
632 childrenAsProperties = true;
633 } else if (atts.Length > 0) {
634 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
635 childrenAsProperties = att.ChildrenAsProperties;
636 if (childrenAsProperties && att.DefaultProperty.Length != 0)
637 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
642 public virtual bool NeedsTagInnerText ()
647 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
649 if (defaultPropertyBuilder == null)
652 ControlBuilder old = defaultPropertyBuilder;
653 defaultPropertyBuilder = null;
654 AppendSubBuilder (old);
657 internal void SetTagName (string name)
662 public virtual void SetTagInnerText (string text)
666 internal string GetNextID (string proposedID)
668 if (proposedID != null && proposedID.Trim ().Length != 0)
671 return "_bctrl_" + nextID++;
674 internal virtual ControlBuilder CreateSubBuilder (string tagid,
677 TemplateParser parser,
680 ControlBuilder childBuilder = null;
681 if (childrenAsProperties) {
682 if (defaultPropertyBuilder == null)
683 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
685 if (String.Compare (defaultPropertyBuilder.TagName, tagid, true, CultureInfo.InvariantCulture) == 0) {
686 // The child tag is the same what our default property name. Act as if there was
687 // no default property builder, or otherwise we'll end up with invalid nested
689 defaultPropertyBuilder = null;
690 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
692 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
700 if (String.Compare (tagName, tagid, true, CultureInfo.InvariantCulture) == 0)
703 childType = GetChildControlType (tagid, atts);
704 if (childType == null)
707 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
708 location.BeginLine, location.Filename);
713 internal virtual object CreateInstance ()
715 // HtmlGenericControl, HtmlTableCell...
716 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
717 object [] args = null;
718 if (atts != null && atts.Length > 0) {
719 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
721 args = new object [] {tagName};
724 return Activator.CreateInstance (type, args);
727 internal virtual void CreateChildren (object parent)
729 if (children == null || children.Count == 0)
732 IParserAccessor parser = parent as IParserAccessor;
736 foreach (object o in children) {
738 parser.AddParsedSubObject (new LiteralControl ((string) o));
740 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
745 [MonoTODO ("unsure, lack documentation")]
746 public virtual object BuildObject ()
748 return CreateInstance ();
751 internal void ResetState()
754 haveParserVariable = false;
756 if (Children != null) {
757 foreach (object child in Children) {
758 ControlBuilder cb = child as ControlBuilder;