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 BindingFlags flagsNoCase = BindingFlags.Public |
50 BindingFlags.Instance |
52 BindingFlags.IgnoreCase;
54 ControlBuilder myNamingContainer;
55 TemplateParser parser;
56 internal ControlBuilder parentBuilder;
59 string originalTagName;
61 internal IDictionary attribs;
63 internal string fileName;
64 bool childrenAsProperties;
65 bool isIParserAccessor = true;
67 internal ControlBuilder defaultPropertyBuilder;
69 ArrayList templateChildren;
72 internal bool haveParserVariable;
73 internal CodeMemberMethod method;
74 internal CodeStatementCollection methodStatements;
75 internal CodeMemberMethod renderMethod;
76 internal int renderIndex;
77 internal bool isProperty;
78 internal ILocation location;
81 public ControlBuilder ()
85 internal ControlBuilder (TemplateParser parser,
86 ControlBuilder parentBuilder,
92 string sourceFileName)
96 this.parentBuilder = parentBuilder;
98 this.tagName = tagName;
100 this.attribs = attribs;
102 this.fileName = sourceFileName;
105 internal void EnsureOtherTags ()
107 if (otherTags == null)
108 otherTags = new ArrayList ();
111 internal ArrayList OtherTags {
112 get { return otherTags; }
115 public Type ControlType {
119 protected bool FChildrenAsProperties {
120 get { return childrenAsProperties; }
123 protected bool FIsNonParserAccessor {
124 get { return !isIParserAccessor; }
127 public bool HasAspCode {
128 get { return hasAspCode; }
136 internal ArrayList Children {
137 get { return children; }
140 internal ArrayList TemplateChildren {
141 get { return templateChildren; }
144 internal void SetControlType (Type t)
149 protected bool InDesigner {
150 get { return false; }
153 public Type NamingContainerType {
155 ControlBuilder cb = myNamingContainer;
158 return typeof (Control);
160 return cb.ControlType;
164 ControlBuilder MyNamingContainer {
166 if (myNamingContainer == null) {
167 Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
169 if (parentBuilder == null && controlType == null)
170 myNamingContainer = null;
171 else if (parentBuilder is TemplateBuilder)
172 myNamingContainer = parentBuilder;
173 else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
174 myNamingContainer = parentBuilder;
176 myNamingContainer = parentBuilder.MyNamingContainer;
179 return myNamingContainer;
188 Type BindingContainerType {
190 ControlBuilder cb = (this is TemplateBuilder && !(this is RootBuilder)) ? this : MyNamingContainer;
192 return typeof (Control);
195 if (cb != this && cb is ContentBuilderInternal && !typeof (INonBindingContainer).IsAssignableFrom (cb.BindingContainerType))
196 return cb.BindingContainerType;
199 if (cb is TemplateBuilder) {
200 Type ct = ((TemplateBuilder) cb).ContainerType;
201 if (typeof (INonBindingContainer).IsAssignableFrom (ct))
202 return MyNamingContainer.BindingContainerType;
208 if (typeof (INonBindingContainer).IsAssignableFrom (ct))
209 return MyNamingContainer.BindingContainerType;
214 return cb.ControlType;
218 internal TemplateBuilder ParentTemplateBuilder {
220 if (parentBuilder == null)
222 else if (parentBuilder is TemplateBuilder)
223 return (TemplateBuilder) parentBuilder;
225 return parentBuilder.ParentTemplateBuilder;
229 protected TemplateParser Parser {
230 get { return parser; }
233 public string TagName {
234 get { return tagName; }
237 internal string OriginalTagName {
239 if (originalTagName == null || originalTagName.Length == 0)
241 return originalTagName;
245 internal RootBuilder Root {
247 if (GetType () == typeof (RootBuilder))
248 return (RootBuilder) this;
250 return (RootBuilder) parentBuilder.Root;
254 internal bool ChildrenAsProperties {
255 get { return childrenAsProperties; }
258 void AddChild (object child)
260 if (children == null)
261 children = new ArrayList ();
263 children.Add (child);
264 if (child is TemplateBuilder) {
265 if (templateChildren == null)
266 templateChildren = new ArrayList ();
267 templateChildren.Add (child);
271 public virtual bool AllowWhitespaceLiterals ()
276 public virtual void AppendLiteralString (string s)
278 if (s == null || s.Length == 0)
281 if (childrenAsProperties || !isIParserAccessor) {
282 if (defaultPropertyBuilder != null) {
283 defaultPropertyBuilder.AppendLiteralString (s);
284 } else if (s.Trim ().Length != 0) {
285 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
286 tagName, GetType (), s));
292 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
295 if (HtmlDecodeLiterals ())
296 s = HttpUtility.HtmlDecode (s);
301 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
303 subBuilder.OnAppendToParentBuilder (this);
305 subBuilder.parentBuilder = this;
306 if (childrenAsProperties) {
307 AppendToProperty (subBuilder);
311 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
312 AppendCode (subBuilder);
316 AddChild (subBuilder);
319 void AppendToProperty (ControlBuilder subBuilder)
321 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
322 throw new HttpException ("Code render not supported here.");
324 if (defaultPropertyBuilder != null) {
325 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
329 AddChild (subBuilder);
332 void AppendCode (ControlBuilder subBuilder)
334 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
335 throw new HttpException ("Code render not supported here.");
337 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
340 AddChild (subBuilder);
343 public virtual void CloseControl ()
348 static Type MapTagType (Type tagType)
353 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
357 TagMapCollection tags = ps.TagMapping;
358 if (tags == null || tags.Count == 0)
361 string tagTypeName = tagType.ToString ();
362 Type mappedType, originalType;
363 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
367 foreach (TagMapInfo tmi in tags) {
372 originalTypeName = tmi.TagType;
373 originalType = HttpApplication.LoadType (originalTypeName);
374 if (originalType == null)
378 } catch (Exception ex) {
383 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
385 if (originalTypeName == tagTypeName) {
386 mappedTypeName = tmi.MappedTagType;
391 mappedType = HttpApplication.LoadType (mappedTypeName);
392 if (mappedType == null)
396 } catch (Exception ex) {
402 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
405 if (!mappedType.IsSubclassOf (originalType))
406 throw new ConfigurationErrorsException (
407 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
417 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
418 ControlBuilder parentBuilder,
424 string sourceFileName)
429 tagType = MapTagType (type);
433 ControlBuilder builder;
434 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
435 if (atts != null && atts.Length > 0) {
436 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
437 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
439 builder = new ControlBuilder ();
442 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
444 builder.fileName = sourceFileName;
448 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
453 public virtual bool HasBody ()
458 public virtual bool HtmlDecodeLiterals ()
463 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
468 if ((idx = propName.IndexOf (':')) >= 0)
469 propertyName = propName.Substring (idx + 1);
471 propertyName = propName;
473 PropertyInfo prop = type.GetProperty (propertyName, flagsNoCase);
475 string msg = String.Format ("Property {0} not found in type {1}", propertyName, type);
476 throw new HttpException (msg);
479 Type propType = prop.PropertyType;
480 ControlBuilder builder = null;
481 if (typeof (ICollection).IsAssignableFrom (propType)) {
482 builder = new CollectionBuilder ();
483 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
484 builder = new TemplateBuilder (prop);
485 } else if (typeof (string) == propType) {
486 builder = new StringPropertyBuilder (prop.Name);
488 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
489 null, atts, line, fileName);
490 builder.isProperty = true;
492 builder.originalTagName = propName;
496 builder.Init (parser, this, null, prop.Name, null, atts);
497 builder.fileName = fileName;
499 builder.isProperty = true;
501 builder.originalTagName = propName;
505 public virtual void Init (TemplateParser parser,
506 ControlBuilder parentBuilder,
512 this.parser = parser;
514 this.location = parser.Location;
516 this.parentBuilder = parentBuilder;
518 this.tagName = tagName;
520 this.attribs = attribs;
524 if (this is TemplateBuilder)
527 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
529 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
530 isIParserAccessor = false;
531 childrenAsProperties = true;
532 } else if (atts.Length > 0) {
533 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
534 childrenAsProperties = att.ChildrenAsProperties;
535 if (childrenAsProperties && att.DefaultProperty.Length != 0)
536 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
541 public virtual bool NeedsTagInnerText ()
546 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
548 if (defaultPropertyBuilder == null)
551 ControlBuilder old = defaultPropertyBuilder;
552 defaultPropertyBuilder = null;
553 AppendSubBuilder (old);
556 internal void SetTagName (string name)
561 public virtual void SetTagInnerText (string text)
565 internal string GetNextID (string proposedID)
567 if (proposedID != null && proposedID.Trim ().Length != 0)
570 return "_bctrl_" + nextID++;
573 internal virtual ControlBuilder CreateSubBuilder (string tagid,
576 TemplateParser parser,
579 ControlBuilder childBuilder = null;
580 if (childrenAsProperties) {
581 if (defaultPropertyBuilder == null)
582 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
584 if (String.Compare (defaultPropertyBuilder.TagName, tagid, true, CultureInfo.InvariantCulture) == 0) {
585 // The child tag is the same what our default property name. Act as if there was
586 // no default property builder, or otherwise we'll end up with invalid nested
588 defaultPropertyBuilder = null;
589 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
591 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
599 if (String.Compare (tagName, tagid, true, CultureInfo.InvariantCulture) == 0)
602 childType = GetChildControlType (tagid, atts);
603 if (childType == null)
606 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
607 location.BeginLine, location.Filename);
612 internal virtual object CreateInstance ()
614 // HtmlGenericControl, HtmlTableCell...
615 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
616 object [] args = null;
617 if (atts != null && atts.Length > 0) {
618 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
620 args = new object [] {tagName};
623 return Activator.CreateInstance (type, args);
626 internal virtual void CreateChildren (object parent)
628 if (children == null || children.Count == 0)
631 IParserAccessor parser = parent as IParserAccessor;
635 foreach (object o in children) {
637 parser.AddParsedSubObject (new LiteralControl ((string) o));
639 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
644 [MonoTODO ("unsure, lack documentation")]
645 public virtual object BuildObject ()
647 return CreateInstance ();
650 internal void ResetState()
653 haveParserVariable = false;
655 if (Children != null) {
656 foreach (object child in Children) {
657 ControlBuilder cb = child as ControlBuilder;