// Authors:
// Duncan Mak (duncan@ximian.com)
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// Marek Habersack <mhabersack@novell.com>
//
// (C) 2002, 2003 Ximian, Inc. (http://www.ximian.com)
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
//
using System.Collections;
+using System.Configuration;
using System.CodeDom;
+using System.Globalization;
using System.Reflection;
using System.Security.Permissions;
using System.Web.Compilation;
+using System.Web.Configuration;
+using System.IO;
+using System.Web.UI.WebControls;
+using System.Web.Util;
+
+using _Location = System.Web.Compilation.Location;
namespace System.Web.UI {
[AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class ControlBuilder
{
- internal static BindingFlags flagsNoCase = BindingFlags.Public |
- BindingFlags.Instance |
- BindingFlags.Static |
- BindingFlags.IgnoreCase;
+ internal static readonly BindingFlags FlagsNoCase = BindingFlags.Public |
+ BindingFlags.Instance |
+ BindingFlags.Static |
+ BindingFlags.IgnoreCase;
+ ControlBuilder myNamingContainer;
TemplateParser parser;
- internal ControlBuilder parentBuilder;
+ Type parserType;
+ ControlBuilder parentBuilder;
Type type;
string tagName;
+ string originalTagName;
string id;
- internal IDictionary attribs;
- internal int line;
- internal string fileName;
+ IDictionary attribs;
+ int line;
+ string fileName;
bool childrenAsProperties;
bool isIParserAccessor = true;
bool hasAspCode;
- internal ControlBuilder defaultPropertyBuilder;
+ ControlBuilder defaultPropertyBuilder;
ArrayList children;
+ ArrayList templateChildren;
static int nextID;
- internal bool haveParserVariable;
- internal CodeMemberMethod method;
- internal CodeStatementCollection methodStatements;
- internal CodeMemberMethod renderMethod;
- internal int renderIndex;
- internal bool isProperty;
- internal ILocation location;
+ bool haveParserVariable;
+ CodeMemberMethod method;
+ CodeStatementCollection methodStatements;
+ CodeMemberMethod renderMethod;
+ int renderIndex;
+ bool isProperty;
+ bool isPropertyWritable;
+ ILocation location;
ArrayList otherTags;
+ int localVariableCount = 0;
+ bool? isTemplate;
+
public ControlBuilder ()
{
}
{
this.parser = parser;
+ this.parserType = parser != null ? parser.GetType () : null;
this.parentBuilder = parentBuilder;
this.type = type;
this.tagName = tagName;
if (otherTags == null)
otherTags = new ArrayList ();
}
+
+ internal ControlBuilder ParentBuilder {
+ get { return parentBuilder; }
+ }
+
+ internal IDictionary Attributes {
+ get { return attribs; }
+ }
+
+ internal int Line {
+ get { return line; }
+ set { line = value; }
+ }
+
+ internal string FileName {
+ get { return fileName; }
+ set { fileName = value; }
+ }
+
+ internal ControlBuilder DefaultPropertyBuilder {
+ get { return defaultPropertyBuilder; }
+ }
+
+ internal bool HaveParserVariable {
+ get { return haveParserVariable; }
+ set { haveParserVariable = value; }
+ }
+
+ internal CodeMemberMethod Method {
+ get { return method; }
+ set { method = value; }
+ }
+
+ internal CodeMemberMethod DataBindingMethod {
+ get;
+ set;
+ }
+
+ internal CodeStatementCollection MethodStatements {
+ get { return methodStatements; }
+ set { methodStatements = value; }
+ }
+
+ internal CodeMemberMethod RenderMethod {
+ get { return renderMethod; }
+ set { renderMethod = value; }
+ }
+
+ internal int RenderIndex {
+ get { return renderIndex; }
+ }
+
+ internal bool IsProperty {
+ get { return isProperty; }
+ }
+
+ internal bool IsPropertyWritable {
+ get { return isPropertyWritable; }
+ }
+ internal ILocation Location {
+ get { return location; }
+ set { location = new _Location (value); }
+ }
+
internal ArrayList OtherTags {
get { return otherTags; }
}
get { return children; }
}
+ internal ArrayList TemplateChildren {
+ get { return templateChildren; }
+ }
+
internal void SetControlType (Type t)
{
type = t;
public Type NamingContainerType {
get {
- if (parentBuilder == null)
+ ControlBuilder cb = myNamingContainer;
+
+ if (cb == null)
return typeof (Control);
- Type ptype = parentBuilder.ControlType;
- if (ptype == null)
- return parentBuilder.NamingContainerType;
+ return cb.ControlType;
+ }
+ }
- if (!typeof (INamingContainer).IsAssignableFrom (ptype))
- return parentBuilder.NamingContainerType;
+ internal bool IsNamingContainer {
+ get {
+ if (type == null)
+ return false;
- return ptype;
+ return typeof (INamingContainer).IsAssignableFrom (type);
}
}
-#if NET_2_0
- public virtual
-#else
- internal
-#endif
- Type BindingContainerType {
+ internal bool IsTemplate {
get {
- if (parentBuilder == null)
+ if (isTemplate == null)
+ isTemplate = (typeof (TemplateBuilder).IsAssignableFrom (GetType ()));
+
+ return isTemplate.Value;
+ }
+ }
+
+ internal bool PropertyBuilderShouldReturnValue {
+ get { return isProperty && isPropertyWritable && RenderMethod == null && !IsTemplate && !(this is CollectionBuilder) && !(this is RootBuilder); }
+ }
+
+ ControlBuilder MyNamingContainer {
+ get {
+ if (myNamingContainer == null) {
+ Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
+
+ if (parentBuilder == null && controlType == null)
+ myNamingContainer = null;
+ else if (parentBuilder is TemplateBuilder)
+ myNamingContainer = parentBuilder;
+ else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
+ myNamingContainer = parentBuilder;
+ else
+ myNamingContainer = parentBuilder.MyNamingContainer;
+ }
+
+ return myNamingContainer;
+ }
+ }
+
+ public virtual Type BindingContainerType {
+ get {
+ ControlBuilder cb = (this is TemplateBuilder && !(this is RootBuilder)) ? this : MyNamingContainer;
+
+ if (cb == null) {
+ if (this is RootBuilder && parserType == typeof (PageParser))
+ return typeof (Page);
+
return typeof (Control);
+ }
+
+ if (cb != this && cb is ContentBuilderInternal && !typeof (INonBindingContainer).IsAssignableFrom (cb.BindingContainerType))
+ return cb.BindingContainerType;
+
+ Type ct;
+ if (cb is TemplateBuilder) {
+ ct = ((TemplateBuilder) cb).ContainerType;
+ if (typeof (INonBindingContainer).IsAssignableFrom (ct))
+ return MyNamingContainer.BindingContainerType;
- if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
- return ((TemplateBuilder)parentBuilder).ContainerType;
+ if (ct != null)
+ return ct;
- Type ptype = parentBuilder.ControlType;
- if (ptype == null)
- return parentBuilder.BindingContainerType;
+ ct = cb.ControlType;
+ if (ct == null)
+ return typeof (Control);
+
+ if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
+ return MyNamingContainer.BindingContainerType;
- if (!typeof (INamingContainer).IsAssignableFrom (ptype))
- return parentBuilder.BindingContainerType;
+ return ct;
+ }
- return ptype;
+ ct = cb.ControlType;
+ if (ct == null)
+ return typeof (Control);
+
+ if (typeof (INonBindingContainer).IsAssignableFrom (ct) || !typeof (INamingContainer).IsAssignableFrom (ct))
+ return MyNamingContainer.BindingContainerType;
+
+ return cb.ControlType;
}
}
get { return tagName; }
}
+ internal string OriginalTagName {
+ get {
+ if (originalTagName == null || originalTagName.Length == 0)
+ return TagName;
+ return originalTagName;
+ }
+ }
+
internal RootBuilder Root {
get {
- if (GetType () == typeof (RootBuilder))
+ if (typeof (RootBuilder).IsAssignableFrom (GetType ()))
return (RootBuilder) this;
return (RootBuilder) parentBuilder.Root;
internal bool ChildrenAsProperties {
get { return childrenAsProperties; }
}
+
+ internal string GetAttribute (string name)
+ {
+ if (attribs == null)
+ return null;
+
+ return attribs [name] as string;
+ }
+
+ internal void IncreaseRenderIndex ()
+ {
+ renderIndex++;
+ }
+
+ void AddChild (object child)
+ {
+ if (children == null)
+ children = new ArrayList ();
+
+ children.Add (child);
+ ControlBuilder cb = child as ControlBuilder;
+ if (cb != null && cb is TemplateBuilder) {
+ if (templateChildren == null)
+ templateChildren = new ArrayList ();
+ templateChildren.Add (child);
+ }
+
+ if (parser == null)
+ return;
+
+ string tag = cb != null ? cb.TagName : null;
+ if (String.IsNullOrEmpty (tag))
+ return;
+
+ RootBuilder rb = Root;
+ AspComponentFoundry foundry = rb != null ? rb.Foundry : null;
+ if (foundry == null)
+ return;
+ AspComponent component = foundry.GetComponent (tag);
+ if (component == null || !component.FromConfig)
+ return;
+
+ parser.AddImport (component.Namespace);
+ parser.AddDependency (component.Source);
+ }
public virtual bool AllowWhitespaceLiterals ()
{
public virtual void AppendLiteralString (string s)
{
- if (s == null || s == "")
+ if (s == null || s.Length == 0)
return;
if (childrenAsProperties || !isIParserAccessor) {
if (defaultPropertyBuilder != null) {
defaultPropertyBuilder.AppendLiteralString (s);
- } else if (s.Trim () != "") {
- throw new HttpException ("Literal content not allowed for " + tagName + " " +
- GetType () + " \"" + s + "\"");
+ } else if (s.Trim ().Length != 0) {
+ throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
+ tagName, GetType (), s));
}
return;
}
- if (!AllowWhitespaceLiterals () && s.Trim () == "")
+ if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
return;
if (HtmlDecodeLiterals ())
s = HttpUtility.HtmlDecode (s);
- if (children == null)
- children = new ArrayList ();
-
- children.Add (s);
+ AddChild (s);
}
public virtual void AppendSubBuilder (ControlBuilder subBuilder)
return;
}
- if (children == null)
- children = new ArrayList ();
-
- children.Add (subBuilder);
+ AddChild (subBuilder);
}
void AppendToProperty (ControlBuilder subBuilder)
return;
}
- if (children == null)
- children = new ArrayList ();
-
- children.Add (subBuilder);
+ AddChild (subBuilder);
}
void AppendCode (ControlBuilder subBuilder)
if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
hasAspCode = true;
- if (children == null)
- children = new ArrayList ();
-
- children.Add (subBuilder);
+ AddChild (subBuilder);
}
public virtual void CloseControl ()
{
}
+ static Type MapTagType (Type tagType)
+ {
+ if (tagType == null)
+ return null;
+
+ PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
+ if (ps == null)
+ return tagType;
+
+ TagMapCollection tags = ps.TagMapping;
+ if (tags == null || tags.Count == 0)
+ return tagType;
+
+ string tagTypeName = tagType.ToString ();
+ Type mappedType, originalType;
+ string originalTypeName = String.Empty, mappedTypeName = String.Empty;
+ bool missingType;
+ Exception error;
+
+ foreach (TagMapInfo tmi in tags) {
+ error = null;
+ originalType = null;
+
+ try {
+ originalTypeName = tmi.TagType;
+ originalType = HttpApplication.LoadType (originalTypeName);
+ if (originalType == null)
+ missingType = true;
+ else
+ missingType = false;
+ } catch (Exception ex) {
+ missingType = true;
+ error = ex;
+ }
+ if (missingType)
+ throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
+
+ if (originalTypeName == tagTypeName) {
+ mappedTypeName = tmi.MappedTagType;
+ error = null;
+ mappedType = null;
+
+ try {
+ mappedType = HttpApplication.LoadType (mappedTypeName);
+ if (mappedType == null)
+ missingType = true;
+ else
+ missingType = false;
+ } catch (Exception ex) {
+ missingType = true;
+ error = ex;
+ }
+
+ if (missingType)
+ throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
+ error);
+
+ if (!mappedType.IsSubclassOf (originalType))
+ throw new ConfigurationErrorsException (
+ String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
+
+ return mappedType;
+ }
+ }
+
+ return tagType;
+ }
+
public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
ControlBuilder parentBuilder,
Type type,
int line,
string sourceFileName)
{
- ControlBuilder builder;
- object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
+ Type tagType = MapTagType (type);
+ ControlBuilder builder;
+ object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
if (atts != null && atts.Length > 0) {
ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
builder = new ControlBuilder ();
}
- builder.Init (parser, parentBuilder, type, tagName, id, attribs);
+ builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
builder.line = line;
builder.fileName = sourceFileName;
return builder;
{
return false;
}
-
+
ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
{
- PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
+ int idx;
+ string propertyName;
+
+ if ((idx = propName.IndexOf (':')) >= 0)
+ propertyName = propName.Substring (idx + 1);
+ else
+ propertyName = propName;
+
+ PropertyInfo prop = type.GetProperty (propertyName, FlagsNoCase);
if (prop == null) {
- string msg = String.Format ("Property {0} not found in type {1}", propName, type);
+ string msg = String.Format ("Property {0} not found in type {1}", propertyName, type);
throw new HttpException (msg);
}
builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
null, atts, line, fileName);
builder.isProperty = true;
+ builder.isPropertyWritable = prop.CanWrite;
+ if (idx >= 0)
+ builder.originalTagName = propName;
return builder;
}
builder.fileName = fileName;
builder.line = line;
builder.isProperty = true;
+ builder.isPropertyWritable = prop.CanWrite;
+ if (idx >= 0)
+ builder.originalTagName = propName;
return builder;
}
{
this.parser = parser;
if (parser != null)
- this.location = parser.Location;
+ this.Location = parser.Location;
this.parentBuilder = parentBuilder;
this.type = type;
} else if (atts.Length > 0) {
ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
childrenAsProperties = att.ChildrenAsProperties;
- if (childrenAsProperties && att.DefaultProperty != "") {
+ if (childrenAsProperties && att.DefaultProperty.Length != 0)
defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
parser, null);
- }
}
}
internal string GetNextID (string proposedID)
{
- if (proposedID != null && proposedID.Trim () != "")
+ if (proposedID != null && proposedID.Trim ().Length != 0)
return proposedID;
return "_bctrl_" + nextID++;
}
+ internal string GetNextLocalVariableName (string baseName)
+ {
+ localVariableCount++;
+ return baseName + localVariableCount.ToString ();
+ }
+
internal virtual ControlBuilder CreateSubBuilder (string tagid,
- Hashtable atts,
+ IDictionary atts,
Type childType,
TemplateParser parser,
ILocation location)
{
ControlBuilder childBuilder = null;
if (childrenAsProperties) {
- if (defaultPropertyBuilder == null) {
+ if (defaultPropertyBuilder == null)
childBuilder = CreatePropertyBuilder (tagid, parser, atts);
- } else {
- childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
- null, parser, location);
+ else {
+ if (String.Compare (defaultPropertyBuilder.TagName, tagid, true, Helpers.InvariantCulture) == 0) {
+ // The child tag is the same what our default property name. Act as if there was
+ // no default property builder, or otherwise we'll end up with invalid nested
+ // builder call.
+ defaultPropertyBuilder = null;
+ childBuilder = CreatePropertyBuilder (tagid, parser, atts);
+ } else {
+ Type ct = ControlType;
+ MemberInfo[] mems = ct != null ? ct.GetMember (tagid, MemberTypes.Property, FlagsNoCase) : null;
+ PropertyInfo prop = mems != null && mems.Length > 0 ? mems [0] as PropertyInfo : null;
+
+ if (prop != null && typeof (ITemplate).IsAssignableFrom (prop.PropertyType)) {
+ childBuilder = CreatePropertyBuilder (tagid, parser, atts);
+ defaultPropertyBuilder = null;
+ } else
+ childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts, null, parser, location);
+ }
}
+
return childBuilder;
}
+ if (String.Compare (tagName, tagid, true, Helpers.InvariantCulture) == 0)
+ return null;
+
childType = GetChildControlType (tagid, atts);
if (childType == null)
return null;
}
}
}
-#if NET_2_0
+
[MonoTODO ("unsure, lack documentation")]
public virtual object BuildObject ()
{
return CreateInstance ();
}
+
+ public virtual void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,
+ CodeTypeDeclaration baseType,
+ CodeTypeDeclaration derivedType,
+ CodeMemberMethod buildMethod,
+ CodeMemberMethod dataBindingMethod)
+ {
+ // nothing to do
+ }
internal void ResetState()
{
}
}
}
-#endif
}
}