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.Reflection;
35 using System.Security.Permissions;
36 using System.Web.Compilation;
37 using System.Web.Configuration;
39 using System.Web.UI.WebControls;
41 namespace System.Web.UI {
44 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
46 public class ControlBuilder
48 internal static BindingFlags flagsNoCase = BindingFlags.Public |
49 BindingFlags.Instance |
51 BindingFlags.IgnoreCase;
53 ControlBuilder myNamingContainer;
54 TemplateParser parser;
55 internal ControlBuilder parentBuilder;
59 internal IDictionary attribs;
61 internal string fileName;
62 bool childrenAsProperties;
63 bool isIParserAccessor = true;
65 internal ControlBuilder defaultPropertyBuilder;
69 internal bool haveParserVariable;
70 internal CodeMemberMethod method;
71 internal CodeStatementCollection methodStatements;
72 internal CodeMemberMethod renderMethod;
73 internal int renderIndex;
74 internal bool isProperty;
75 internal ILocation location;
78 static string privateBinPath;
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 void SetControlType (Type t)
145 protected bool InDesigner {
146 get { return false; }
149 public Type NamingContainerType {
151 ControlBuilder cb = myNamingContainer;
154 return typeof (Control);
156 return cb.ControlType;
160 ControlBuilder MyNamingContainer {
162 if (myNamingContainer == null) {
163 Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
165 if (parentBuilder == null && controlType == null)
166 myNamingContainer = null;
167 else if (parentBuilder is TemplateBuilder)
168 myNamingContainer = parentBuilder;
169 else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
170 myNamingContainer = parentBuilder;
172 myNamingContainer = parentBuilder.MyNamingContainer;
175 return myNamingContainer;
184 Type BindingContainerType {
186 ControlBuilder cb = MyNamingContainer;
188 return typeof (Control);
191 if (cb is ContentBuilderInternal)
192 return cb.BindingContainerType;
195 if (cb is TemplateBuilder) {
196 Type ct =((TemplateBuilder) cb).ContainerType;
198 return typeof (Control);
202 return cb.ControlType;
206 internal TemplateBuilder ParentTemplateBuilder {
208 if (parentBuilder == null)
210 else if (parentBuilder is TemplateBuilder)
211 return (TemplateBuilder) parentBuilder;
213 return parentBuilder.ParentTemplateBuilder;
217 protected TemplateParser Parser {
218 get { return parser; }
221 public string TagName {
222 get { return tagName; }
225 internal RootBuilder Root {
227 if (GetType () == typeof (RootBuilder))
228 return (RootBuilder) this;
230 return (RootBuilder) parentBuilder.Root;
234 internal bool ChildrenAsProperties {
235 get { return childrenAsProperties; }
238 public virtual bool AllowWhitespaceLiterals ()
243 public virtual void AppendLiteralString (string s)
245 if (s == null || s.Length == 0)
248 if (childrenAsProperties || !isIParserAccessor) {
249 if (defaultPropertyBuilder != null) {
250 defaultPropertyBuilder.AppendLiteralString (s);
251 } else if (s.Trim ().Length != 0) {
252 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
253 tagName, GetType (), s));
259 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
262 if (HtmlDecodeLiterals ())
263 s = HttpUtility.HtmlDecode (s);
265 if (children == null)
266 children = new ArrayList ();
271 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
273 subBuilder.OnAppendToParentBuilder (this);
275 subBuilder.parentBuilder = this;
276 if (childrenAsProperties) {
277 AppendToProperty (subBuilder);
281 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
282 AppendCode (subBuilder);
286 if (children == null)
287 children = new ArrayList ();
289 children.Add (subBuilder);
292 void AppendToProperty (ControlBuilder subBuilder)
294 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
295 throw new HttpException ("Code render not supported here.");
297 if (defaultPropertyBuilder != null) {
298 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
302 if (children == null)
303 children = new ArrayList ();
305 children.Add (subBuilder);
308 void AppendCode (ControlBuilder subBuilder)
310 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
311 throw new HttpException ("Code render not supported here.");
313 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
316 if (children == null)
317 children = new ArrayList ();
319 children.Add (subBuilder);
322 public virtual void CloseControl ()
327 static Type MapTagType (Type tagType)
332 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
336 TagMapCollection tags = ps.TagMapping;
337 if (tags == null || tags.Count == 0)
340 string tagTypeName = tagType.ToString ();
341 Type mappedType, originalType;
342 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
346 foreach (TagMapInfo tmi in tags) {
351 originalTypeName = tmi.TagType;
352 originalType = HttpApplication.LoadType (originalTypeName);
353 if (originalType == null)
357 } catch (Exception ex) {
362 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
364 if (originalTypeName == tagTypeName) {
365 mappedTypeName = tmi.MappedTagType;
370 mappedType = HttpApplication.LoadType (mappedTypeName);
371 if (mappedType == null)
375 } catch (Exception ex) {
381 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
384 if (!mappedType.IsSubclassOf (originalType))
385 throw new ConfigurationErrorsException (
386 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
396 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
397 ControlBuilder parentBuilder,
403 string sourceFileName)
408 tagType = MapTagType (type);
412 ControlBuilder builder;
413 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
414 if (atts != null && atts.Length > 0) {
415 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
416 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
418 builder = new ControlBuilder ();
421 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
423 builder.fileName = sourceFileName;
427 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
432 public virtual bool HasBody ()
437 public virtual bool HtmlDecodeLiterals ()
442 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
444 PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
446 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
447 throw new HttpException (msg);
450 Type propType = prop.PropertyType;
451 ControlBuilder builder = null;
452 if (typeof (ICollection).IsAssignableFrom (propType)) {
453 builder = new CollectionBuilder ();
454 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
455 builder = new TemplateBuilder (prop);
456 } else if (typeof (string) == propType) {
457 builder = new StringPropertyBuilder (prop.Name);
459 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
460 null, atts, line, fileName);
461 builder.isProperty = true;
465 builder.Init (parser, this, null, prop.Name, null, atts);
466 builder.fileName = fileName;
468 builder.isProperty = true;
472 public virtual void Init (TemplateParser parser,
473 ControlBuilder parentBuilder,
479 this.parser = parser;
481 this.location = parser.Location;
483 this.parentBuilder = parentBuilder;
485 this.tagName = tagName;
487 this.attribs = attribs;
491 if (this is TemplateBuilder)
494 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
496 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
497 isIParserAccessor = false;
498 childrenAsProperties = true;
499 } else if (atts.Length > 0) {
500 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
501 childrenAsProperties = att.ChildrenAsProperties;
502 if (childrenAsProperties && att.DefaultProperty.Length != 0)
503 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
508 public virtual bool NeedsTagInnerText ()
513 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
515 if (defaultPropertyBuilder == null)
518 ControlBuilder old = defaultPropertyBuilder;
519 defaultPropertyBuilder = null;
520 AppendSubBuilder (old);
523 internal void SetTagName (string name)
528 public virtual void SetTagInnerText (string text)
532 internal string GetNextID (string proposedID)
534 if (proposedID != null && proposedID.Trim ().Length != 0)
537 return "_bctrl_" + nextID++;
540 internal virtual ControlBuilder CreateSubBuilder (string tagid,
543 TemplateParser parser,
546 ControlBuilder childBuilder = null;
547 if (childrenAsProperties) {
548 if (defaultPropertyBuilder == null)
549 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
551 if (defaultPropertyBuilder.TagName == tagid) {
552 // The child tag is the same what our default property name. Act as if there was
553 // no default property builder, or otherwise we'll end up with invalid nested
555 defaultPropertyBuilder = null;
556 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
558 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
565 if (tagName == tagid)
568 childType = GetChildControlType (tagid, atts);
569 if (childType == null)
572 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
573 location.BeginLine, location.Filename);
578 internal virtual object CreateInstance ()
580 // HtmlGenericControl, HtmlTableCell...
581 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
582 object [] args = null;
583 if (atts != null && atts.Length > 0) {
584 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
586 args = new object [] {tagName};
589 return Activator.CreateInstance (type, args);
592 internal virtual void CreateChildren (object parent)
594 if (children == null || children.Count == 0)
597 IParserAccessor parser = parent as IParserAccessor;
601 foreach (object o in children) {
603 parser.AddParsedSubObject (new LiteralControl ((string) o));
605 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
610 [MonoTODO ("unsure, lack documentation")]
611 public virtual object BuildObject ()
613 return CreateInstance ();
616 internal void ResetState()
619 haveParserVariable = false;
621 if (Children != null) {
622 foreach (object child in Children) {
623 ControlBuilder cb = child as ControlBuilder;