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) {
186 Type bt = Parser.BaseType;
191 return typeof (Control);
194 if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
195 return ((TemplateBuilder)parentBuilder).ContainerType;
197 Type ptype = parentBuilder.ControlType;
199 return parentBuilder.BindingContainerType;
201 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
202 return parentBuilder.BindingContainerType;
205 return typeof (Control);
212 internal TemplateBuilder ParentTemplateBuilder {
214 if (parentBuilder == null)
216 else if (parentBuilder is TemplateBuilder)
217 return (TemplateBuilder) parentBuilder;
219 return parentBuilder.ParentTemplateBuilder;
223 protected TemplateParser Parser {
224 get { return parser; }
227 public string TagName {
228 get { return tagName; }
231 internal RootBuilder Root {
233 if (GetType () == typeof (RootBuilder))
234 return (RootBuilder) this;
236 return (RootBuilder) parentBuilder.Root;
240 internal bool ChildrenAsProperties {
241 get { return childrenAsProperties; }
244 public virtual bool AllowWhitespaceLiterals ()
249 public virtual void AppendLiteralString (string s)
251 if (s == null || s.Length == 0)
254 if (childrenAsProperties || !isIParserAccessor) {
255 if (defaultPropertyBuilder != null) {
256 defaultPropertyBuilder.AppendLiteralString (s);
257 } else if (s.Trim ().Length != 0) {
258 throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
259 tagName, GetType (), s));
265 if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
268 if (HtmlDecodeLiterals ())
269 s = HttpUtility.HtmlDecode (s);
271 if (children == null)
272 children = new ArrayList ();
277 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
279 subBuilder.OnAppendToParentBuilder (this);
281 subBuilder.parentBuilder = this;
282 if (childrenAsProperties) {
283 AppendToProperty (subBuilder);
287 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
288 AppendCode (subBuilder);
292 if (children == null)
293 children = new ArrayList ();
295 children.Add (subBuilder);
298 void AppendToProperty (ControlBuilder subBuilder)
300 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
301 throw new HttpException ("Code render not supported here.");
303 if (defaultPropertyBuilder != null) {
304 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
308 if (children == null)
309 children = new ArrayList ();
311 children.Add (subBuilder);
314 void AppendCode (ControlBuilder subBuilder)
316 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
317 throw new HttpException ("Code render not supported here.");
319 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
322 if (children == null)
323 children = new ArrayList ();
325 children.Add (subBuilder);
328 public virtual void CloseControl ()
333 static Type MapTagType (Type tagType)
338 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
342 TagMapCollection tags = ps.TagMapping;
343 if (tags == null || tags.Count == 0)
346 string tagTypeName = tagType.ToString ();
347 Type mappedType, originalType;
348 string originalTypeName = String.Empty, mappedTypeName = String.Empty;
352 foreach (TagMapInfo tmi in tags) {
357 originalTypeName = tmi.TagType;
358 originalType = HttpApplication.LoadType (originalTypeName);
359 if (originalType == null)
363 } catch (Exception ex) {
368 throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
370 if (originalTypeName == tagTypeName) {
371 mappedTypeName = tmi.MappedTagType;
376 mappedType = HttpApplication.LoadType (mappedTypeName);
377 if (mappedType == null)
381 } catch (Exception ex) {
387 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
390 if (!mappedType.IsSubclassOf (originalType))
391 throw new ConfigurationErrorsException (
392 String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
402 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
403 ControlBuilder parentBuilder,
409 string sourceFileName)
414 tagType = MapTagType (type);
418 ControlBuilder builder;
419 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
420 if (atts != null && atts.Length > 0) {
421 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
422 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
424 builder = new ControlBuilder ();
427 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
429 builder.fileName = sourceFileName;
433 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
438 public virtual bool HasBody ()
443 public virtual bool HtmlDecodeLiterals ()
448 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
450 PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
452 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
453 throw new HttpException (msg);
456 Type propType = prop.PropertyType;
457 ControlBuilder builder = null;
458 if (typeof (ICollection).IsAssignableFrom (propType)) {
459 builder = new CollectionBuilder ();
460 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
461 builder = new TemplateBuilder (prop);
462 } else if (typeof (string) == propType) {
463 builder = new StringPropertyBuilder (prop.Name);
465 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
466 null, atts, line, fileName);
467 builder.isProperty = true;
471 builder.Init (parser, this, null, prop.Name, null, atts);
472 builder.fileName = fileName;
474 builder.isProperty = true;
478 public virtual void Init (TemplateParser parser,
479 ControlBuilder parentBuilder,
485 this.parser = parser;
487 this.location = parser.Location;
489 this.parentBuilder = parentBuilder;
491 this.tagName = tagName;
493 this.attribs = attribs;
497 if (this is TemplateBuilder)
500 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
502 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
503 isIParserAccessor = false;
504 childrenAsProperties = true;
505 } else if (atts.Length > 0) {
506 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
507 childrenAsProperties = att.ChildrenAsProperties;
508 if (childrenAsProperties && att.DefaultProperty.Length != 0)
509 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
514 public virtual bool NeedsTagInnerText ()
519 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
521 if (defaultPropertyBuilder == null)
524 ControlBuilder old = defaultPropertyBuilder;
525 defaultPropertyBuilder = null;
526 AppendSubBuilder (old);
529 internal void SetTagName (string name)
534 public virtual void SetTagInnerText (string text)
538 internal string GetNextID (string proposedID)
540 if (proposedID != null && proposedID.Trim ().Length != 0)
543 return "_bctrl_" + nextID++;
546 internal virtual ControlBuilder CreateSubBuilder (string tagid,
549 TemplateParser parser,
552 ControlBuilder childBuilder = null;
553 if (childrenAsProperties) {
554 if (defaultPropertyBuilder == null)
555 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
557 if (defaultPropertyBuilder.TagName == tagid) {
558 // The child tag is the same what our default property name. Act as if there was
559 // no default property builder, or otherwise we'll end up with invalid nested
561 defaultPropertyBuilder = null;
562 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
564 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
571 if (tagName == tagid)
574 childType = GetChildControlType (tagid, atts);
575 if (childType == null)
578 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
579 location.BeginLine, location.Filename);
584 internal virtual object CreateInstance ()
586 // HtmlGenericControl, HtmlTableCell...
587 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
588 object [] args = null;
589 if (atts != null && atts.Length > 0) {
590 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
592 args = new object [] {tagName};
595 return Activator.CreateInstance (type, args);
598 internal virtual void CreateChildren (object parent)
600 if (children == null || children.Count == 0)
603 IParserAccessor parser = parent as IParserAccessor;
607 foreach (object o in children) {
609 parser.AddParsedSubObject (new LiteralControl ((string) o));
611 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
616 [MonoTODO ("unsure, lack documentation")]
617 public virtual object BuildObject ()
619 return CreateInstance ();
622 internal void ResetState()
625 haveParserVariable = false;
627 if (Children != null) {
628 foreach (object child in Children) {
629 ControlBuilder cb = child as ControlBuilder;