2 // System.Web.UI.ControlBuilder.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Marek Habersack <mhabersack@novell.com>
9 // (C) 2002, 2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Configuration;
35 using System.Globalization;
36 using System.Reflection;
37 using System.Security.Permissions;
38 using System.Web.Compilation;
39 using System.Web.Configuration;
41 using System.Web.UI.WebControls;
42 using System.Web.Util;
44 using _Location = System.Web.Compilation.Location;
46 namespace System.Web.UI {
49 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
50 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
51 public class ControlBuilder
53 internal static readonly BindingFlags FlagsNoCase = BindingFlags.Public |
54 BindingFlags.Instance |
56 BindingFlags.IgnoreCase;
58 ControlBuilder myNamingContainer;
59 TemplateParser parser;
61 ControlBuilder parentBuilder;
64 string originalTagName;
69 bool childrenAsProperties;
70 bool isIParserAccessor = true;
72 ControlBuilder defaultPropertyBuilder;
74 ArrayList templateChildren;
77 bool haveParserVariable;
78 CodeMemberMethod method;
79 CodeStatementCollection methodStatements;
80 CodeMemberMethod renderMethod;
86 public ControlBuilder ()
90 internal ControlBuilder (TemplateParser parser,
91 ControlBuilder parentBuilder,
97 string sourceFileName)
100 this.parser = parser;
101 this.parserType = parser != null ? parser.GetType () : null;
102 this.parentBuilder = parentBuilder;
104 this.tagName = tagName;
106 this.attribs = attribs;
108 this.fileName = sourceFileName;
111 internal void EnsureOtherTags ()
113 if (otherTags == null)
114 otherTags = new ArrayList ();
117 internal ControlBuilder ParentBuilder {
118 get { return parentBuilder; }
121 internal IDictionary Attributes {
122 get { return attribs; }
127 set { line = value; }
130 internal string FileName {
131 get { return fileName; }
132 set { fileName = value; }
135 internal ControlBuilder DefaultPropertyBuilder {
136 get { return defaultPropertyBuilder; }
139 internal bool HaveParserVariable {
140 get { return haveParserVariable; }
141 set { haveParserVariable = value; }
144 internal CodeMemberMethod Method {
145 get { return method; }
146 set { method = value; }
149 internal CodeMemberMethod DataBindingMethod {
154 internal CodeStatementCollection MethodStatements {
155 get { return methodStatements; }
156 set { methodStatements = value; }
159 internal CodeMemberMethod RenderMethod {
160 get { return renderMethod; }
161 set { renderMethod = value; }
164 internal int RenderIndex {
165 get { return renderIndex; }
168 internal bool IsProperty {
169 get { return isProperty; }
172 internal ILocation Location {
173 get { return location; }
174 set { location = new _Location (value); }
177 internal ArrayList OtherTags {
178 get { return otherTags; }
181 public Type ControlType {
185 protected bool FChildrenAsProperties {
186 get { return childrenAsProperties; }
189 protected bool FIsNonParserAccessor {
190 get { return !isIParserAccessor; }
193 public bool HasAspCode {
194 get { return hasAspCode; }
202 internal ArrayList Children {
203 get { return children; }
206 internal ArrayList TemplateChildren {
207 get { return templateChildren; }
210 internal void SetControlType (Type t)
215 protected bool InDesigner {
216 get { return false; }
219 public Type NamingContainerType {
221 ControlBuilder cb = myNamingContainer;
224 return typeof (Control);
226 return cb.ControlType;
230 internal bool IsNamingContainer {
235 return typeof (INamingContainer).IsAssignableFrom (type);
239 ControlBuilder MyNamingContainer {
241 if (myNamingContainer == null) {
242 Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
244 if (parentBuilder == null && controlType == null)
245 myNamingContainer = null;
246 else if (parentBuilder is TemplateBuilder)
247 myNamingContainer = parentBuilder;
248 else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
249 myNamingContainer = parentBuilder;
251 myNamingContainer = parentBuilder.MyNamingContainer;
254 return myNamingContainer;
258 public virtual Type BindingContainerType {
260 ControlBuilder cb = (this is TemplateBuilder && !(this is RootBuilder)) ? this : MyNamingContainer;
263 if (this is RootBuilder && parserType == typeof (PageParser))
264 return typeof (Page);
266 return typeof (Control);
269 if (cb != this && cb is ContentBuilderInternal && !typeof (INonBindingContainer).IsAssignableFrom (cb.BindingContainerType))
270 return cb.BindingContainerType;
273 if (cb is TemplateBuilder) {
274 ct = ((TemplateBuilder) cb).ContainerType;
275 if (typeof (INonBindingContainer).IsAssignableFrom (ct))
276 return MyNamingContainer.BindingContainerType;
283 return typeof (Control);
285 if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
286 return MyNamingContainer.BindingContainerType;
293 return typeof (Control);
295 if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
296 return MyNamingContainer.BindingContainerType;
298 return cb.ControlType;
302 internal TemplateBuilder ParentTemplateBuilder {
304 if (parentBuilder == null)
306 else if (parentBuilder is TemplateBuilder)
307 return (TemplateBuilder) parentBuilder;
309 return parentBuilder.ParentTemplateBuilder;
313 protected TemplateParser Parser {
314 get { return parser; }
317 public string TagName {
318 get { return tagName; }
321 internal string OriginalTagName {
323 if (originalTagName == null || originalTagName.Length == 0)
325 return originalTagName;
329 internal RootBuilder Root {
331 if (typeof (RootBuilder).IsAssignableFrom (GetType ()))
332 return (RootBuilder) this;
334 return (RootBuilder) parentBuilder.Root;
338 internal bool ChildrenAsProperties {
339 get { return childrenAsProperties; }
342 internal string GetAttribute (string name)
347 return attribs [name] as string;
350 internal void IncreaseRenderIndex ()
355 void AddChild (object child)
357 if (children == null)
358 children = new ArrayList ();
360 children.Add (child);
361 ControlBuilder cb = child as ControlBuilder;
362 if (cb != null && cb is TemplateBuilder) {
363 if (templateChildren == null)
364 templateChildren = new ArrayList ();
365 templateChildren.Add (child);
371 string tag = cb != null ? cb.TagName : null;
372 if (String.IsNullOrEmpty (tag))
375 RootBuilder rb = Root;
376 AspComponentFoundry foundry = rb != null ? rb.Foundry : null;
379 AspComponent component = foundry.GetComponent (tag);
380 if (component == null || !component.FromConfig)
383 parser.AddImport (component.Namespace);
384 parser.AddDependency (component.Source);
387 public virtual bool AllowWhitespaceLiterals ()
392 public virtual void AppendLiteralString (string s)
394 if (s == null || s.Length == 0)
397 if (childrenAsProperties || !isIParserAccessor) {
398 if (defaultPropertyBuilder != null) {
399 defaultPropertyBuilder.AppendLiteralString (s);
400 } else if (s.Trim ().Length != 0) {
401 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
402 tagName, GetType (), s));
408 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
411 if (HtmlDecodeLiterals ())
412 s = HttpUtility.HtmlDecode (s);
417 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
419 subBuilder.OnAppendToParentBuilder (this);
421 subBuilder.parentBuilder = this;
422 if (childrenAsProperties) {
423 AppendToProperty (subBuilder);
427 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
428 AppendCode (subBuilder);
432 AddChild (subBuilder);
435 void AppendToProperty (ControlBuilder subBuilder)
437 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
438 throw new HttpException ("Code render not supported here.");
440 if (defaultPropertyBuilder != null) {
441 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
445 AddChild (subBuilder);
448 void AppendCode (ControlBuilder subBuilder)
450 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
451 throw new HttpException ("Code render not supported here.");
453 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
456 AddChild (subBuilder);
459 public virtual void CloseControl ()
463 static Type MapTagType (Type tagType)
468 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
472 TagMapCollection tags = ps.TagMapping;
473 if (tags == null || tags.Count == 0)
476 string tagTypeName = tagType.ToString ();
477 Type mappedType, originalType;
478 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
482 foreach (TagMapInfo tmi in tags) {
487 originalTypeName = tmi.TagType;
488 originalType = HttpApplication.LoadType (originalTypeName);
489 if (originalType == null)
493 } catch (Exception ex) {
498 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
500 if (originalTypeName == tagTypeName) {
501 mappedTypeName = tmi.MappedTagType;
506 mappedType = HttpApplication.LoadType (mappedTypeName);
507 if (mappedType == null)
511 } catch (Exception ex) {
517 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
520 if (!mappedType.IsSubclassOf (originalType))
521 throw new ConfigurationErrorsException (
522 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
531 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
532 ControlBuilder parentBuilder,
538 string sourceFileName)
541 Type tagType = MapTagType (type);
542 ControlBuilder builder;
543 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
544 if (atts != null && atts.Length > 0) {
545 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
546 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
548 builder = new ControlBuilder ();
551 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
553 builder.fileName = sourceFileName;
557 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
562 public virtual bool HasBody ()
567 public virtual bool HtmlDecodeLiterals ()
572 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
577 if ((idx = propName.IndexOf (':')) >= 0)
578 propertyName = propName.Substring (idx + 1);
580 propertyName = propName;
582 PropertyInfo prop = type.GetProperty (propertyName, FlagsNoCase);
584 string msg = String.Format ("Property {0} not found in type {1}", propertyName, type);
585 throw new HttpException (msg);
588 Type propType = prop.PropertyType;
589 ControlBuilder builder = null;
590 if (typeof (ICollection).IsAssignableFrom (propType)) {
591 builder = new CollectionBuilder ();
592 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
593 builder = new TemplateBuilder (prop);
594 } else if (typeof (string) == propType) {
595 builder = new StringPropertyBuilder (prop.Name);
597 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
598 null, atts, line, fileName);
599 builder.isProperty = true;
601 builder.originalTagName = propName;
605 builder.Init (parser, this, null, prop.Name, null, atts);
606 builder.fileName = fileName;
608 builder.isProperty = true;
610 builder.originalTagName = propName;
614 public virtual void Init (TemplateParser parser,
615 ControlBuilder parentBuilder,
621 this.parser = parser;
623 this.Location = parser.Location;
625 this.parentBuilder = parentBuilder;
627 this.tagName = tagName;
629 this.attribs = attribs;
633 if (this is TemplateBuilder)
636 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
638 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
639 isIParserAccessor = false;
640 childrenAsProperties = true;
641 } else if (atts.Length > 0) {
642 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
643 childrenAsProperties = att.ChildrenAsProperties;
644 if (childrenAsProperties && att.DefaultProperty.Length != 0)
645 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
650 public virtual bool NeedsTagInnerText ()
655 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
657 if (defaultPropertyBuilder == null)
660 ControlBuilder old = defaultPropertyBuilder;
661 defaultPropertyBuilder = null;
662 AppendSubBuilder (old);
665 internal void SetTagName (string name)
670 public virtual void SetTagInnerText (string text)
674 internal string GetNextID (string proposedID)
676 if (proposedID != null && proposedID.Trim ().Length != 0)
679 return "_bctrl_" + nextID++;
682 internal virtual ControlBuilder CreateSubBuilder (string tagid,
685 TemplateParser parser,
688 ControlBuilder childBuilder = null;
689 if (childrenAsProperties) {
690 if (defaultPropertyBuilder == null)
691 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
693 if (String.Compare (defaultPropertyBuilder.TagName, tagid, true, Helpers.InvariantCulture) == 0) {
694 // The child tag is the same what our default property name. Act as if there was
695 // no default property builder, or otherwise we'll end up with invalid nested
697 defaultPropertyBuilder = null;
698 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
700 Type ct = ControlType;
701 MemberInfo[] mems = ct != null ? ct.GetMember (tagid, MemberTypes.Property, FlagsNoCase) : null;
702 PropertyInfo prop = mems != null && mems.Length > 0 ? mems [0] as PropertyInfo : null;
704 if (prop != null && typeof (ITemplate).IsAssignableFrom (prop.PropertyType)) {
705 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
706 defaultPropertyBuilder = null;
708 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts, null, parser, location);
715 if (String.Compare (tagName, tagid, true, Helpers.InvariantCulture) == 0)
718 childType = GetChildControlType (tagid, atts);
719 if (childType == null)
722 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
723 location.BeginLine, location.Filename);
728 internal virtual object CreateInstance ()
730 // HtmlGenericControl, HtmlTableCell...
731 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
732 object [] args = null;
733 if (atts != null && atts.Length > 0) {
734 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
736 args = new object [] {tagName};
739 return Activator.CreateInstance (type, args);
742 internal virtual void CreateChildren (object parent)
744 if (children == null || children.Count == 0)
747 IParserAccessor parser = parent as IParserAccessor;
751 foreach (object o in children) {
753 parser.AddParsedSubObject (new LiteralControl ((string) o));
755 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
760 [MonoTODO ("unsure, lack documentation")]
761 public virtual object BuildObject ()
763 return CreateInstance ();
766 public virtual void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,
767 CodeTypeDeclaration baseType,
768 CodeTypeDeclaration derivedType,
769 CodeMemberMethod buildMethod,
770 CodeMemberMethod dataBindingMethod)
775 internal void ResetState()
778 haveParserVariable = false;
780 if (Children != null) {
781 foreach (object child in Children) {
782 ControlBuilder cb = child as ControlBuilder;