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;
33 using System.Reflection;
34 using System.Security.Permissions;
35 using System.Web.Compilation;
36 using System.Web.Configuration;
39 namespace System.Web.UI {
42 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
43 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44 public class ControlBuilder
46 internal static BindingFlags flagsNoCase = BindingFlags.Public |
47 BindingFlags.Instance |
49 BindingFlags.IgnoreCase;
51 TemplateParser parser;
52 internal ControlBuilder parentBuilder;
56 internal IDictionary attribs;
58 internal string fileName;
59 bool childrenAsProperties;
60 bool isIParserAccessor = true;
62 internal ControlBuilder defaultPropertyBuilder;
66 internal bool haveParserVariable;
67 internal CodeMemberMethod method;
68 internal CodeStatementCollection methodStatements;
69 internal CodeMemberMethod renderMethod;
70 internal int renderIndex;
71 internal bool isProperty;
72 internal ILocation location;
75 static string privateBinPath;
78 public ControlBuilder ()
82 internal ControlBuilder (TemplateParser parser,
83 ControlBuilder parentBuilder,
89 string sourceFileName)
93 this.parentBuilder = parentBuilder;
95 this.tagName = tagName;
97 this.attribs = attribs;
99 this.fileName = sourceFileName;
102 internal void EnsureOtherTags ()
104 if (otherTags == null)
105 otherTags = new ArrayList ();
108 internal ArrayList OtherTags {
109 get { return otherTags; }
112 public Type ControlType {
116 protected bool FChildrenAsProperties {
117 get { return childrenAsProperties; }
120 protected bool FIsNonParserAccessor {
121 get { return !isIParserAccessor; }
124 public bool HasAspCode {
125 get { return hasAspCode; }
133 internal ArrayList Children {
134 get { return children; }
137 internal void SetControlType (Type t)
142 protected bool InDesigner {
143 get { return false; }
146 public Type NamingContainerType {
148 if (parentBuilder == null)
149 return typeof (Control);
151 Type ptype = parentBuilder.ControlType;
153 return parentBuilder.NamingContainerType;
155 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
156 return parentBuilder.NamingContainerType;
163 private static string PrivateBinPath {
165 if (privateBinPath != null)
166 return privateBinPath;
168 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
169 privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
171 return privateBinPath;
181 Type BindingContainerType {
183 if (parentBuilder == null)
184 return typeof (Control);
186 if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
187 return ((TemplateBuilder)parentBuilder).ContainerType;
189 Type ptype = parentBuilder.ControlType;
191 return parentBuilder.BindingContainerType;
193 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
194 return parentBuilder.BindingContainerType;
200 internal TemplateBuilder ParentTemplateBuilder {
202 if (parentBuilder == null)
204 else if (parentBuilder is TemplateBuilder)
205 return (TemplateBuilder) parentBuilder;
207 return parentBuilder.ParentTemplateBuilder;
211 protected TemplateParser Parser {
212 get { return parser; }
215 public string TagName {
216 get { return tagName; }
219 internal RootBuilder Root {
221 if (GetType () == typeof (RootBuilder))
222 return (RootBuilder) this;
224 return (RootBuilder) parentBuilder.Root;
228 internal bool ChildrenAsProperties {
229 get { return childrenAsProperties; }
232 public virtual bool AllowWhitespaceLiterals ()
237 public virtual void AppendLiteralString (string s)
239 if (s == null || s == "")
242 if (childrenAsProperties || !isIParserAccessor) {
243 if (defaultPropertyBuilder != null) {
244 defaultPropertyBuilder.AppendLiteralString (s);
245 } else if (s.Trim () != "") {
246 throw new HttpException ("Literal content not allowed for " + tagName + " " +
247 GetType () + " \"" + s + "\"");
253 if (!AllowWhitespaceLiterals () && s.Trim () == "")
256 if (HtmlDecodeLiterals ())
257 s = HttpUtility.HtmlDecode (s);
259 if (children == null)
260 children = new ArrayList ();
265 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
267 subBuilder.OnAppendToParentBuilder (this);
269 subBuilder.parentBuilder = this;
270 if (childrenAsProperties) {
271 AppendToProperty (subBuilder);
275 if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
276 AppendCode (subBuilder);
280 if (children == null)
281 children = new ArrayList ();
283 children.Add (subBuilder);
286 void AppendToProperty (ControlBuilder subBuilder)
288 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
289 throw new HttpException ("Code render not supported here.");
291 if (defaultPropertyBuilder != null) {
292 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
296 if (children == null)
297 children = new ArrayList ();
299 children.Add (subBuilder);
302 void AppendCode (ControlBuilder subBuilder)
304 if (type != null && !(typeof (Control).IsAssignableFrom (type)))
305 throw new HttpException ("Code render not supported here.");
307 if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
310 if (children == null)
311 children = new ArrayList ();
313 children.Add (subBuilder);
316 public virtual void CloseControl ()
321 static Type LoadType (string typeName)
323 Type type = Type.GetType (typeName);
328 if ((tla = BuildManager.TopLevelAssemblies) != null) {
329 foreach (Assembly asm in tla) {
332 type = asm.GetType (typeName);
338 if (!Directory.Exists (PrivateBinPath))
341 string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
342 foreach (string s in binDlls) {
343 Assembly binA = Assembly.LoadFrom (s);
344 type = binA.GetType (typeName);
354 static Type MapTagType (Type tagType)
359 PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
363 TagMapCollection tags = ps.TagMapping;
364 if (tags == null || tags.Count == 0)
366 string tagTypeName = tagType.ToString ();
367 foreach (TagMapInfo tmi in tags)
368 if (tmi.TagType == tagTypeName) {
370 return LoadType (tmi.MappedTagType);
371 } catch (Exception ex) {
372 throw new HttpException (String.Format ("Unable to map tag type {0}", tagTypeName),
380 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
381 ControlBuilder parentBuilder,
387 string sourceFileName)
392 tagType = MapTagType (type);
396 ControlBuilder builder;
397 object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
398 if (atts != null && atts.Length > 0) {
399 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
400 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
402 builder = new ControlBuilder ();
405 builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
407 builder.fileName = sourceFileName;
411 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
416 public virtual bool HasBody ()
421 public virtual bool HtmlDecodeLiterals ()
426 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
428 PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
430 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
431 throw new HttpException (msg);
434 Type propType = prop.PropertyType;
435 ControlBuilder builder = null;
436 if (typeof (ICollection).IsAssignableFrom (propType)) {
437 builder = new CollectionBuilder ();
438 } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
439 builder = new TemplateBuilder (prop);
440 } else if (typeof (string) == propType) {
441 builder = new StringPropertyBuilder (prop.Name);
443 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
444 null, atts, line, fileName);
445 builder.isProperty = true;
449 builder.Init (parser, this, null, prop.Name, null, atts);
450 builder.fileName = fileName;
452 builder.isProperty = true;
456 public virtual void Init (TemplateParser parser,
457 ControlBuilder parentBuilder,
463 this.parser = parser;
465 this.location = parser.Location;
467 this.parentBuilder = parentBuilder;
469 this.tagName = tagName;
471 this.attribs = attribs;
475 if (this is TemplateBuilder)
478 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
480 if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
481 isIParserAccessor = false;
482 childrenAsProperties = true;
483 } else if (atts.Length > 0) {
484 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
485 childrenAsProperties = att.ChildrenAsProperties;
486 if (childrenAsProperties && att.DefaultProperty != "") {
487 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
493 public virtual bool NeedsTagInnerText ()
498 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
500 if (defaultPropertyBuilder == null)
503 ControlBuilder old = defaultPropertyBuilder;
504 defaultPropertyBuilder = null;
505 AppendSubBuilder (old);
508 internal void SetTagName (string name)
513 public virtual void SetTagInnerText (string text)
517 internal string GetNextID (string proposedID)
519 if (proposedID != null && proposedID.Trim () != "")
522 return "_bctrl_" + nextID++;
525 internal virtual ControlBuilder CreateSubBuilder (string tagid,
528 TemplateParser parser,
531 ControlBuilder childBuilder = null;
532 if (childrenAsProperties) {
533 if (defaultPropertyBuilder == null) {
534 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
536 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
537 null, parser, location);
542 childType = GetChildControlType (tagid, atts);
543 if (childType == null)
546 childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
547 location.BeginLine, location.Filename);
552 internal virtual object CreateInstance ()
554 // HtmlGenericControl, HtmlTableCell...
555 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
556 object [] args = null;
557 if (atts != null && atts.Length > 0) {
558 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
560 args = new object [] {tagName};
563 return Activator.CreateInstance (type, args);
566 internal virtual void CreateChildren (object parent)
568 if (children == null || children.Count == 0)
571 IParserAccessor parser = parent as IParserAccessor;
575 foreach (object o in children) {
577 parser.AddParsedSubObject (new LiteralControl ((string) o));
579 parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
584 [MonoTODO ("unsure, lack documentation")]
585 public virtual object BuildObject ()
587 return CreateInstance ();
590 internal void ResetState()
593 haveParserVariable = false;
595 if (Children != null) {
596 foreach (object child in Children) {
597 ControlBuilder cb = child as ControlBuilder;