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;
164 private static string PrivateBinPath {
166 if (privateBinPath != null)
167 return privateBinPath;
169 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
170 privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
172 return privateBinPath;
182 Type BindingContainerType {
184 if (parentBuilder == null)
185 return typeof (Control);
187 if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
188 return ((TemplateBuilder)parentBuilder).ContainerType;
190 Type ptype = parentBuilder.ControlType;
192 return parentBuilder.BindingContainerType;
194 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
195 return parentBuilder.BindingContainerType;
201 internal TemplateBuilder ParentTemplateBuilder {
203 if (parentBuilder == null)
205 else if (parentBuilder is TemplateBuilder)
206 return (TemplateBuilder) parentBuilder;
208 return parentBuilder.ParentTemplateBuilder;
212 protected TemplateParser Parser {
213 get { return parser; }
216 public string TagName {
217 get { return tagName; }
220 internal RootBuilder Root {
222 if (GetType () == typeof (RootBuilder))
223 return (RootBuilder) this;
225 return (RootBuilder) parentBuilder.Root;
229 internal bool ChildrenAsProperties {
230 get { return childrenAsProperties; }
233 public virtual bool AllowWhitespaceLiterals ()
238 public virtual void AppendLiteralString (string s)
240 if (s == null || s.Length == 0)
243 if (childrenAsProperties || !isIParserAccessor) {
244 if (defaultPropertyBuilder != null) {
245 defaultPropertyBuilder.AppendLiteralString (s);
246 } else if (s.Trim ().Length != 0) {
247 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
248 tagName, GetType (), s));
254 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
257 if (HtmlDecodeLiterals ())
258 s = HttpUtility.HtmlDecode (s);
260 if (children == null)
261 children = new ArrayList ();
266 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
268 subBuilder.OnAppendToParentBuilder (this);
270 subBuilder.parentBuilder = this;
271 if (childrenAsProperties) {
272 AppendToProperty (subBuilder);
276 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
277 AppendCode (subBuilder);
281 if (children == null)
282 children = new ArrayList ();
284 children.Add (subBuilder);
287 void AppendToProperty (ControlBuilder subBuilder)
289 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
290 throw new HttpException ("Code render not supported here.");
292 if (defaultPropertyBuilder != null) {
293 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
297 if (children == null)
298 children = new ArrayList ();
300 children.Add (subBuilder);
303 void AppendCode (ControlBuilder subBuilder)
305 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
306 throw new HttpException ("Code render not supported here.");
308 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
311 if (children == null)
312 children = new ArrayList ();
314 children.Add (subBuilder);
317 public virtual void CloseControl ()
322 static Type MapTagType (Type tagType)
327 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
331 TagMapCollection tags = ps.TagMapping;
332 if (tags == null || tags.Count == 0)
335 string tagTypeName = tagType.ToString ();
336 Type mappedType, originalType;
337 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
341 foreach (TagMapInfo tmi in tags) {
346 originalTypeName = tmi.TagType;
347 originalType = HttpApplication.LoadType (originalTypeName);
348 if (originalType == null)
352 } catch (Exception ex) {
357 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
359 if (originalTypeName == tagTypeName) {
360 mappedTypeName = tmi.MappedTagType;
365 mappedType = HttpApplication.LoadType (mappedTypeName);
366 if (mappedType == null)
370 } catch (Exception ex) {
376 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
379 if (!mappedType.IsSubclassOf (originalType))
380 throw new ConfigurationErrorsException (
381 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
391 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
392 ControlBuilder parentBuilder,
398 string sourceFileName)
403 tagType = MapTagType (type);
407 ControlBuilder builder;
408 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
409 if (atts != null && atts.Length > 0) {
410 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
411 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
413 builder = new ControlBuilder ();
416 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
418 builder.fileName = sourceFileName;
422 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
427 public virtual bool HasBody ()
432 public virtual bool HtmlDecodeLiterals ()
437 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
439 PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
441 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
442 throw new HttpException (msg);
445 Type propType = prop.PropertyType;
446 ControlBuilder builder = null;
447 if (typeof (ICollection).IsAssignableFrom (propType)) {
448 builder = new CollectionBuilder ();
449 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
450 builder = new TemplateBuilder (prop);
451 } else if (typeof (string) == propType) {
452 builder = new StringPropertyBuilder (prop.Name);
454 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
455 null, atts, line, fileName);
456 builder.isProperty = true;
460 builder.Init (parser, this, null, prop.Name, null, atts);
461 builder.fileName = fileName;
463 builder.isProperty = true;
467 public virtual void Init (TemplateParser parser,
468 ControlBuilder parentBuilder,
474 this.parser = parser;
476 this.location = parser.Location;
478 this.parentBuilder = parentBuilder;
480 this.tagName = tagName;
482 this.attribs = attribs;
486 if (this is TemplateBuilder)
489 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
491 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
492 isIParserAccessor = false;
493 childrenAsProperties = true;
494 } else if (atts.Length > 0) {
495 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
496 childrenAsProperties = att.ChildrenAsProperties;
497 if (childrenAsProperties && att.DefaultProperty.Length != 0)
498 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
503 public virtual bool NeedsTagInnerText ()
508 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
510 if (defaultPropertyBuilder == null)
513 ControlBuilder old = defaultPropertyBuilder;
514 defaultPropertyBuilder = null;
515 AppendSubBuilder (old);
518 internal void SetTagName (string name)
523 public virtual void SetTagInnerText (string text)
527 internal string GetNextID (string proposedID)
529 if (proposedID != null && proposedID.Trim ().Length != 0)
532 return "_bctrl_" + nextID++;
535 internal virtual ControlBuilder CreateSubBuilder (string tagid,
538 TemplateParser parser,
541 ControlBuilder childBuilder = null;
542 if (childrenAsProperties) {
543 if (defaultPropertyBuilder == null)
544 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
546 if (defaultPropertyBuilder.TagName == tagid) {
547 // The child tag is the same what our default property name. Act as if there was
548 // no default property builder, or otherwise we'll end up with invalid nested
550 defaultPropertyBuilder = null;
551 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
553 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
560 if (tagName == tagid)
563 childType = GetChildControlType (tagid, atts);
564 if (childType == null)
567 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
568 location.BeginLine, location.Filename);
573 internal virtual object CreateInstance ()
575 // HtmlGenericControl, HtmlTableCell...
576 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
577 object [] args = null;
578 if (atts != null && atts.Length > 0) {
579 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
581 args = new object [] {tagName};
584 return Activator.CreateInstance (type, args);
587 internal virtual void CreateChildren (object parent)
589 if (children == null || children.Count == 0)
592 IParserAccessor parser = parent as IParserAccessor;
596 foreach (object o in children) {
598 parser.AddParsedSubObject (new LiteralControl ((string) o));
600 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
605 [MonoTODO ("unsure, lack documentation")]
606 public virtual object BuildObject ()
608 return CreateInstance ();
611 internal void ResetState()
614 haveParserVariable = false;
616 if (Children != null) {
617 foreach (object child in Children) {
618 ControlBuilder cb = child as ControlBuilder;