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;
40 namespace System.Web.UI {
43 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 public class ControlBuilder
47 internal static BindingFlags flagsNoCase = BindingFlags.Public |
48 BindingFlags.Instance |
50 BindingFlags.IgnoreCase;
52 TemplateParser parser;
53 internal ControlBuilder parentBuilder;
57 internal IDictionary attribs;
59 internal string fileName;
60 bool childrenAsProperties;
61 bool isIParserAccessor = true;
63 internal ControlBuilder defaultPropertyBuilder;
67 internal bool haveParserVariable;
68 internal CodeMemberMethod method;
69 internal CodeStatementCollection methodStatements;
70 internal CodeMemberMethod renderMethod;
71 internal int renderIndex;
72 internal bool isProperty;
73 internal ILocation location;
76 static string privateBinPath;
79 public ControlBuilder ()
83 internal ControlBuilder (TemplateParser parser,
84 ControlBuilder parentBuilder,
90 string sourceFileName)
94 this.parentBuilder = parentBuilder;
96 this.tagName = tagName;
98 this.attribs = attribs;
100 this.fileName = sourceFileName;
103 internal void EnsureOtherTags ()
105 if (otherTags == null)
106 otherTags = new ArrayList ();
109 internal ArrayList OtherTags {
110 get { return otherTags; }
113 public Type ControlType {
117 protected bool FChildrenAsProperties {
118 get { return childrenAsProperties; }
121 protected bool FIsNonParserAccessor {
122 get { return !isIParserAccessor; }
125 public bool HasAspCode {
126 get { return hasAspCode; }
134 internal ArrayList Children {
135 get { return children; }
138 internal void SetControlType (Type t)
143 protected bool InDesigner {
144 get { return false; }
147 public Type NamingContainerType {
149 if (parentBuilder == null)
150 return typeof (Control);
152 Type ptype = parentBuilder.ControlType;
154 return parentBuilder.NamingContainerType;
156 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
157 return parentBuilder.NamingContainerType;
168 Type BindingContainerType {
170 if (parentBuilder == null) {
172 Type bt = Parser.BaseType;
177 return typeof (Control);
180 if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
181 return ((TemplateBuilder)parentBuilder).ContainerType;
183 Type ptype = parentBuilder.ControlType;
185 return parentBuilder.BindingContainerType;
187 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
188 return parentBuilder.BindingContainerType;
191 return typeof (Control);
198 internal TemplateBuilder ParentTemplateBuilder {
200 if (parentBuilder == null)
202 else if (parentBuilder is TemplateBuilder)
203 return (TemplateBuilder) parentBuilder;
205 return parentBuilder.ParentTemplateBuilder;
209 protected TemplateParser Parser {
210 get { return parser; }
213 public string TagName {
214 get { return tagName; }
217 internal RootBuilder Root {
219 if (GetType () == typeof (RootBuilder))
220 return (RootBuilder) this;
222 return (RootBuilder) parentBuilder.Root;
226 internal bool ChildrenAsProperties {
227 get { return childrenAsProperties; }
230 public virtual bool AllowWhitespaceLiterals ()
235 public virtual void AppendLiteralString (string s)
237 if (s == null || s.Length == 0)
240 if (childrenAsProperties || !isIParserAccessor) {
241 if (defaultPropertyBuilder != null) {
242 defaultPropertyBuilder.AppendLiteralString (s);
243 } else if (s.Trim ().Length != 0) {
244 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
245 tagName, GetType (), s));
251 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
254 if (HtmlDecodeLiterals ())
255 s = HttpUtility.HtmlDecode (s);
257 if (children == null)
258 children = new ArrayList ();
263 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
265 subBuilder.OnAppendToParentBuilder (this);
267 subBuilder.parentBuilder = this;
268 if (childrenAsProperties) {
269 AppendToProperty (subBuilder);
273 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
274 AppendCode (subBuilder);
278 if (children == null)
279 children = new ArrayList ();
281 children.Add (subBuilder);
284 void AppendToProperty (ControlBuilder subBuilder)
286 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
287 throw new HttpException ("Code render not supported here.");
289 if (defaultPropertyBuilder != null) {
290 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
294 if (children == null)
295 children = new ArrayList ();
297 children.Add (subBuilder);
300 void AppendCode (ControlBuilder subBuilder)
302 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
303 throw new HttpException ("Code render not supported here.");
305 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
308 if (children == null)
309 children = new ArrayList ();
311 children.Add (subBuilder);
314 public virtual void CloseControl ()
319 static Type MapTagType (Type tagType)
324 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
328 TagMapCollection tags = ps.TagMapping;
329 if (tags == null || tags.Count == 0)
332 string tagTypeName = tagType.ToString ();
333 Type mappedType, originalType;
334 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
338 foreach (TagMapInfo tmi in tags) {
343 originalTypeName = tmi.TagType;
344 originalType = HttpApplication.LoadType (originalTypeName);
345 if (originalType == null)
349 } catch (Exception ex) {
354 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
356 if (originalTypeName == tagTypeName) {
357 mappedTypeName = tmi.MappedTagType;
362 mappedType = HttpApplication.LoadType (mappedTypeName);
363 if (mappedType == null)
367 } catch (Exception ex) {
373 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
376 if (!mappedType.IsSubclassOf (originalType))
377 throw new ConfigurationErrorsException (
378 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
388 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
389 ControlBuilder parentBuilder,
395 string sourceFileName)
400 tagType = MapTagType (type);
404 ControlBuilder builder;
405 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
406 if (atts != null && atts.Length > 0) {
407 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
408 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
410 builder = new ControlBuilder ();
413 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
415 builder.fileName = sourceFileName;
419 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
424 public virtual bool HasBody ()
429 public virtual bool HtmlDecodeLiterals ()
434 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
436 PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
438 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
439 throw new HttpException (msg);
442 Type propType = prop.PropertyType;
443 ControlBuilder builder = null;
444 if (typeof (ICollection).IsAssignableFrom (propType)) {
445 builder = new CollectionBuilder ();
446 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
447 builder = new TemplateBuilder (prop);
448 } else if (typeof (string) == propType) {
449 builder = new StringPropertyBuilder (prop.Name);
451 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
452 null, atts, line, fileName);
453 builder.isProperty = true;
457 builder.Init (parser, this, null, prop.Name, null, atts);
458 builder.fileName = fileName;
460 builder.isProperty = true;
464 public virtual void Init (TemplateParser parser,
465 ControlBuilder parentBuilder,
471 this.parser = parser;
473 this.location = parser.Location;
475 this.parentBuilder = parentBuilder;
477 this.tagName = tagName;
479 this.attribs = attribs;
483 if (this is TemplateBuilder)
486 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
488 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
489 isIParserAccessor = false;
490 childrenAsProperties = true;
491 } else if (atts.Length > 0) {
492 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
493 childrenAsProperties = att.ChildrenAsProperties;
494 if (childrenAsProperties && att.DefaultProperty.Length != 0)
495 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
500 public virtual bool NeedsTagInnerText ()
505 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
507 if (defaultPropertyBuilder == null)
510 ControlBuilder old = defaultPropertyBuilder;
511 defaultPropertyBuilder = null;
512 AppendSubBuilder (old);
515 internal void SetTagName (string name)
520 public virtual void SetTagInnerText (string text)
524 internal string GetNextID (string proposedID)
526 if (proposedID != null && proposedID.Trim ().Length != 0)
529 return "_bctrl_" + nextID++;
532 internal virtual ControlBuilder CreateSubBuilder (string tagid,
535 TemplateParser parser,
538 ControlBuilder childBuilder = null;
539 if (childrenAsProperties) {
540 if (defaultPropertyBuilder == null)
541 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
543 if (defaultPropertyBuilder.TagName == tagid) {
544 // The child tag is the same what our default property name. Act as if there was
545 // no default property builder, or otherwise we'll end up with invalid nested
547 defaultPropertyBuilder = null;
548 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
550 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
557 if (tagName == tagid)
560 childType = GetChildControlType (tagid, atts);
561 if (childType == null)
564 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
565 location.BeginLine, location.Filename);
570 internal virtual object CreateInstance ()
572 // HtmlGenericControl, HtmlTableCell...
573 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
574 object [] args = null;
575 if (atts != null && atts.Length > 0) {
576 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
578 args = new object [] {tagName};
581 return Activator.CreateInstance (type, args);
584 internal virtual void CreateChildren (object parent)
586 if (children == null || children.Count == 0)
589 IParserAccessor parser = parent as IParserAccessor;
593 foreach (object o in children) {
595 parser.AddParsedSubObject (new LiteralControl ((string) o));
597 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
602 [MonoTODO ("unsure, lack documentation")]
603 public virtual object BuildObject ()
605 return CreateInstance ();
608 internal void ResetState()
611 haveParserVariable = false;
613 if (Children != null) {
614 foreach (object child in Children) {
615 ControlBuilder cb = child as ControlBuilder;