Code:
/ FXUpdate3074 / FXUpdate3074 / 1.1 / untmp / whidbey / QFE / ndp / fx / src / xsp / System / Web / UI / ControlBuilder.cs / 10 / ControlBuilder.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.UI { using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Resources; using System.Web; using System.Web.Compilation; using System.Web.RegularExpressions; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.Util; using System.CodeDom; ////// Implementation of a generic control builder used by all controls and child objects /// [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public class ControlBuilder { public readonly static string DesignerFilter = "__designer"; #if DEBUG private bool _initCalled; #endif // Parses a databinding expression (e.g. <%# i+1 %> private readonly static Regex databindRegex = new DataBindRegex(); internal readonly static Regex expressionBuilderRegex = new ExpressionBuilderRegex(); private readonly static Regex bindExpressionRegex = new BindExpressionRegex(); private readonly static Regex bindParametersRegex = new BindParametersRegex(); private readonly static Regex evalExpressionRegex = new EvalExpressionRegex(); private readonly static Regex formatStringRegex = new FormatStringRegex(); private Type _controlType; private string _tagName; private string _skinID; private ArrayList _subBuilders; private ControlBuilderParseTimeData _parseTimeData; private IServiceProvider _serviceProvider; // private ArrayList _eventEntries; private ArrayList _simplePropertyEntries; private ArrayList _complexPropertyEntries; private ArrayList _templatePropertyEntries; private ArrayList _boundPropertyEntries; private PropertyDescriptor _bindingContainerDescriptor; // // const masks into the BitVector32 private const int parseComplete = 0x00000001; private const int needsTagAttributeComputed = 0x00000002; private const int needsTagAttribute = 0x00000004; private const int doneInitObjectOptimizations = 0x00000008; private const int isICollection = 0x00000010; private const int isIParserAccessor = 0x00000020; private const int hasFilteredSimpleProps = 0x00000040; private const int hasFilteredComplexProps = 0x00000080; private const int hasFilteredTemplateProps = 0x00000100; private const int hasFilteredBoundProps = 0x00000200; private const int hasTwoWayBoundProps = 0x00000400; private const int triedFieldToControlBinding = 0x00000800; private const int hasFieldToControlBinding = 0x00001000; private const int controlTypeIsControl = 0x00002000; // Indicates that the type specified in _controlType derives from Control private const int entriesSorted = 0x00004000; private const int applyTheme = 0x00008000; #pragma warning disable 0649 private SimpleBitVector32 flags; #pragma warning restore 0649 ////// public virtual Type BindingContainerType { get { if (NamingContainerBuilder == null) { return typeof(System.Web.UI.Control); } Type ctrlType = NamingContainerBuilder.ControlType; Debug.Assert(ctrlType != null, "Control type is null."); Debug.Assert(typeof(INamingContainer).IsAssignableFrom(ctrlType), String.Format(CultureInfo.InvariantCulture, "NamingContainerBuilder.Control type {0} is not an INamingContainer", ctrlType.FullName)); // Recursively lookup if the NamingContainerBuilder.ControlType is an INonBindingContainer if (typeof(INonBindingContainer).IsAssignableFrom(ctrlType)) { return NamingContainerBuilder.BindingContainerType; } return NamingContainerBuilder.ControlType; } } internal ICollection EventEntries { get { // If there are no entries, return a static empty collection if (_eventEntries == null) return EmptyCollection.Instance; return _eventEntries; } } private ArrayList EventEntriesInternal { get { // Create the ArrayList on demand if (_eventEntries == null) _eventEntries = new ArrayList(); return _eventEntries; } } internal ICollection SimplePropertyEntries { get { // If there are no entries, return a static empty collection if (_simplePropertyEntries == null) return EmptyCollection.Instance; return _simplePropertyEntries; } } internal ArrayList SimplePropertyEntriesInternal { get { // Create the ArrayList on demand if (_simplePropertyEntries == null) _simplePropertyEntries = new ArrayList(); return _simplePropertyEntries; } } internal ICollection ComplexPropertyEntries { get { // If there are no entries, return a static empty collection if (_complexPropertyEntries == null) return EmptyCollection.Instance; return _complexPropertyEntries; } } private ArrayList ComplexPropertyEntriesInternal { get { // Create the ArrayList on demand if (_complexPropertyEntries == null) _complexPropertyEntries = new ArrayList(); return _complexPropertyEntries; } } internal ICollection TemplatePropertyEntries { get { // If there are no entries, return a static empty collection if (_templatePropertyEntries == null) return EmptyCollection.Instance; return _templatePropertyEntries; } } private ArrayList TemplatePropertyEntriesInternal { get { // Create the ArrayList on demand if (_templatePropertyEntries == null) _templatePropertyEntries = new ArrayList(); return _templatePropertyEntries; } } internal ICollection BoundPropertyEntries { get { // If there are no entries, return a static empty collection if (_boundPropertyEntries == null) return EmptyCollection.Instance; return _boundPropertyEntries; } } private ArrayList BoundPropertyEntriesInternal { get { // Create the ArrayList on demand if (_boundPropertyEntries == null) _boundPropertyEntries = new ArrayList(); return _boundPropertyEntries; } } internal bool HasFilteredBoundEntries { get { return flags[hasFilteredBoundProps]; } } internal bool IsNoCompile { get { return flags[parseComplete]; } } internal string SkinID { get { return _skinID; } set { _skinID = value; } } ////// Return the type of the control that this builder creates /// public Type ControlType { get { return _controlType; } } public IFilterResolutionService CurrentFilterResolutionService { get { if (ServiceProvider != null) { return (IFilterResolutionService)ServiceProvider.GetService(typeof(IFilterResolutionService)); } else { // If there is no ServiceProvider, use the TemplateControl ( return TemplateControl; } } } ////// Return the type that will be used by codegen to declare the control /// public virtual Type DeclareType { get { return _controlType; } } ////// Gets the IDesignerHost if we are in design mode. This is used to load /// config through IWebApplication rather than the RuntimeConfig object. /// private IDesignerHost DesignerHost { get { if (InDesigner && ParseTimeData != null) { TemplateParser parser = ParseTimeData.Parser; if (parser != null) { return parser.DesignerHost; } } return null; } } ////// /// private ControlBuilder DefaultPropertyBuilder { get { return ParseTimeData.DefaultPropertyBuilder; } } public IThemeResolutionService ThemeResolutionService { get { if (ServiceProvider != null) { return (IThemeResolutionService)ServiceProvider.GetService(typeof(IThemeResolutionService)); } else { // If there is no ServiceProvider, use the TemplateControl ( return TemplateControl as IThemeResolutionService; } } } ////// /// private EventDescriptorCollection EventDescriptors { get { if (ParseTimeData.EventDescriptors == null) { ParseTimeData.EventDescriptors = TypeDescriptor.GetEvents(_controlType); } return ParseTimeData.EventDescriptors; } } internal string Filter { get { return ParseTimeData.Filter; } set { ParseTimeData.Filter = value; } } ////// /// protected bool FChildrenAsProperties { get { return ParseTimeData.ChildrenAsProperties; } } ////// protected bool FIsNonParserAccessor { get { return ParseTimeData.IsNonParserAccessor; } } ///[To be supplied.] ////// public bool HasAspCode { get { return ParseTimeData.HasAspCode; } } /* * Return the ID of the control that this builder creates */ ///[To be supplied.] ////// public string ID { get { return ParseTimeData.ID; } set { ParseTimeData.ID = value; } } internal bool IsGeneratedID { get { return ParseTimeData.IsGeneratedID; } set { ParseTimeData.IsGeneratedID = value; } } private bool IgnoreControlProperty { get { return ParseTimeData.IgnoreControlProperties; } } ///[To be supplied.] ////// protected bool InDesigner { get { // If we're in no-compile page mode, return false if (IsNoCompile) return false; // Simply return null when called without a Parser. // This is in the codepath from TemplateBuilder.NeedsTagInnerText() // to determine designer source code preservation behavior by tools. if (Parser == null) return false; return Parser.FInDesigner; } } ///InDesigner property gets used by control builders so that they can behave /// differently if needed. ////// protected bool InPageTheme { get { return Parser is PageThemeParser; } } internal bool IsControlSkin { get { return ParentBuilder is FileLevelPageThemeBuilder; } } ///InPageTheme property indicates if the control builder is used to generate page themes. ////// /// private bool IsHtmlControl { get { return ParseTimeData.IsHtmlControl; } } ////// The source file line number at which this builder is defined /// internal int Line { get { return ParseTimeData.Line; } set { ParseTimeData.Line = value; } } public bool Localize { get { if (ParseTimeData != null) { return ParseTimeData.Localize; } return true; } } ////// /// private ControlBuilder NamingContainerBuilder { get { if (ParseTimeData.NamingContainerSearched) { return ParseTimeData.NamingContainerBuilder; } if (ParentBuilder == null || ParentBuilder.ControlType == null) { ParseTimeData.NamingContainerBuilder = null; } else if (typeof(INamingContainer).IsAssignableFrom(ParentBuilder.ControlType)) { ParseTimeData.NamingContainerBuilder = ParentBuilder; } else { ParseTimeData.NamingContainerBuilder = ParentBuilder.NamingContainerBuilder; } ParseTimeData.NamingContainerSearched = true; return ParseTimeData.NamingContainerBuilder; } } ////// /// Return the type of the naming container of the control that this builder creates /// public Type NamingContainerType { get { if (NamingContainerBuilder == null) { return typeof(System.Web.UI.Control); } return NamingContainerBuilder.ControlType; } } ////// /// internal CompilationMode CompilationMode { get { return Parser.CompilationMode; } } ////// /// internal ControlBuilder ParentBuilder { get { return ParseTimeData.ParentBuilder; } } ////// /// protected internal TemplateParser Parser { get { return ParseTimeData.Parser; } } ////// /// private ControlBuilderParseTimeData ParseTimeData { get { if (_parseTimeData == null) { if (IsNoCompile) { throw new InvalidOperationException(SR.GetString(SR.ControlBuilder_ParseTimeDataNotAvailable)); } _parseTimeData = new ControlBuilderParseTimeData(); } return _parseTimeData; } } ////// /// private PropertyDescriptorCollection PropertyDescriptors { get { if (ParseTimeData.PropertyDescriptors == null) { ParseTimeData.PropertyDescriptors = TypeDescriptor.GetProperties(_controlType); } return ParseTimeData.PropertyDescriptors; } } private StringSet PropertyEntries { get { if (ParseTimeData.PropertyEntries == null) { ParseTimeData.PropertyEntries = new CaseInsensitiveStringSet(); } return ParseTimeData.PropertyEntries; } } ////// /// internal ArrayList SubBuilders { get { if (_subBuilders == null) { _subBuilders = new ArrayList(); } return _subBuilders; } } public IServiceProvider ServiceProvider { get { return _serviceProvider; } } ////// /// private bool SupportsAttributes { get { return ParseTimeData.SupportsAttributes; } } ////// public string TagName { get { return _tagName; } } ///[To be supplied.] ////// The name of the source file in which this builder is defined /// internal VirtualPath VirtualPath { get { return ParseTimeData.VirtualPath; } set { ParseTimeData.VirtualPath = value; } } internal string VirtualPathString { get { return System.Web.VirtualPath.GetVirtualPathString(VirtualPath); } } ////// The template control that hosts the current control. For example, it is the usercontrol for controls defined in a usercontrol file. /// This property is available only in non-compiled cases. /// internal TemplateControl TemplateControl { get { HttpContext context = HttpContext.Current; if (context == null) { return null; } return context.TemplateControl; } } private void AddBoundProperty(string filter, string name, string expressionPrefix, string expression, ExpressionBuilder expressionBuilder, object parsedExpressionData, string fieldName, string formatString, bool twoWayBound) { AddBoundProperty(filter, name, expressionPrefix, expression, expressionBuilder, parsedExpressionData, false, fieldName, formatString, twoWayBound); } ////// /// private void AddBoundProperty(string filter, string name, string expressionPrefix, string expression, ExpressionBuilder expressionBuilder, object parsedExpressionData, bool generated, string fieldName, string formatString, bool twoWayBound) { Debug.Assert(!String.IsNullOrEmpty(name)); string controlID = ParseTimeData.ID; // Get the IDesignerHost in case we need it to find ExpressionBuilders IDesignerHost host = DesignerHost; if (String.IsNullOrEmpty(expressionPrefix)) { // This is a databinding entry if (String.IsNullOrEmpty(controlID)) { if (CompilationMode == CompilationMode.Never) { throw new HttpException(SR.GetString(SR.NoCompileBinding_requires_ID, _controlType.Name, fieldName)); } if (twoWayBound) { throw new HttpException(SR.GetString(SR.TwoWayBinding_requires_ID, _controlType.Name, fieldName)); } } Debug.Assert(ControlType != null, "ControlType should not be null if we're adding a property entry"); // We only support databindings on objects that have an event named "DataBinding" if (!flags[controlTypeIsControl]) { if (ControlType.GetEvent("DataBinding") == null) { throw new InvalidOperationException(SR.GetString(SR.ControlBuilder_DatabindingRequiresEvent, _controlType.FullName)); } } } else { // If we don't have an expression builder yet, go get it if (expressionBuilder == null) { expressionBuilder = ExpressionBuilder.GetExpressionBuilder(expressionPrefix, VirtualPath, host); } } Debug.Assert(!(String.IsNullOrEmpty(expressionPrefix) ^ (expressionBuilder == null)), "expressionBuilder should be non-null iff expressionPrefix is non-empty"); // Set up a BoundPropertyEntry since we know this is an expression BoundPropertyEntry entry = new BoundPropertyEntry(); entry.Filter = filter; entry.Expression = expression; entry.ExpressionBuilder = expressionBuilder; entry.ExpressionPrefix = expressionPrefix; entry.Generated = generated; entry.FieldName = fieldName; entry.FormatString = formatString; entry.ControlType = _controlType; entry.ControlID = controlID; entry.TwoWayBound = twoWayBound; entry.ParsedExpressionData = parsedExpressionData; FillUpBoundPropertyEntry(entry, name); // Check for duplicate bound property entries and throws if it finds one. // This is done here rather than on AddBoundProperty(BoundPropertyEntry entry) since // that overload can be called by other control builders in two-way binding scenarios. // In that case it is valid to have duplicate bound property entries since they are on // the BindableTemplateBuilder, not the control's ControlBuilder. foreach (BoundPropertyEntry bpe in BoundPropertyEntriesInternal) { if (String.Equals(bpe.Name, entry.Name, StringComparison.OrdinalIgnoreCase) && String.Equals(bpe.Filter, entry.Filter, StringComparison.OrdinalIgnoreCase)) { string fullPropertyName = entry.Name; if (!String.IsNullOrEmpty(entry.Filter)) { fullPropertyName = entry.Filter + ":" + fullPropertyName; } throw new InvalidOperationException(SR.GetString(SR.ControlBuilder_CannotHaveMultipleBoundEntries, fullPropertyName, ControlType)); } } // Add these to the bound entries AddBoundProperty(entry); } private void AddBoundProperty(BoundPropertyEntry entry) { // Add these to the bound entries AddEntry(BoundPropertyEntriesInternal, entry); if (entry.TwoWayBound) { // Remember that this builder has some two-way entries flags[hasTwoWayBoundProps] = true; } } private void FillUpBoundPropertyEntry(BoundPropertyEntry entry, string name) { // Grab a member info corresponding to the property string objectModelName; MemberInfo memberInfo = PropertyMapper.GetMemberInfo(_controlType, name, out objectModelName); entry.Name = objectModelName; // If we got a memberInfo if (memberInfo != null) { if (memberInfo is PropertyInfo) { // If it's a property, make sure it is persistable PropertyInfo propInfo = ((PropertyInfo)memberInfo); if (propInfo.GetSetMethod() == null) { if (!SupportsAttributes) { throw new HttpException(SR.GetString(SR.Property_readonly, name)); } else { // If the property is readonly, fall back to using SetAttribute if (entry.TwoWayBound) { entry.ReadOnlyProperty = true; } else { entry.UseSetAttribute = true; } } } else { // The property is settable, so we can use it entry.PropertyInfo = propInfo; entry.Type = propInfo.PropertyType; } } else { // If it's a field, just grab the type Debug.Assert(memberInfo is FieldInfo); entry.Type = ((FieldInfo)memberInfo).FieldType; } } // If we didn't find a member, we need to use the IAttributeAccessor else { if (!SupportsAttributes) { throw new HttpException(SR.GetString( SR.Type_doesnt_have_property, _controlType.FullName, name)); } else { if (entry.TwoWayBound) { throw new InvalidOperationException(SR.GetString(SR.ControlBuilder_TwoWayBindingNonProperty, name, ControlType.Name)); } entry.Name = name; entry.UseSetAttribute = true; } } // Make sure we have parsed expression data if (entry.ParsedExpressionData == null) { entry.ParseExpression(new ExpressionBuilderContext(VirtualPath)); } if (!Parser.IgnoreParseErrors && entry.ParsedExpressionData == null) { // Disallow empty expressions ( if (Util.IsWhiteSpaceString(entry.Expression)) { throw new HttpException( SR.GetString(SR.Empty_expression)); } } } ////// private void AddCollectionItem(ControlBuilder builder) { // Just save the builder and filter and add it to the complex entries ComplexPropertyEntry entry = new ComplexPropertyEntry(true); entry.Builder = builder; entry.Filter = String.Empty; AddEntry(ComplexPropertyEntriesInternal, entry); } ////// private void AddComplexProperty(string filter, string name, ControlBuilder builder) { // /* if (IgnoreControlProperty) { return; } */ Debug.Assert(!String.IsNullOrEmpty(name)); Debug.Assert(builder != null); // Look for a MemberInfo string objectModelName = String.Empty; MemberInfo memberInfo = PropertyMapper.GetMemberInfo(_controlType, name, out objectModelName); // Initialize the entry ComplexPropertyEntry entry = new ComplexPropertyEntry(); entry.Builder = builder; entry.Filter = filter; entry.Name = objectModelName; Type memberType = null; if (memberInfo != null) { if (memberInfo is PropertyInfo) { PropertyInfo propInfo = ((PropertyInfo)memberInfo); entry.PropertyInfo = propInfo; if (propInfo.GetSetMethod() == null) { entry.ReadOnly = true; } // Check if the property is themeable and persistable ValidatePersistable(propInfo, false, false, false, filter); memberType = propInfo.PropertyType; } else { Debug.Assert(memberInfo is FieldInfo); memberType = ((FieldInfo)memberInfo).FieldType; } entry.Type = memberType; } else { throw new HttpException(SR.GetString(SR.Type_doesnt_have_property, _controlType.FullName, name)); } // Add the entry to the complex entries AddEntry(ComplexPropertyEntriesInternal, entry); } ////// /// private void AddEntry(ArrayList entries, PropertyEntry entry) { // Only allow setting the ID property of a control using a simple property (e.g. ID="Button1"). // This restricts the user from using databinding, expressions, implicit expressions, // or inner string properties to set the ID. if (String.Equals(entry.Name, "ID", StringComparison.OrdinalIgnoreCase) && flags[controlTypeIsControl] && !(entry is SimplePropertyEntry)) { throw new HttpException(SR.GetString(SR.ControlBuilder_IDMustUseAttribute)); } // Remember the item index to perform a stable sort. entry.Index = entries.Count; // We used to sort the entries here via an insertion-type sort // But it's faster just to sort before we use the entries. entries.Add(entry); } ///// //// private void AddProperty(string filter, string name, string value, bool mainDirectiveMode) { Debug.Assert(!String.IsNullOrEmpty(name)); if (IgnoreControlProperty) { return; } string objectModelName = String.Empty; MemberInfo memberInfo = null; // This _controlType can be null if we're using a StringPropertyBuilder that has designer expandos if (_controlType != null) { if (String.Equals(name, BaseTemplateCodeDomTreeGenerator.skinIDPropertyName, StringComparison.OrdinalIgnoreCase) && flags[controlTypeIsControl]) { // Make sure there isn't filter for skinID property. if (!String.IsNullOrEmpty(filter)) { throw new InvalidOperationException(SR.GetString(SR.Illegal_Device, BaseTemplateCodeDomTreeGenerator.skinIDPropertyName)); } SkinID = value; return; } memberInfo = PropertyMapper.GetMemberInfo(_controlType, name, out objectModelName); } if (memberInfo != null) { // Found a property on the object, so start building a simple property setter SimplePropertyEntry entry = new SimplePropertyEntry(); entry.Filter = filter; entry.Name = objectModelName; entry.PersistedValue = value; Type memberType = null; if (memberInfo is PropertyInfo) { PropertyInfo propInfo = ((PropertyInfo)memberInfo); entry.PropertyInfo = propInfo; // If the property is read-only if (propInfo.GetSetMethod() == null) { if (!SupportsAttributes) { // If it doesn't support attributes, throw an exception throw new HttpException(SR.GetString(SR.Property_readonly, name)); } else { // Otherwise, use the attribute accessor entry.UseSetAttribute = true; // Use the original casing of the name from the parsed data entry.Name = name; } } ValidatePersistable(propInfo, entry.UseSetAttribute, mainDirectiveMode, true, filter); memberType = propInfo.PropertyType; } else { Debug.Assert(memberInfo is FieldInfo); memberType = ((FieldInfo)memberInfo).FieldType; } entry.Type = memberType; if (entry.UseSetAttribute) { entry.Value = value; } else { // Get the actual value for the property and store it in the entry object objectValue = PropertyConverter.ObjectFromString(memberType, memberInfo, value); DesignTimePageThemeParser themeParser = Parser as DesignTimePageThemeParser; if (themeParser != null) { object[] attrs = memberInfo.GetCustomAttributes(typeof(UrlPropertyAttribute), true); if (attrs.Length > 0) { string url = objectValue.ToString(); // Do not combine the url if it's apprelative, let controls resolve the url. if (UrlPath.IsRelativeUrl(url) && !UrlPath.IsAppRelativePath(url)) { objectValue = themeParser.ThemePhysicalPath + url; } } } entry.Value = objectValue; // if (memberType.IsEnum) { if (objectValue == null) { throw new HttpException(SR.GetString(SR.Invalid_enum_value, value, name, entry.Type.FullName)); } entry.PersistedValue = Enum.Format(memberType, objectValue, "G"); } else if (memberType == typeof(Boolean)) { // if (objectValue == null) { entry.Value = true; } } } AddEntry(SimplePropertyEntriesInternal, entry); } else { bool foundEvent = false; // Check if the property is actually an event handler if (StringUtil.StringStartsWithIgnoreCase(name, "on")) { string eventName = name.Substring(2); EventDescriptor eventDesc = EventDescriptors.Find(eventName, true); if (eventDesc != null) { if (InPageTheme) { throw new HttpException(SR.GetString(SR.Property_theme_disabled, eventName, ControlType.FullName)); } if (value != null) value = value.Trim(); if (String.IsNullOrEmpty(value)) { throw new HttpException(SR.GetString(SR.Event_handler_cant_be_empty, name)); } if (filter.Length > 0) { throw new HttpException(SR.GetString(SR.Events_cant_be_filtered, filter, name)); } foundEvent = true; // First, give the PageParserFilter a chance to handle the event hookup if (!Parser.PageParserFilterProcessedEventHookupAttribute(ID, eventDesc.Name, value)) { // Make sure event handlers are allowed. In no-compile pages, they aren't. ( Parser.OnFoundEventHandler(name); EventEntry entry = new EventEntry(); entry.Name = eventDesc.Name; entry.HandlerType = eventDesc.EventType; entry.HandlerMethodName = value; EventEntriesInternal.Add(entry); } } } // If we didn't find an eventhandler, we need to use the IAttributeAccessor if (!foundEvent) { // Allow the designer filter expandos for simple attributes if (!SupportsAttributes && (filter != DesignerFilter)) { if (_controlType != null) { throw new HttpException(SR.GetString(SR.Type_doesnt_have_property, _controlType.FullName, name)); } else { throw new HttpException(SR.GetString(SR.Property_doesnt_have_property, TagName, name)); } } SimplePropertyEntry entry = new SimplePropertyEntry(); entry.Filter = filter; entry.Name = name; entry.PersistedValue = value; entry.UseSetAttribute = true; entry.Value = value; AddEntry(SimplePropertyEntriesInternal, entry); } } } ////// private void AddTemplateProperty(string filter, string name, TemplateBuilder builder) { /* Do not ignore template properties since we do want to generate IDs for controls defined inside SingleInstanceTemplates. */ Debug.Assert(!String.IsNullOrEmpty(name)); Debug.Assert(builder != null); // Look for a MemberInfo string objectModelName = String.Empty; MemberInfo memberInfo = PropertyMapper.GetMemberInfo(_controlType, name, out objectModelName); // Setup a template entry bool bindableTemplate = builder is BindableTemplateBuilder; TemplatePropertyEntry entry = new TemplatePropertyEntry(bindableTemplate); entry.Builder = builder; entry.Filter = filter; entry.Name = objectModelName; Type memberType = null; if (memberInfo != null) { if (memberInfo is PropertyInfo) { PropertyInfo propInfo = ((PropertyInfo)memberInfo); entry.PropertyInfo = propInfo; ValidatePersistable(propInfo, false, false, false, filter); // Check the attribute on the property to see if it has a ContainerType TemplateContainerAttribute templateAttrib = (TemplateContainerAttribute)Attribute.GetCustomAttribute(propInfo, typeof(TemplateContainerAttribute), false); if (templateAttrib != null) { if (!typeof(INamingContainer).IsAssignableFrom(templateAttrib.ContainerType)) { throw new HttpException(SR.GetString(SR.Invalid_template_container, name, templateAttrib.ContainerType.FullName)); } // If it had one, make sure the builder knows what type it is builder.SetControlType(templateAttrib.ContainerType); } entry.Type = propInfo.PropertyType; } else { Debug.Assert(memberInfo is FieldInfo); memberType = ((FieldInfo)memberInfo).FieldType; } entry.Type = memberType; } else { throw new HttpException(SR.GetString(SR.Type_doesnt_have_property, _controlType.FullName, name)); } // Add it to the template entries AddEntry(TemplatePropertyEntriesInternal, entry); } ////// /// internal void AddSubBuilder(object o) { SubBuilders.Add(o); } internal bool HasTwoWayBoundProperties { get { return flags[hasTwoWayBoundProps]; } } ////// /// public virtual bool AllowWhitespaceLiterals() { return true; } ////// /// public virtual void AppendLiteralString(string s) { // Ignore null strings if (s == null) { return; } // If we are not building a control, or if our children define // properties, we should not get literal strings. Ignore whitespace // ones, and fail for others if (FIsNonParserAccessor || FChildrenAsProperties) { // If there is a default property, delegate to its builder if (DefaultPropertyBuilder != null) { DefaultPropertyBuilder.AppendLiteralString(s); return; } s = s.Trim(); if (FChildrenAsProperties) { // Throw a better error message if the content start with the '<' char. if (s.StartsWith("<", StringComparison.OrdinalIgnoreCase)) { throw new HttpException(SR.GetString(SR.Literal_content_not_match_property, _controlType.FullName, s)); } } if (s.Length != 0) { throw new HttpException(SR.GetString(SR.Literal_content_not_allowed, _controlType.FullName, s)); } return; } // Ignore literals that are just whitespace if the control does not want them if ((AllowWhitespaceLiterals() == false) && Util.IsWhiteSpaceString(s)) return; // A builder can specify its strings need to be html decoded if (HtmlDecodeLiterals()) { s = HttpUtility.HtmlDecode(s); } // If the last builder is a DataBoundLiteralControlBuilder, add the string // to it instead of to our list of sub-builders object lastBuilder = GetLastBuilder(); DataBoundLiteralControlBuilder dataBoundBuilder = lastBuilder as DataBoundLiteralControlBuilder; if (dataBoundBuilder != null) { Debug.Assert(!InDesigner, "!InDesigner"); dataBoundBuilder.AddLiteralString(s); } else { AddSubBuilder(s); } } ////// /// public virtual void AppendSubBuilder(ControlBuilder subBuilder) { // Tell the sub builder that it's about to be appended to its parent subBuilder.OnAppendToParentBuilder(this); if (FChildrenAsProperties) { // Don't allow code blocks when properties are expected (ASURT 97838) if (subBuilder is CodeBlockBuilder) { throw new HttpException(SR.GetString(SR.Code_not_supported_on_not_controls)); } // If there is a default property, delegate to its builder if (DefaultPropertyBuilder != null) { DefaultPropertyBuilder.AppendSubBuilder(subBuilder); return; } // The tagname is the property name string propName = subBuilder.TagName; if (subBuilder is TemplateBuilder) { TemplateBuilder tplBuilder = (TemplateBuilder)subBuilder; AddTemplateProperty(tplBuilder.Filter, propName, tplBuilder); } else if (subBuilder is CollectionBuilder) { // If there are items in the collection, add them if ((subBuilder.SubBuilders != null) && (subBuilder.SubBuilders.Count > 0)) { IEnumerator subBuilders = subBuilder.SubBuilders.GetEnumerator(); while (subBuilders.MoveNext()) { ControlBuilder builder = (ControlBuilder)subBuilders.Current; subBuilder.AddCollectionItem(builder); } subBuilder.SubBuilders.Clear(); AddComplexProperty(subBuilder.Filter, propName, subBuilder); } } else if (subBuilder is StringPropertyBuilder) { // Trim this so whitespace doesn't matter inside a tag? string text = ((StringPropertyBuilder)subBuilder).Text.Trim(); if (!String.IsNullOrEmpty(text)) { // Make sure we haven't set this property in the attributes already (special case for TextBox and similar things) AddComplexProperty(subBuilder.Filter, propName, subBuilder); } } else { AddComplexProperty(subBuilder.Filter, propName, subBuilder); } return; } CodeBlockBuilder codeBlockBuilder = subBuilder as CodeBlockBuilder; if (codeBlockBuilder != null) { // Don't allow code blocks inside non-control tags (ASURT 76719) if (ControlType != null && !flags[controlTypeIsControl]) { throw new HttpException(SR.GetString(SR.Code_not_supported_on_not_controls)); } // Is it a databinding expression? <%# ... %> if (codeBlockBuilder.BlockType == CodeBlockType.DataBinding) { // Bind statements are not allowed as DataBoundLiterals inside any template. // Outside a template, they should be treated as calls to page code. Match match; if ((match = bindExpressionRegex.Match(codeBlockBuilder.Content, 0)).Success) { ControlBuilder currentBuilder = this; while (currentBuilder != null && !(currentBuilder is TemplateBuilder)) { currentBuilder = currentBuilder.ParentBuilder; } if (currentBuilder != null && currentBuilder.ParentBuilder != null && currentBuilder is TemplateBuilder) { throw new HttpException(SR.GetString(SR.DataBoundLiterals_cant_bind)); } } if (InDesigner) { // In the designer, don't use the fancy multipart DataBoundLiteralControl, // which breaks a number of things (ASURT 82925,86738). Instead, use the // simpler DesignerDataBoundLiteralControl, and do standard databinding // on its Text property. IDictionary attribs = new ParsedAttributeCollection(); attribs.Add("Text", "<%#" + codeBlockBuilder.Content + "%>"); subBuilder = CreateBuilderFromType(Parser, this, typeof(DesignerDataBoundLiteralControl), null, null, attribs, codeBlockBuilder.Line, codeBlockBuilder.VirtualPathString); } else { // Get the last builder, and object lastBuilder = GetLastBuilder(); DataBoundLiteralControlBuilder dataBoundBuilder = lastBuilder as DataBoundLiteralControlBuilder; // If not, then we need to create one. Otherwise, just append to the // existing one bool fNewDataBoundLiteralControl = false; if (dataBoundBuilder == null) { dataBoundBuilder = new DataBoundLiteralControlBuilder(); dataBoundBuilder.Init(Parser, this, typeof(DataBoundLiteralControl), null, null, null); dataBoundBuilder.Line = codeBlockBuilder.Line; dataBoundBuilder.VirtualPath = codeBlockBuilder.VirtualPath; fNewDataBoundLiteralControl = true; // If the previous builder was a string, add it as the first // entry in the composite control. string s = lastBuilder as string; if (s != null) { SubBuilders.RemoveAt(SubBuilders.Count - 1); dataBoundBuilder.AddLiteralString(s); } } dataBoundBuilder.AddDataBindingExpression(codeBlockBuilder); if (!fNewDataBoundLiteralControl) return; subBuilder = dataBoundBuilder; } } else { // Set a flag if there is at least one block of ASP code ParseTimeData.HasAspCode = true; } } if (FIsNonParserAccessor) { throw new HttpException(SR.GetString(SR.Children_not_supported_on_not_controls)); } AddSubBuilder(subBuilder); } ////// Builds all child ControlBuilders of this ControlBuilder. /// Only used in the no-compile scenario. /// internal virtual void BuildChildren(object parentObj) { // Create all the children if (_subBuilders != null) { IEnumerator en = _subBuilders.GetEnumerator(); for (int i = 0; en.MoveNext(); i++) { object childObj; object cur = en.Current; if (cur is string) { childObj = new LiteralControl((string)cur); } else if (cur is CodeBlockBuilder) { // continue; } else { ControlBuilder controlBuilder = (ControlBuilder)cur; Debug.Assert(controlBuilder.ServiceProvider == null); controlBuilder.SetServiceProvider(ServiceProvider); try { childObj = controlBuilder.BuildObject(flags[applyTheme]); // If it's a user control, call its InitializeAsUserControl if (!InDesigner) { UserControl uc = childObj as UserControl; if (uc != null) { Control parent = parentObj as Control; Debug.Assert(parent != null); uc.InitializeAsUserControl(parent.Page); } } } finally { controlBuilder.SetServiceProvider(null); } } Debug.Assert(childObj != null); Debug.Assert(typeof(IParserAccessor).IsAssignableFrom(parentObj.GetType())); ((IParserAccessor)parentObj).AddParsedSubObject(childObj); } } } ////// This code is only used in the no-compile mode. /// It is used at design-time and when the user calls Page.ParseControl. /// public virtual object BuildObject() { return BuildObjectInternal(); } // Helper which sets themebility (This will only ever be true in the designer) internal object BuildObject(bool shouldApplyTheme) { if (flags[applyTheme] != shouldApplyTheme) flags[applyTheme] = shouldApplyTheme; return BuildObject(); } internal object BuildObjectInternal() { // Can't assertt these anymore since we've discarded this information by the time we call this method // Debug.Assert(InDesigner || CompilationMode == CompilationMode.Never, "Expected to be in designer mode."); // Since getting the ConstructorNeedsTagAttribute is very expensive, cache // the result in a flag if (!flags[needsTagAttributeComputed]) { // If it has a ConstructorNeedsTagAttribute, it needs a tag name ConstructorNeedsTagAttribute cnta = (ConstructorNeedsTagAttribute)TypeDescriptor.GetAttributes(ControlType)[typeof(ConstructorNeedsTagAttribute)]; if (cnta != null && cnta.NeedsTag) { flags[needsTagAttribute] = true; } // Remember that we have cached it flags[needsTagAttributeComputed] = true; } Object obj; if (flags[needsTagAttribute]) { // Create the object, using its ctor that takes the tag name Object[] args = new Object[] { TagName }; obj = HttpRuntime.CreatePublicInstance(_controlType, args); } else { // Create the object obj = HttpRuntime.FastCreatePublicInstance(_controlType); } if (flags[applyTheme]) obj = GetThemedObject(obj); InitObject(obj); return obj; } ////// Called when the parser is done with parsing for this ControlBuilder. /// public virtual void CloseControl() { } internal static ParsedAttributeCollection ConvertDictionaryToParsedAttributeCollection(IDictionary attribs) { if (attribs is ParsedAttributeCollection) { return (ParsedAttributeCollection)attribs; } // Assert here so we know our own code is never passign in a plain IDictionary // System.Web.Mobile does this, so we don't assertt // Debug.Assert(false); ParsedAttributeCollection newAttribs = new ParsedAttributeCollection(); foreach (DictionaryEntry entry in attribs) { newAttribs.AddFilteredAttribute(String.Empty, entry.Key.ToString(), entry.Value.ToString()); } return newAttribs; } internal ControlBuilder CreateChildBuilder(string filter, string tagName, IDictionary attribs, TemplateParser parser, ControlBuilder parentBuilder, string id, int line, VirtualPath virtualPath, ref Type childType, bool defaultProperty) { ControlBuilder subBuilder; if (FChildrenAsProperties) { // If there is a default property, delegate to its builder if (DefaultPropertyBuilder != null) { // PropertyInfo pInfo = _controlType.GetProperty(tagName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase); if (pInfo != null) { subBuilder = GetChildPropertyBuilder(tagName, attribs, ref childType, parser, false); // If items have already been added to the default builder, throw an exception if (DefaultPropertyBuilder.SubBuilders.Count > 0) { ParseChildrenAttribute pca = null; object[] attrs = ControlType.GetCustomAttributes(typeof(ParseChildrenAttribute), /*inherit*/ true); pca = (ParseChildrenAttribute)attrs[0]; Debug.Assert(pca != null); throw new HttpException(SR.GetString(SR.Cant_use_default_items_and_filtered_collection, _controlType.FullName, pca.DefaultProperty)); } // Can't use the default property builder anymore since we have filtered collections ParseTimeData.DefaultPropertyBuilder = null; } else { subBuilder = DefaultPropertyBuilder.CreateChildBuilder(filter, tagName, attribs, parser, parentBuilder, id, line, virtualPath, ref childType, false /*defaultProperty*/); } } else { subBuilder = GetChildPropertyBuilder(tagName, attribs, ref childType, parser, defaultProperty); } } else { string fullTagName = Util.CreateFilteredName(filter, tagName); childType = GetChildControlType(fullTagName, attribs); if (childType == null) { return null; } // We have to pass in the fullTagName since these will be actual registered controls subBuilder = CreateBuilderFromType(parser, parentBuilder, childType, fullTagName, id, attribs, line, VirtualPathString); } if (subBuilder == null) { return null; } subBuilder.Filter = filter; subBuilder.SetParentBuilder((parentBuilder != null) ? parentBuilder : this); return subBuilder; } ////// Create a ControlBuilder for a given tag /// public static ControlBuilder CreateBuilderFromType(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, IDictionary attribs, int line, string sourceFileName) { ControlBuilder builder = CreateBuilderFromType(type); // builder.Line = line; builder.VirtualPath = System.Web.VirtualPath.CreateAllowNull(sourceFileName); // Initialize the builder builder.Init(parser, parentBuilder, type, tagName, id, attribs); return builder; } #if !DONTUSEFACTORYGENERATOR // Cache instances of IWebObjectFactory for each control Type, which allow us // to instantiate the builders very efficiently, compared to calling // GetCustomAttributes and Activator.CreateInstance on every call. private static FactoryGenerator s_controlBuilderFactoryGenerator; #endif // DONTUSEFACTORYGENERATOR private static Hashtable s_controlBuilderFactoryCache; private static ControlBuilder CreateBuilderFromType(Type type) { // Create the factory generator on demand if (s_controlBuilderFactoryCache == null) { #if !DONTUSEFACTORYGENERATOR s_controlBuilderFactoryGenerator = new FactoryGenerator(); #endif // DONTUSEFACTORYGENERATOR // Create the factory cache s_controlBuilderFactoryCache = Hashtable.Synchronized(new Hashtable()); // Seed the cache with a few types that we don't want to expose as public (they // need to be public for FactoryGenerator to be used). s_controlBuilderFactoryCache[typeof(Content)] = new ContentBuilderInternalFactory(); s_controlBuilderFactoryCache[typeof(ContentPlaceHolder)] = new ContentPlaceHolderBuilderFactory(); } // First, IWebObjectFactory factory = (IWebObjectFactory)s_controlBuilderFactoryCache[type]; if (factory == null) { // Check whether the control's class exposes a custom builder type ControlBuilderAttribute cba = GetControlBuilderAttribute(type); if (cba != null) { // Make sure the type has the correct base class (ASURT 123677) Util.CheckAssignableType(typeof(ControlBuilder), cba.BuilderType); #if !DONTUSEFACTORYGENERATOR if (cba.BuilderType.IsPublic) { // If the builder type is public, codegen a fast factory for it factory = s_controlBuilderFactoryGenerator.CreateFactory(cba.BuilderType); } else { Debug.Assert(false, "The type " + cba.BuilderType.Name + " should be made public for better performance."); #endif // DONTUSEFACTORYGENERATOR // It's not public, so we must stick with slower reflection factory = new ReflectionBasedControlBuilderFactory(cba.BuilderType); #if !DONTUSEFACTORYGENERATOR } #endif // DONTUSEFACTORYGENERATOR } else { // use a factory that creates generic builders (i.e. ControlBuilder's) factory = s_defaultControlBuilderFactory; } // Cache the factory s_controlBuilderFactoryCache[type] = factory; } return (ControlBuilder) factory.CreateInstance(); } private static ControlBuilderAttribute GetControlBuilderAttribute(Type controlType) { // Check whether the control's class exposes a custom builder type ControlBuilderAttribute cba = null; object[] attrs = controlType.GetCustomAttributes(typeof(ControlBuilderAttribute), /*inherit*/ true); if ((attrs != null) && (attrs.Length > 0)) { Debug.Assert(attrs[0] is ControlBuilderAttribute); cba = (ControlBuilderAttribute)attrs[0]; } return cba; } private ControlBuilder GetChildPropertyBuilder(string tagName, IDictionary attribs, ref Type childType, TemplateParser templateParser, bool defaultProperty) { Debug.Assert(FChildrenAsProperties, "ChildrenAsProperties"); // Parse the device filter if any // The child is supposed to be a property, so look for it PropertyInfo pInfo = _controlType.GetProperty(tagName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase); if (pInfo == null) { throw new HttpException(SR.GetString(SR.Type_doesnt_have_property, _controlType.FullName, tagName)); } // Get its type childType = pInfo.PropertyType; ControlBuilder builder = null; // If it's a collection, return the collection builder if (typeof(ICollection).IsAssignableFrom(childType)) { // Check whether the prop has an IgnoreUnknownContentAttribute IgnoreUnknownContentAttribute attr = (IgnoreUnknownContentAttribute)Attribute.GetCustomAttribute(pInfo, typeof(IgnoreUnknownContentAttribute), true); builder = new CollectionBuilder(attr != null /*ignoreUnknownContent*/); } else if (typeof(ITemplate).IsAssignableFrom(childType)) { bool useBindableTemplate = false; bool allowMultipleInstances = true; object[] containerAttrs = pInfo.GetCustomAttributes(typeof(TemplateContainerAttribute), /*inherits*/ false); if ((containerAttrs != null) && (containerAttrs.Length > 0)) { Debug.Assert(containerAttrs[0] is TemplateContainerAttribute); useBindableTemplate = (((TemplateContainerAttribute)containerAttrs[0]).BindingDirection == BindingDirection.TwoWay); } object[] instanceAttrs = pInfo.GetCustomAttributes(typeof(TemplateInstanceAttribute), /*inherits*/ false); if ((instanceAttrs != null) && (instanceAttrs.Length > 0)) { Debug.Assert(instanceAttrs[0] is TemplateInstanceAttribute); allowMultipleInstances = (((TemplateInstanceAttribute)instanceAttrs[0]).Instances == TemplateInstance.Multiple); } if (useBindableTemplate) { // If it's a bindable template, return the bindable template builder builder = new BindableTemplateBuilder(); } else { // If it's a template, return the template builder builder = new TemplateBuilder(); } if (builder is TemplateBuilder) { ((TemplateBuilder)builder).AllowMultipleInstances = allowMultipleInstances; // If we're in the designer, set a reference to the designer host // so we can get to a filter resolution service later if (InDesigner) { ((TemplateBuilder)builder).SetDesignerHost(templateParser.DesignerHost); } } } else if (childType == typeof(string)) { PersistenceModeAttribute persistenceAttr = (PersistenceModeAttribute)Attribute.GetCustomAttribute(pInfo, typeof(PersistenceModeAttribute), true); if (((persistenceAttr == null) || (persistenceAttr.Mode == PersistenceMode.Attribute)) && !defaultProperty) { // If the property is supposed to be declared as an attribute on a control tag, throw if it was declared as an inner property // We don't throw if we are simply building the DefaultPropertyBuilder. throw new HttpException(SR.GetString(SR.ControlBuilder_CannotHaveComplexString, _controlType.FullName, tagName)); } builder = new StringPropertyBuilder(); } if (builder != null) { builder.Line = Line; builder.VirtualPath = VirtualPath; // Initialize the builder builder.Init(Parser, (ControlBuilder)this, null, tagName, null, attribs); return builder; } // Otherwise, simply return the builder for the property builder = CreateBuilderFromType(Parser, this, childType, tagName, null, attribs, Line, VirtualPathString); return builder; } ////// When overridden, returns the control type of any parsed child controls /// public virtual Type GetChildControlType(string tagName, IDictionary attribs) { return null; } ////// Convenience method for grabbing the set of property entries that we need to set /// on an instance of the object this controlbuilder builds. /// internal ICollection GetFilteredPropertyEntrySet(ICollection entries) { // If we encounter the default value, put it in the table if it doesn't already exist // If we encounter the filter value, replace whatevers in the table. IDictionary filteredEntries = new HybridDictionary(true); IFilterResolutionService filterResolutionService = CurrentFilterResolutionService; if (filterResolutionService != null) { foreach (PropertyEntry entry in entries) { if (!filteredEntries.Contains(entry.Name)) { String filter = entry.Filter; // empty filter always matches. if (String.IsNullOrEmpty(filter) || filterResolutionService.EvaluateFilter(filter)) { filteredEntries[entry.Name] = entry; } } } } else { // If there isn't a filter resolution service, just add anything from the default filter foreach (PropertyEntry entry in entries) { if (String.IsNullOrEmpty(entry.Filter)) { filteredEntries[entry.Name] = entry; } } } return filteredEntries.Values; } // Check if any of the entries have a filter private bool HasFilteredEntries(ICollection entries) { foreach (PropertyEntry entry in entries) { if (entry.Filter.Length > 0) return true; } // None of the entries are filtered return false; } ////// Return the last sub builder added to this builder /// internal object GetLastBuilder() { if (SubBuilders.Count == 0) { return null; } return SubBuilders[SubBuilders.Count - 1]; } public ObjectPersistData GetObjectPersistData() { return new ObjectPersistData(this, Parser.RootBuilder.BuiltObjects); } ////// Does this control have a body. e.g. public virtual bool HasBody() { return true; } public virtual bool HtmlDecodeLiterals() { return false; } ///doesn't. /// /// @param parser The instance of the parser that is controlling us. /// @param tagName The name of the tag to be built. This is necessary /// to allow a builder to support multiple tag types. /// @param attribs IDictionary which holds all the attributes of /// the tag. It is immutable. /// @param type Type of the control that this builder will create. /// public virtual void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, IDictionary attribs) { #if DEBUG Debug.Assert(!_initCalled, "ControlBuilder.Init() should never be called more than once on the same ControlBuilder."); _initCalled = true; #endif ParseTimeData.Parser = parser; ParseTimeData.ParentBuilder = parentBuilder; if (parser != null) { ParseTimeData.IgnoreControlProperties = parser.IgnoreControlProperties; } _tagName = tagName; if (type != null) { _controlType = type; flags[controlTypeIsControl] = typeof(Control).IsAssignableFrom(_controlType); ID = id; // Try to get a ParseChildrenAttribute from the object ParseChildrenAttribute pca = GetParseChildrenAttribute(type); // Is this a builder for an object that implements IParserAccessor? if (!typeof(IParserAccessor).IsAssignableFrom(type)) { ParseTimeData.IsNonParserAccessor = true; // Non controls never have children ParseTimeData.ChildrenAsProperties = true; } else { // Check if the nested tags define properties, as opposed to children if (pca != null) { ParseTimeData.ChildrenAsProperties = pca.ChildrenAsProperties; } } if (FChildrenAsProperties) { // Check if there is a default property if (pca != null && pca.DefaultProperty.Length != 0) { Type subType = null; // Create a builder for the default prop // Default property is always the default filter ParseTimeData.DefaultPropertyBuilder = CreateChildBuilder(String.Empty, pca.DefaultProperty, null/*attribs*/, parser, null, null /*id*/, Line, VirtualPath, ref subType, true /*defaultProperty*/); Debug.Assert(DefaultPropertyBuilder != null, pca.DefaultProperty); } } // Check if the object is an HtmlControl ParseTimeData.IsHtmlControl = typeof(HtmlControl).IsAssignableFrom(_controlType); // Check if the object supports attributes ParseTimeData.SupportsAttributes = typeof(IAttributeAccessor).IsAssignableFrom(_controlType); } else { flags[controlTypeIsControl] = false; } // Process the attributes, if any if (attribs != null) { // This could be called by anyone, so if it's not the parser that's calling us // we have to copy all the attribs values over to a ParsedAttributeCollection // in the default filter PreprocessAttributes(ConvertDictionaryToParsedAttributeCollection(attribs)); } // Check if the same control with identical skinID is already defined as a control skin. // If so, fails now instead of infinite recursion during runtime or designtime rendering. // if (InPageTheme) { ControlBuilder builder = ((PageThemeParser)parser).CurrentSkinBuilder; if (builder != null && builder.ControlType == ControlType && String.Equals(builder.SkinID, SkinID, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException(SR.GetString(SR.Cannot_set_recursive_skin, builder.ControlType.Name)); } } } // Cache the custom ParseChildrenAttribute for each type to avoid having to call // GetCustomAttributes any more than necessary (it's extremely slow) private static ParseChildrenAttribute s_markerParseChildrenAttribute = new ParseChildrenAttribute(); private static Hashtable s_parseChildrenAttributeCache = new Hashtable(); private static ParseChildrenAttribute GetParseChildrenAttribute(Type controlType) { // First, see if we have it cached for this type ParseChildrenAttribute pca = (ParseChildrenAttribute)s_parseChildrenAttributeCache[controlType]; if (pca == null) { // Try to get a ParseChildrenAttribute from the type object[] attrs = controlType.GetCustomAttributes(typeof(ParseChildrenAttribute), /*inherit*/ true); if ((attrs != null) && (attrs.Length > 0)) { Debug.Assert(attrs[0] is ParseChildrenAttribute); pca = (ParseChildrenAttribute)attrs[0]; } // If it doesn't have one, use a default as a marker if (pca == null) pca = s_markerParseChildrenAttribute; // Cache the ParseChildrenAttribute lock (s_parseChildrenAttributeCache.SyncRoot) { s_parseChildrenAttributeCache[controlType] = pca; } } // If it's the marker one, just return null if (pca == s_markerParseChildrenAttribute) return null; return pca; } private void DoInitObjectOptimizations(object obj) { // Cache whether it's an ICollection, since IsAssignableFrom is expensive flags[isICollection] = typeof(ICollection).IsAssignableFrom(ControlType); // Cache whether it's an IParserAccessor, since IsAssignableFrom is expensive flags[isIParserAccessor] = typeof(IParserAccessor).IsAssignableFrom(obj.GetType()); if (_simplePropertyEntries != null) { flags[hasFilteredSimpleProps] = HasFilteredEntries(_simplePropertyEntries); } if (_complexPropertyEntries != null) { flags[hasFilteredComplexProps] = HasFilteredEntries(_complexPropertyEntries); } if (_templatePropertyEntries != null) { flags[hasFilteredTemplateProps] = HasFilteredEntries(_templatePropertyEntries); } if (_boundPropertyEntries != null) { flags[hasFilteredBoundProps] = HasFilteredEntries(_boundPropertyEntries); } } internal virtual object GetThemedObject(object obj) { Control control = obj as Control; if (control == null) return obj; IThemeResolutionService themeService = ThemeResolutionService; if (themeService != null) { if (!String.IsNullOrEmpty(SkinID)) { control.SkinID = SkinID; } // Apply the theme builders before we run the regular builders ThemeProvider themeProvider = themeService.GetStylesheetThemeProvider(); SkinBuilder themeBuilder = null; if (themeProvider != null) { themeBuilder = themeProvider.GetSkinBuilder(control); if (themeBuilder != null) { try { themeBuilder.SetServiceProvider(ServiceProvider); return themeBuilder.ApplyTheme(); } finally { themeBuilder.SetServiceProvider(null); } } } } return control; } ////// Sets all the properties of a built object corresponding to this ControlBuilder. /// This code is only used in the no-compile and designer modes /// internal virtual void InitObject(object obj) { // Can't assertt these anymore since we've discarded this information by the time we call this method // Debug.Assert(InDesigner || CompilationMode == CompilationMode.Never, "Expected to be in designer mode."); // Make sure we initialize the property entries in the right order EnsureEntriesSorted(); // Do some expensive one time pre-computations on demand if (!flags[doneInitObjectOptimizations]) { DoInitObjectOptimizations(obj); flags[doneInitObjectOptimizations] = true; } Control control = obj as Control; if (control != null) { if (InDesigner) { control.SetDesignMode(); } if (SkinID != null) { control.SkinID = SkinID; } // Need to apply stylesheet on the controls in non-compiled pages. if (!InDesigner && TemplateControl != null) { control.ApplyStyleSheetSkin(TemplateControl.Page); } } InitSimpleProperties(obj); if (flags[isICollection]) { InitCollectionsComplexProperties(obj); } else { InitComplexProperties(obj); } if (InDesigner) { if (control != null) { if (Parser.DesignTimeDataBindHandler != null) { control.DataBinding += Parser.DesignTimeDataBindHandler; } // Set a reference to the control builder that created this object control.SetControlBuilder(this); } Parser.RootBuilder.BuiltObjects[obj] = this; } InitBoundProperties(obj); if (flags[isIParserAccessor]) { // Build the children BuildChildren(obj); } InitTemplateProperties(obj); if (control != null) BindFieldToControl(control); // } private void InitSimpleProperties(object obj) { // Don't do anything if there are no entries if (_simplePropertyEntries == null) return; // If there are no filters in the picture, use the entries as is ICollection entries; if (flags[hasFilteredSimpleProps]) entries = GetFilteredPropertyEntrySet(SimplePropertyEntries); else entries = SimplePropertyEntries; // Now that we have the proper set, set all the entries foreach (SimplePropertyEntry entry in entries) { SetSimpleProperty(entry, obj); } } internal void SetSimpleProperty(SimplePropertyEntry entry, object obj) { if (entry.UseSetAttribute) { ((IAttributeAccessor)obj).SetAttribute(entry.Name, entry.Value.ToString()); } else { try { PropertyMapper.SetMappedPropertyValue(obj, entry.Name, entry.Value); } catch (Exception e) { throw new HttpException(SR.GetString(SR.Cannot_set_property, entry.PersistedValue, entry.Name), e); } } } private void InitCollectionsComplexProperties(object obj) { // Don't do anything if there are no entries if (_complexPropertyEntries == null) return; foreach (ComplexPropertyEntry entry in ComplexPropertyEntries) { try { ControlBuilder controlBuilder = ((ComplexPropertyEntry)entry).Builder; Debug.Assert(((ComplexPropertyEntry)entry).IsCollectionItem, "The entry should be a collection entry, instead it's a " + entry.GetType()); object objValue; Debug.Assert(controlBuilder.ServiceProvider == null); controlBuilder.SetServiceProvider(ServiceProvider); try { objValue = controlBuilder.BuildObject(flags[applyTheme]); } finally { controlBuilder.SetServiceProvider(null); } object[] parameters = new object[1]; parameters[0] = objValue; MethodInfo methodInfo = ControlType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { objValue.GetType() }, null); if (methodInfo == null) { throw new InvalidOperationException(SR.GetString(SR.ControlBuilder_CollectionHasNoAddMethod, TagName)); } Util.InvokeMethod(methodInfo, obj, parameters); } catch (Exception ex) { throw new HttpException(SR.GetString(SR.Cannot_add_value_not_collection, TagName, ex.Message), ex); } } } private void InitComplexProperties(object obj) { // Don't do anything if there are no entries if (_complexPropertyEntries == null) return; // If there are no filters in the picture, use the entries as is ICollection entries; if (flags[hasFilteredComplexProps]) entries = GetFilteredPropertyEntrySet(ComplexPropertyEntries); else entries = ComplexPropertyEntries; foreach (ComplexPropertyEntry entry in entries) { if (entry.ReadOnly) { try { object objectValue = FastPropertyAccessor.GetProperty(obj, entry.Name); entry.Builder.SetServiceProvider(ServiceProvider); try { // We must push the theme flag to child complex objects so they are init'd properly // DevDiv Bug 59351 // Set applytheme only when necessary. if (entry.Builder.flags[applyTheme] != flags[applyTheme]) { entry.Builder.flags[applyTheme] = flags[applyTheme]; } entry.Builder.InitObject(objectValue); } finally { entry.Builder.SetServiceProvider(null); } } catch (Exception e) { throw new HttpException(SR.GetString(SR.Cannot_init, entry.Name, e.Message), e); } } else { try { ControlBuilder controlBuilder = entry.Builder; Debug.Assert(controlBuilder.ServiceProvider == null); object objectValue = null; controlBuilder.SetServiceProvider(ServiceProvider); try { objectValue = controlBuilder.BuildObject(flags[applyTheme]); } finally { controlBuilder.SetServiceProvider(null); } // Use the FastPropertyAccessor to assign the value FastPropertyAccessor.SetProperty(obj, entry.Name, objectValue); } catch (Exception e) { throw new HttpException(SR.GetString(SR.Cannot_set_property, TagName, entry.Name), e); } } } } private void InitBoundProperties(object obj) { // Don't do anything if there are no entries if (_boundPropertyEntries == null) return; DataBindingCollection dataBindings = null; IAttributeAccessor attributeAccessor = null; // If there are no filters in the picture, use the entries as is ICollection entries; if (flags[hasFilteredBoundProps]) entries = GetFilteredPropertyEntrySet(BoundPropertyEntries); else entries = BoundPropertyEntries; foreach (BoundPropertyEntry entry in entries) { if (entry.TwoWayBound && this is BindableTemplateBuilder) { if (InDesigner) { // Skip two-way entries for BindableTemplateBuilders in designer continue; } } InitBoundProperty(obj, entry, ref dataBindings, ref attributeAccessor); } } private void InitBoundProperty(object obj, BoundPropertyEntry entry, ref DataBindingCollection dataBindings, ref IAttributeAccessor attributeAccessor) { string expressionPrefix = entry.ExpressionPrefix == null ? String.Empty : entry.ExpressionPrefix.Trim(); // If we're in the designer, add the bound properties to the collections if (InDesigner) { if (String.IsNullOrEmpty(expressionPrefix)) { if (dataBindings == null && obj is IDataBindingsAccessor) { dataBindings = ((IDataBindingsAccessor)obj).DataBindings; } dataBindings.Add(new DataBinding(entry.Name, entry.Type, entry.Expression.Trim())); } else { if (obj is IExpressionsAccessor) { string expression = entry.Expression == null ? String.Empty : entry.Expression.Trim(); ((IExpressionsAccessor)obj).Expressions.Add(new ExpressionBinding(entry.Name, entry.Type, expressionPrefix, expression, entry.Generated, entry.ParsedExpressionData)); } } } // If we're in no-compile mode, set the values for expressions that support evaluate else { if (!String.IsNullOrEmpty(expressionPrefix)) { ExpressionBuilder eb = entry.ExpressionBuilder; Debug.Assert(eb != null, "Did not expect null expression builder"); if (eb.SupportsEvaluate) { string name = entry.Name; // DevDiv Bugs 160497: Create the expression context with whatever information we have. // We used to always use the TemplateControl one, but sometimes it's null, so we should // fall back to the VirtualPath one if we can. ExpressionBuilderContext expressionContext; if (TemplateControl != null) { expressionContext = new ExpressionBuilderContext(TemplateControl); } else { expressionContext = new ExpressionBuilderContext(VirtualPath); } object value = eb.EvaluateExpression(obj, entry, entry.ParsedExpressionData, expressionContext); if (entry.UseSetAttribute) { if (attributeAccessor == null) { Debug.Assert(obj is IAttributeAccessor); attributeAccessor = (IAttributeAccessor)obj; } attributeAccessor.SetAttribute(name, value.ToString()); } else { try { PropertyMapper.SetMappedPropertyValue(obj, name, value); } catch (Exception e) { throw new HttpException(SR.GetString(SR.Cannot_set_property, entry.ExpressionPrefix + ":" + entry.Expression, name), e); } } } else { Debug.Fail("Got a ExpressionBuilder that does not support Evaluate in a non-compiled page"); } } else { // no-compile Bind property handling ((Control)obj).DataBinding += new EventHandler(DataBindingMethod); } } } private void DataBindingMethod(object sender, EventArgs e) { /*System.Web.UI.WebControls.DropDownList dataBindingExpressionBuilderTarget; dataBindingExpressionBuilderTarget = ((System.Web.UI.WebControls.DropDownList)(sender)); System.Web.UI.IDataItemContainer Container; Container = ((System.Web.UI.IDataItemContainer)(dataBindingExpressionBuilderTarget.BindingContainer)); if ((this.Page.GetDataItem() != null)) { dataBindingExpressionBuilderTarget.SelectedValue = System.Convert.ToString(this.Eval("FavVegetable")); }*/ bool isBindableTemplateBuilder = this is BindableTemplateBuilder; bool isTemplateBuilder = this is TemplateBuilder; bool firstEntry = true; object evalValue; Control containerControl = null; ICollection entries; // If there are no filters in the picture, use the entries as is if (!flags[hasFilteredBoundProps]) { entries = BoundPropertyEntries; } else { Debug.Assert(ServiceProvider == null); Debug.Assert(TemplateControl != null, "TemplateControl should not be null in no-compile pages. We need it for the FilterResolutionService."); ServiceContainer container = new ServiceContainer(); container.AddService(typeof(IFilterResolutionService), TemplateControl); try { SetServiceProvider(container); entries = GetFilteredPropertyEntrySet(BoundPropertyEntries); } finally { SetServiceProvider(null); } } foreach (BoundPropertyEntry entry in entries) { // Skip all one-way entries. No-compile supported only on Bind statements. // Skip two-way entries if it's a BindableTemplateBuilder or the two way entry is read only if ((entry.TwoWayBound && (isBindableTemplateBuilder || entry.ReadOnlyProperty)) || (!entry.TwoWayBound && isTemplateBuilder)) continue; // We only care about databinding entries here if (!entry.IsDataBindingEntry) continue; Debug.Assert(!entry.UseSetAttribute, "Two-way binding is not supported on expandos - this should have been prevented in ControlBuilder"); if (firstEntry) { firstEntry = false; Debug.Assert(entry.ControlType.IsInstanceOfType(sender), "The DataBinding event sender was not of type " + entry.ControlType.Name); if (_bindingContainerDescriptor == null) { _bindingContainerDescriptor = TypeDescriptor.GetProperties(typeof(Control))["BindingContainer"]; } object container = _bindingContainerDescriptor.GetValue(sender); containerControl = container as Control; if (containerControl.Page.GetDataItem() == null) { break; // nothing to do if GetDataItem is null } } evalValue = containerControl.TemplateControl.Eval(entry.FieldName, entry.FormatString); string objectModelName; MemberInfo memberInfo = PropertyMapper.GetMemberInfo(entry.ControlType, entry.Name, out objectModelName); // If destination is property: // If destination type is string: // {{target}}.{{targetPropertyName}} = System.Convert.ToString( {{value}} ); // Else If destination type is reference type: // {{target}}.{{targetPropertyName}} = ( {{destinationType}} ) {{value}}; // Else destination type is value type: // {{target}}.{{targetPropertyName}} = ( {{destinationType}} ) ({value}); if (entry.Type.IsValueType && evalValue == null) { continue; } object convertedValue = evalValue; if (entry.Type == typeof(string)) { convertedValue = System.Convert.ToString(evalValue, CultureInfo.CurrentCulture); } else if (evalValue != null && !entry.Type.IsAssignableFrom(evalValue.GetType())) { convertedValue = PropertyConverter.ObjectFromString(entry.Type, memberInfo, System.Convert.ToString(evalValue, CultureInfo.CurrentCulture)); } PropertyMapper.SetMappedPropertyValue(sender, objectModelName, convertedValue); } } private void InitTemplateProperties(object obj) { // Don't do anything if there are no entries if (_templatePropertyEntries == null) return; object[] parameters = new object[1]; // If there are no filters in the picture, use the entries as is ICollection entries; if (flags[hasFilteredTemplateProps]) entries = GetFilteredPropertyEntrySet(TemplatePropertyEntries); else entries = TemplatePropertyEntries; foreach (TemplatePropertyEntry entry in entries) { try { ControlBuilder controlBuilder = ((TemplatePropertyEntry)entry).Builder; Debug.Assert(controlBuilder.ServiceProvider == null); controlBuilder.SetServiceProvider(ServiceProvider); try { parameters[0] = controlBuilder.BuildObject(flags[applyTheme]); } finally { controlBuilder.SetServiceProvider(null); } MethodInfo methodInfo = entry.PropertyInfo.GetSetMethod(); Debug.Assert(methodInfo != null); Util.InvokeMethod(methodInfo, obj, parameters); } catch (Exception e) { throw new HttpException(SR.GetString(SR.Cannot_set_property, TagName, entry.Name), e); } } } // If the page has a field which name matches the ID of this control, // assign the control to the field. This matches what we do for compiled // pages ( private void BindFieldToControl(Control control) { // If we tried before and did not find a field, don't try again if (flags[triedFieldToControlBinding] && !flags[hasFieldToControlBinding]) return; flags[triedFieldToControlBinding] = true; TemplateControl templateControl = TemplateControl; if (templateControl == null) return; Type templateControlType = TemplateControl.GetType(); // This logic only needs to be checked once if (!flags[hasFieldToControlBinding]) { // This doesn't apply to designer scenarios. It's only for no-compile pages. if (InDesigner) return; // Nothing to bind if the control doesn't have an ID if (control.ID == null) return; // If the TemplateControl is a built in class (Page or UserControl), // there is no point in looking for fields. if (templateControlType.Assembly == typeof(HttpRuntime).Assembly) return; } // Try to find a field named after the ID in the TemplateControl FieldInfo fieldInfo = templateControl.GetType().GetField(control.ID, BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); // If we couldn't find a field or it doesn't qualify, give up if (fieldInfo == null || fieldInfo.IsPrivate || !fieldInfo.FieldType.IsAssignableFrom(control.GetType())) { return; } // Everything is in place, so set the field to the control fieldInfo.SetValue(templateControl, control); // Remember that it was successful so we know we should try again next time flags[hasFieldToControlBinding] = true; } ////// Returns true is it needs SetTagInnerText() to be called. /// public virtual bool NeedsTagInnerText() { return false; } ////// This method is used to tell the builder that it's about to be appended to its parent. /// public virtual void OnAppendToParentBuilder(ControlBuilder parentBuilder) { // If we have a default property, add it to ourselves if (DefaultPropertyBuilder != null) { ControlBuilder defaultPropBuilder = DefaultPropertyBuilder; // Need to make it null to avoid infinite recursion ParseTimeData.DefaultPropertyBuilder = null; AppendSubBuilder(defaultPropBuilder); } if (!(this is BindableTemplateBuilder)) { ControlBuilder currentBuilder = this; while (currentBuilder != null && !(currentBuilder is BindableTemplateBuilder)) { currentBuilder = currentBuilder.ParentBuilder; } if (currentBuilder != null && currentBuilder is BindableTemplateBuilder) { // Add all the TwoWay BoundPropertyEntry's to the BindableTemplateBuilder foreach (BoundPropertyEntry entry in BoundPropertyEntries) { if (entry.TwoWayBound) { ((BindableTemplateBuilder)currentBuilder).AddBoundProperty(entry); } } } } } ////// Prepares this ControlBuilder and all of it's subbuilders for /// use in no-compile pages (minimizing the memory usage). /// This makes the use of all parse-time properties invalid. /// internal virtual void PrepareNoCompilePageSupport() { // Clear out all the flags and cached data from parse time flags[parseComplete] = true; _parseTimeData = null; // Remove any property entry lists that aren't being used if ((_eventEntries != null) && (_eventEntries.Count == 0)) { _eventEntries = null; } if ((_simplePropertyEntries != null) && (_simplePropertyEntries.Count == 0)) { _simplePropertyEntries = null; } if (_complexPropertyEntries != null) { if (_complexPropertyEntries.Count == 0) { _complexPropertyEntries = null; } else { foreach (BuilderPropertyEntry entry in _complexPropertyEntries) { if (entry.Builder != null) entry.Builder.PrepareNoCompilePageSupport(); } } } if (_templatePropertyEntries != null) { if (_templatePropertyEntries.Count == 0) { _templatePropertyEntries = null; } else { foreach (BuilderPropertyEntry entry in _templatePropertyEntries) { if (entry.Builder != null) entry.Builder.PrepareNoCompilePageSupport(); } } } if ((_boundPropertyEntries != null) && (_boundPropertyEntries.Count == 0)) { _boundPropertyEntries = null; } if (_subBuilders != null) { if (_subBuilders.Count > 0) { foreach (Object builderObj in _subBuilders) { ControlBuilder builder = builderObj as ControlBuilder; if (builder != null) builder.PrepareNoCompilePageSupport(); } } else { _subBuilders = null; } } // Sort the entry here to make sure we don't run into a race condition if we try to // do it later on demand ( EnsureEntriesSorted(); } ////// If the control has a Property which matches the name of the /// attribute, create an PropertyEntry for it, that will /// be used at BuildControl time. /// internal void PreprocessAttribute(string filter, string attribname, string attribvalue, bool mainDirectiveMode) { Match match; // Treat a null value as an empty string if (attribvalue == null) { attribvalue = String.Empty; } if ((match = databindRegex.Match(attribvalue, 0)).Success) { // Don't process databinding expressions during updatable precomp, because we're only // generating the base class, and only need the Type and ID of the controls ( if (BuildManager.PrecompilingForUpdatableDeployment) return; string code = match.Groups["code"].Value; bool isParsedBindingStatement = false; bool isTwoWayBindingStatement = false; if (!InDesigner) { if ((match = bindExpressionRegex.Match(code, 0)).Success) { isParsedBindingStatement = true; isTwoWayBindingStatement = true; } // Treat it as a binding statement for skin files so the expression // format will be checked first. else if ((CompilationMode == CompilationMode.Never || InPageTheme) && (match = evalExpressionRegex.Match(code, 0)).Success) { isParsedBindingStatement = true; } } // Is it a two-way binding statement or eval in no-compile? if (isParsedBindingStatement) { string paramString = match.Groups["params"].Value; if (!(match = bindParametersRegex.Match(paramString, 0)).Success) { throw new HttpException(SR.GetString(SR.BadlyFormattedBind)); } string fieldName = match.Groups["fieldName"].Value; string formatString = String.Empty; Group formatStringGroup = match.Groups["formatString"]; if (formatStringGroup != null) { formatString = formatStringGroup.Value; } if (formatString.Length > 0) { if (!(match = formatStringRegex.Match(formatString, 0)).Success) { throw new HttpException(SR.GetString(SR.BadlyFormattedBind)); } } // Pass the code expression to AddBoundProperty since the Eval expression needs to be compiled in skin files. // The bind expression needs to call without code if (InPageTheme && !isTwoWayBindingStatement) { AddBoundProperty(filter, attribname, String.Empty, code, null /*expressionBuilder*/, null /*parsedExpressionData*/, String.Empty, String.Empty, false); return; } AddBoundProperty(filter, attribname, String.Empty, code, null /*expressionBuilder*/, null /*parsedExpressionData*/, fieldName, formatString, isTwoWayBindingStatement); return; } else { // First, give the PageParserFilter a chance to handle the databinding if (!Parser.PageParserFilterProcessedDataBindingAttribute(ID, attribname, code)) { // If it's a non compiled page and it's not a Bind or Eval statement, fail Parser.EnsureCodeAllowed(); // Get the piece of code and add the property AddBoundProperty(filter, attribname, String.Empty, code, null /*expressionBuilder*/, null /*parsedExpressionData*/, String.Empty, String.Empty, false); } return; } } else if ((match = expressionBuilderRegex.Match(attribvalue, 0)).Success) { if (InPageTheme) { throw new HttpParseException(SR.GetString(SR.ControlBuilder_ExpressionsNotAllowedInThemes)); } // Don't process expression builders during updatable precomp, because we're only // generating the base class, and only need the Type and ID of the controls ( if (BuildManager.PrecompilingForUpdatableDeployment) return; string code = match.Groups["code"].Value.Trim(); int indexOfColon = code.IndexOf(':'); if (indexOfColon == -1) { throw new HttpParseException(SR.GetString(SR.InvalidExpressionSyntax, attribvalue)); } string expressionPrefix = code.Substring(0, indexOfColon).Trim(); string expressionCode = code.Substring(indexOfColon + 1).Trim(); if (expressionPrefix.Length == 0) { throw new HttpParseException(SR.GetString(SR.MissingExpressionPrefix, attribvalue)); } if (expressionCode.Length == 0) { throw new HttpParseException(SR.GetString(SR.MissingExpressionValue, attribvalue)); } // If it's a non compiled page, fail if the expressiom builder has SupportsEvaluate==false ExpressionBuilder expressionBuilder = null; if (CompilationMode == CompilationMode.Never) { expressionBuilder = ExpressionBuilder.GetExpressionBuilder(expressionPrefix, Parser.CurrentVirtualPath); if ((expressionBuilder != null) && !expressionBuilder.SupportsEvaluate) { throw new InvalidOperationException(SR.GetString(SR.Cannot_evaluate_expression, expressionPrefix + ":" + expressionCode)); } } AddBoundProperty(filter, attribname, expressionPrefix, expressionCode, expressionBuilder, null /*parsedExpressionData*/, String.Empty, String.Empty, false); return; } AddProperty(filter, attribname, attribvalue, mainDirectiveMode); } ////// Indicates whether this ControlBuilder should allow meta:localize and meta:resourcekey /// attributes on the tag. We only allow this for tags that represent controls and tags /// that represent collection items. /// private bool IsValidForImplicitLocalization() { if (flags[controlTypeIsControl]) { // If this is a control, we can localize return true; } if (ParentBuilder == null) { // We must have a parent builder return false; } // If we have a parent builder, if (ParentBuilder.DefaultPropertyBuilder != null) { return typeof(ICollection).IsAssignableFrom(ParentBuilder.DefaultPropertyBuilder.ControlType); } else { return typeof(ICollection).IsAssignableFrom(ParentBuilder.ControlType); } } ////// Process implicit resources if the control has a meta:resourcekey attribute /// internal void ProcessImplicitResources(ParsedAttributeCollection attribs) { // Check if meta:localize="false" was specified. Always do this since we need it at design-time string localize = (string)((IDictionary)attribs)["meta:localize"]; if (localize != null) { // Depending on the control type, don't allow meta:localize (e.g. ITemplate case) ( if (!IsValidForImplicitLocalization()) { throw new InvalidOperationException(SR.GetString(SR.meta_localize_notallowed, TagName)); } bool parseResult; if (!Boolean.TryParse(localize, out parseResult)) { throw new HttpException(SR.GetString(SR.ControlBuilder_InvalidLocalizeValue, localize)); } ParseTimeData.Localize = parseResult; } else { ParseTimeData.Localize = true; } // Check whether a resource key was specified string keyPrefix = (string) ((IDictionary)attribs)["meta:resourcekey"]; // Remove all meta attributes from the collection ( attribs.ClearFilter("meta"); if (keyPrefix == null) return; // Depending on the control type, don't allow meta:reskey (e.g. ITemplate case) ( if (!IsValidForImplicitLocalization()) { throw new InvalidOperationException(SR.GetString(SR.meta_reskey_notallowed, TagName)); } Debug.Assert(_controlType != null, "If we get here then the tag type must be either an ICollection or a Control, so how can it be null?"); // Restrict resource keys the same way as we restrict ID's ( if (!System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(keyPrefix)) { throw new HttpException(SR.GetString(SR.Invalid_resourcekey, keyPrefix)); } if (!ParseTimeData.Localize) { // If we have a key prefix (from meta:resourcekey) but we also have // meta:localize=false, we throw. throw new HttpException(SR.GetString(SR.meta_localize_error)); } ParseTimeData.ResourceKeyPrefix = keyPrefix; // Try to get the implicit resources for this specific Page IImplicitResourceProvider implicitResourceProvider; if (Parser.FInDesigner && Parser.DesignerHost != null) { implicitResourceProvider = (IImplicitResourceProvider)Parser.DesignerHost.GetService(typeof(IImplicitResourceProvider)); } else { implicitResourceProvider = Parser.GetImplicitResourceProvider(); } // If the Page has resources, get the specific ones for this meta:resourcekey ICollection tagResources = null; if (implicitResourceProvider != null) tagResources = implicitResourceProvider.GetImplicitResourceKeys(keyPrefix); if (tagResources != null) { // Get the IDesignerHost in case we need it to find ExpressionBuilders IDesignerHost host = DesignerHost; // Note: this code expect that the "resources" expression builder be // registered in config. If the user removes it, they will get an error. ExpressionBuilder resourcesExpressionBuilder = ExpressionBuilder.GetExpressionBuilder("resources", Parser.CurrentVirtualPath, host); bool usingStandardResources = typeof(ResourceExpressionBuilder) == resourcesExpressionBuilder.GetType(); foreach (ImplicitResourceKey entry in tagResources) { // Put together the complete resource key, as would appear in an explicit resource string fullResourceKey = keyPrefix + "." + entry.Property; if (entry.Filter.Length > 0) fullResourceKey = entry.Filter + ':' + fullResourceKey; // Replace '.' with '-', since that's what AddBoundProperty expects string property = entry.Property.Replace('.', '-'); object parsedExpressionData = null; string expression; if (usingStandardResources) { // If we're using the standard System.Web.Compilation.ResourceExpressionBuilder // we can optimized the parsed data. parsedExpressionData = ResourceExpressionBuilder.ParseExpression(fullResourceKey); expression = String.Empty; } else { expression = fullResourceKey; } AddBoundProperty(entry.Filter, property, "resources", expression, resourcesExpressionBuilder, parsedExpressionData, true, String.Empty, String.Empty, false); } } } ////// Preprocess all the attributes at parse time, so that we'll be left /// with as little work as possible when we build the control. /// private void PreprocessAttributes(ParsedAttributeCollection attribs) { ProcessImplicitResources(attribs); // Preprocess all the attributes foreach (FilteredAttributeDictionary filteredAttributes in attribs.GetFilteredAttributeDictionaries()) { string filter = filteredAttributes.Filter; foreach (DictionaryEntry attribute in filteredAttributes) { string name = attribute.Key.ToString(); string value = attribute.Value.ToString(); PreprocessAttribute(filter, name, value, false); } } } // public /* internal */ void SetServiceProvider(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } internal void EnsureEntriesSorted() { // Always perform the sorting only once, even in derived classes, // so as to avoid concurrency issues (DevDiv bugs 203787). if (!flags[entriesSorted]) { flags[entriesSorted] = true; SortEntries(); } } internal virtual void SortEntries() { // Don't sort the entries in a collection builder if (this is CollectionBuilder) { return; } FilteredPropertyEntryComparer comparer = null; ProcessAndSortPropertyEntries(_boundPropertyEntries, ref comparer); ProcessAndSortPropertyEntries(_complexPropertyEntries, ref comparer); ProcessAndSortPropertyEntries(_simplePropertyEntries, ref comparer); ProcessAndSortPropertyEntries(_templatePropertyEntries, ref comparer); } internal void ProcessAndSortPropertyEntries(ArrayList propertyEntries, ref FilteredPropertyEntryComparer comparer) { if (propertyEntries != null && propertyEntries.Count > 1) { HybridDictionary dictionary = new HybridDictionary(propertyEntries.Count, true); int index = 0; // Determine the order of the entry based on location of the first entry with the same name foreach (PropertyEntry entry in propertyEntries) { object o = dictionary[entry.Name]; if (o != null) { entry.Order = (int)o; } else { entry.Order = index; dictionary.Add(entry.Name, index++); } } if (comparer == null) { comparer = new FilteredPropertyEntryComparer(CurrentFilterResolutionService); } propertyEntries.Sort(comparer); } } ////// /// internal void SetControlType(Type controlType) { _controlType = controlType; if (_controlType != null) { flags[controlTypeIsControl] = typeof(Control).IsAssignableFrom(_controlType); } else { flags[controlTypeIsControl] = false; } } ////// Set the ControlBuilder that's the parent of this ControlBuilder /// internal virtual void SetParentBuilder(ControlBuilder parentBuilder) { ParseTimeData.ParentBuilder = parentBuilder; if ((ParseTimeData.FirstNonThemableProperty != null) && (parentBuilder is FileLevelPageThemeBuilder)) { throw new InvalidOperationException(SR.GetString(SR.Property_theme_disabled, ParseTimeData.FirstNonThemableProperty.Name, ControlType.FullName)); } } // public string GetResourceKey() { // This should only be used in the designer Debug.Assert(InDesigner); return ParseTimeData.ResourceKeyPrefix; } // public void SetResourceKey(string resourceKey) { // This should only be used in the designer Debug.Assert(InDesigner); SimplePropertyEntry entry = new SimplePropertyEntry(); entry.Filter = "meta"; entry.Name = "resourcekey"; entry.Value = resourceKey; entry.PersistedValue = resourceKey; entry.UseSetAttribute = true; entry.Type = typeof(string); AddEntry(SimplePropertyEntriesInternal, entry); } ////// Give the builder the raw inner text of the tag. /// public virtual void SetTagInnerText(string text) { } ////// Give the ControlBuilder a chance to look at and modify the tree /// public virtual void ProcessGeneratedCode( CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod) { } ////// Make sure the given property with the specified context (using SetAttribute to set the value or a directive property) is persistable /// throwing otherwise /// private void ValidatePersistable(PropertyInfo propInfo, bool usingSetAttribute, bool mainDirectiveMode, bool simplePropertyEntry, string filter) { // Get the appropriate PropertyDescriptorCollection. If it's for our own type, just // call our PropertyDescriptors property, which caches it. Otherwise (for sub properties) // get it directly without caching (less common case). PropertyDescriptorCollection propertyDescriptors; // Use the current control type if it derives from propInfo.DeclaringType bool useCurrentControlType = propInfo.DeclaringType.IsAssignableFrom(_controlType); if (useCurrentControlType) { propertyDescriptors = PropertyDescriptors; } else { // See comments below regarding when we propertyDescriptors = TypeDescriptor.GetProperties(propInfo.DeclaringType); } PropertyDescriptor propDesc = propertyDescriptors[propInfo.Name]; if (propDesc != null) { if (useCurrentControlType) { // These checks are only done for top-level properties (e.g. Text="hello"). // We don't do it for sub-properties (e.g. Font-Name="Arial") since it would // break backwards compatibility with v1.1 (where we did not do these checks). // If it's an HtmlControl, if (IsHtmlControl) { if (propDesc.Attributes.Contains(HtmlControlPersistableAttribute.No)) { throw new HttpException(SR.GetString(SR.Property_Not_Persistable, propDesc.Name)); } } // Otherwise, if we're not using the attribute accessor, we're not processing the main directive // else if (!usingSetAttribute && !mainDirectiveMode && propDesc.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden)) { throw new HttpException(SR.GetString(SR.Property_Not_Persistable, propDesc.Name)); } } // These checks are done for both top-level properties, as well as sub-properties. // Backwards compatibility is satisfied since the Filterable() and Themeable() // attributes are new in v2.0. // Make sure the property is filterable if there is a filter if (!FilterableAttribute.IsPropertyFilterable(propDesc) && !String.IsNullOrEmpty(filter)) { throw new InvalidOperationException(SR.GetString(SR.Illegal_Device, propDesc.Name)); } if (InPageTheme && (ParseTimeData.FirstNonThemableProperty == null)) { // For simple properties, don't validate if it's a customAttribute if (!simplePropertyEntry || !usingSetAttribute) { ThemeableAttribute attr = (ThemeableAttribute)propDesc.Attributes[typeof(ThemeableAttribute)]; if (attr != null && !attr.Themeable) { if (this.ParentBuilder != null) { if (ParentBuilder is FileLevelPageThemeBuilder) { throw new InvalidOperationException(SR.GetString(SR.Property_theme_disabled, propDesc.Name, ControlType.FullName)); } } else { ParseTimeData.FirstNonThemableProperty = propDesc; } } } } } } // Default factory used create base ControlBuilder objects private static IWebObjectFactory s_defaultControlBuilderFactory = new DefaultControlBuilderFactory(); private class DefaultControlBuilderFactory : IWebObjectFactory { object IWebObjectFactory.CreateInstance() { return new ControlBuilder(); } } // Factories used when we cannot generate a fast factory (e.g. because the ControlBuilder // type is internal). private class ReflectionBasedControlBuilderFactory : IWebObjectFactory { private Type _builderType; internal ReflectionBasedControlBuilderFactory(Type builderType) { _builderType = builderType; } object IWebObjectFactory.CreateInstance() { return (ControlBuilder)HttpRuntime.CreateNonPublicInstance(_builderType); } } ////// Space-saving class used to store variables used only during parse and codegen time. /// All these are cleared when the ControlBuilder is used in a no-compile page. /// private sealed class ControlBuilderParseTimeData { // const masks into the BitVector32 private const int childrenAsProperties = 0x00000001; private const int hasAspCode = 0x00000002; private const int isHtmlControl = 0x00000004; private const int isNonParserAccessor = 0x00000008; private const int namingContainerSearched = 0x00000010; private const int supportsAttributes = 0x00000020; private const int isGeneratedID = 0x00000040; private const int localize = 0x00000080; private const int ignoreControlProperties = 0x00000100; #pragma warning disable 0649 private SimpleBitVector32 flags; #pragma warning restore 0649 internal bool ChildrenAsProperties { get { return flags[childrenAsProperties]; } set { flags[childrenAsProperties] = value; } } internal ControlBuilder DefaultPropertyBuilder; internal EventDescriptorCollection EventDescriptors; internal string Filter; internal bool HasAspCode { get { return flags[hasAspCode]; } set { flags[hasAspCode] = value; } } internal bool IsHtmlControl { get { return flags[isHtmlControl]; } set { flags[isHtmlControl] = value; } } internal bool IgnoreControlProperties { get { return flags[ignoreControlProperties]; } set { flags[ignoreControlProperties] = value; } } internal bool IsNonParserAccessor { get { return flags[isNonParserAccessor]; } set { flags[isNonParserAccessor] = value; } } internal bool IsGeneratedID { get { return flags[isGeneratedID]; } set { flags[isGeneratedID] = value; } } internal string ID; internal int Line; internal bool Localize { get { return flags[localize]; } set { flags[localize] = value; } } internal bool NamingContainerSearched { get { return flags[namingContainerSearched]; } set { flags[namingContainerSearched] = value; } } internal ControlBuilder NamingContainerBuilder; internal ControlBuilder ParentBuilder; internal TemplateParser Parser; internal PropertyDescriptorCollection PropertyDescriptors; internal StringSet PropertyEntries; internal bool SupportsAttributes { get { return flags[supportsAttributes]; } set { flags[supportsAttributes] = value; } } internal VirtualPath VirtualPath; internal PropertyDescriptor FirstNonThemableProperty; internal string ResourceKeyPrefix; } internal sealed class FilteredPropertyEntryComparer : IComparer { IFilterResolutionService _filterResolutionService; public FilteredPropertyEntryComparer(IFilterResolutionService filterResolutionService) { _filterResolutionService = filterResolutionService; } int IComparer.Compare(object o1, object o2) { if (o1 == o2) { return 0; } if (o1 == null) { return 1; } if (o2 == null) { return -1; } Debug.Assert(o1 is PropertyEntry); Debug.Assert(o2 is PropertyEntry); PropertyEntry entry1 = (PropertyEntry)o1; PropertyEntry entry2 = (PropertyEntry)o2; // Compare the order of the item to make the sorting stable. int compareValue = entry1.Order - entry2.Order; if (compareValue == 0) { if (_filterResolutionService == null) { if (String.IsNullOrEmpty(entry1.Filter)) { if ((entry2.Filter != null) && (entry2.Filter.Length > 0)) { compareValue = 1; } else { compareValue = 0; } } else { if (String.IsNullOrEmpty(entry2.Filter)) { compareValue = -1; } else { compareValue = 0; } } } else { string filter1 = (entry1.Filter.Length == 0) ? "Default" : entry1.Filter; string filter2 = (entry2.Filter.Length == 0) ? "Default" : entry2.Filter; compareValue = _filterResolutionService.CompareFilters(filter1, filter2); } // Compare the index of the item in the array to make the sorting stable. if (compareValue == 0) { return entry1.Index - entry2.Index; } } return compareValue; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DockPanel.cs
- CursorEditor.cs
- InlineCollection.cs
- WebInvokeAttribute.cs
- SessionPageStateSection.cs
- LineMetrics.cs
- FontStretch.cs
- ViewStateModeByIdAttribute.cs
- PassportPrincipal.cs
- RadioButtonList.cs
- FileStream.cs
- RuleConditionDialog.cs
- AppDomainManager.cs
- SqlExpressionNullability.cs
- RelationshipEndMember.cs
- TripleDESCryptoServiceProvider.cs
- GrowingArray.cs
- MatrixCamera.cs
- Parsers.cs
- TextWriter.cs
- Rules.cs
- NameValueCollection.cs
- TypeValidationEventArgs.cs
- SqlFactory.cs
- BindingListCollectionView.cs
- AttributeParameterInfo.cs
- Switch.cs
- UTF8Encoding.cs
- DocumentPage.cs
- FixUp.cs
- CalendarSelectionChangedEventArgs.cs
- TransactionManagerProxy.cs
- WindowExtensionMethods.cs
- GradientStop.cs
- ObjectKeyFrameCollection.cs
- TreeNodeStyle.cs
- ProcessModelInfo.cs
- QuaternionKeyFrameCollection.cs
- SizeConverter.cs
- Classification.cs
- IdentityModelStringsVersion1.cs
- FixedTextPointer.cs
- PrinterUnitConvert.cs
- LogicalTreeHelper.cs
- AliasGenerator.cs
- DataRelationCollection.cs
- BinaryObjectWriter.cs
- ListenerElementsCollection.cs
- DataComponentMethodGenerator.cs
- HorizontalAlignConverter.cs
- DesignerActionListCollection.cs
- HostingEnvironmentWrapper.cs
- MemoryFailPoint.cs
- CursorInteropHelper.cs
- MenuCommand.cs
- SplitterCancelEvent.cs
- XPathSingletonIterator.cs
- AssemblyBuilderData.cs
- TemplatedMailWebEventProvider.cs
- ZipIOZip64EndOfCentralDirectoryBlock.cs
- ControlSerializer.cs
- PropertyChangedEventArgs.cs
- TreeNodeClickEventArgs.cs
- SqlServer2KCompatibilityAnnotation.cs
- AudioFileOut.cs
- PenThreadWorker.cs
- HostedTcpTransportManager.cs
- CryptographicAttribute.cs
- FormsAuthenticationUser.cs
- DataBindingExpressionBuilder.cs
- TextOutput.cs
- StreamAsIStream.cs
- Triplet.cs
- MailWebEventProvider.cs
- CharEnumerator.cs
- UIntPtr.cs
- CodeRegionDirective.cs
- BinHexEncoding.cs
- HwndTarget.cs
- Form.cs
- SqlBuilder.cs
- InkCollectionBehavior.cs
- thaishape.cs
- EnumerableRowCollection.cs
- MemberAssignmentAnalysis.cs
- ListViewItemEventArgs.cs
- PackWebRequestFactory.cs
- MemberHolder.cs
- DefaultTextStoreTextComposition.cs
- COM2ExtendedUITypeEditor.cs
- InputChannelAcceptor.cs
- Axis.cs
- CheckoutException.cs
- PageCatalogPart.cs
- Activator.cs
- NativeWindow.cs
- EqualityComparer.cs
- RadioButtonBaseAdapter.cs
- TreeView.cs
- DataServiceRequestException.cs