예제 #1
0
        /// <summary>
        /// Construct a new blob of metadata for a single control, performing any reflection needed to determine its
        /// structure and parsing behavior.
        /// </summary>
        /// <param name="compileContext">The context in which errors should be reported.</param>
        /// <param name="tag">The complete tag for this control, as found in the markup.</param>
        /// <param name="tagRegistrations">The known list of registrations, formed from the directives in the
        /// "web.config" and any &lt;%@ Register %&gt; directives in the markup.</param>
        /// <param name="assemblies">The complete list of known pre-loaded assemblies for reflection.</param>
        /// <param name="allowedTypes">The allowable types of control that may be returned.  If the matching
        /// .NET class type for this control does not match one of these types (or is not derivable from one
        /// of these types), this constructor will throw an exception.</param>
        public ReflectedControl(ICompileContext compileContext, Tag tag, IEnumerable <TagRegistration> tagRegistrations, AssemblyLoader assemblies, IEnumerable <Type> allowedTypes)
        {
            // Decode the tag declaration.
            DecodeFullTagNameWithPrefix(tag.TagName, out TagPrefix, out TagName);

            // Find the matching C# type for that tag declaration.
            if (string.IsNullOrEmpty(TagPrefix))
            {
                TagRegistration = _htmlTagRegistration;
                ControlType     = FindMatchingHtmlControlType(tag);
            }
            else
            {
                ControlType = FindMatchingControlType(TagPrefix, TagName, tagRegistrations, assemblies, out TagRegistration);
            }
            if (ControlType == null)
            {
                throw new InvalidOperationException(string.Format("No matching type for <{0}> was found in any of the loaded assemblies.", tag.TagName));
            }

            // If we are restricted to only load certain types (such as the nested not-a-control instances inside a DataPager control),
            // check that the control we have found matches one of those types.
            if (!allowedTypes.Any(t => t.IsAssignableFrom(ControlType)))
            {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendFormat("Found matching type for <{0}>, but it is a {1}, not one of the {2} allowed types:\r\n",
                                           tag.TagName, ControlType.FullName, allowedTypes.Count());
                foreach (Type allowedType in allowedTypes)
                {
                    stringBuilder.AppendFormat("- {0}\r\n", allowedType.FullName);
                }
                throw new InvalidOperationException(stringBuilder.ToString());
            }

            // Extract the [ParseChildren] attribute, if it has one.
            System.Web.UI.ParseChildrenAttribute[] parseChildrenAttributes = (System.Web.UI.ParseChildrenAttribute[])ControlType.GetCustomAttributes(typeof(System.Web.UI.ParseChildrenAttribute), true);
            ParseChildrenAttribute = parseChildrenAttributes.Length == 0 ? null : parseChildrenAttributes[0];

            // Extract the [ControlBuilder] attribute, if it has one.
            System.Web.UI.ControlBuilderAttribute[] controlBuilderAttributes = (System.Web.UI.ControlBuilderAttribute[])ControlType.GetCustomAttributes(typeof(System.Web.UI.ControlBuilderAttribute), true);
            ControlBuilderAttribute = controlBuilderAttributes.Length == 0 ? null : controlBuilderAttributes[0];

            // Extract the type's properties, since their declarations control what's legal in the markup.
            ControlProperties = CollectControlProperties(compileContext, ControlType, this);

            // HtmlControls have to be handled specially, since they have [ParseChildren(true)] in many cases but
            // aren't really using it for anything.
            if (ControlType.Namespace == _htmlTagRegistration.Namespace)
            {
                ParseChildrenAttribute = new ParseChildrenAttribute(false);
            }

            // Validate the ParseChildrenAttribute, which may be broken or weird.
            if (ParseChildrenAttribute != null)
            {
                if (!string.IsNullOrEmpty(ParseChildrenAttribute.DefaultProperty))
                {
                    string propertyName = ParseChildrenAttribute.DefaultProperty.ToLower();                             // ASP.NET also ignores case on this; see internals of ControlBuilder.CreateChildBuilder() for details.
                    if (!ControlProperties.ContainsKey(propertyName))
                    {
                        throw new InvalidOperationException(string.Format("The [ParseChildren] attribute on class \"{0}\" names a default property \"{1}\" that does not exist in that class.",
                                                                          ControlType.FullName, ParseChildrenAttribute.DefaultProperty));
                    }

                    DefaultCollectionProperty = ControlProperties[propertyName];
                    if (!DefaultCollectionProperty.IsCollectionProperty)
                    {
                        throw new InvalidOperationException(string.Format("The [ParseChildren] attribute on class \"{0}\" names a default property \"{1}\" that is not a collection property.  The default property must always be a collection property.",
                                                                          ControlType.FullName, ParseChildrenAttribute.DefaultProperty));
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Search through the given set of tag registrations and assemblies to find a suitable .NET class type that
        /// matches the given tag prefix and tag name.  (This method handles server controls and user controls, not HTML
        /// controls; the tag prefix must not be empty.)
        /// </summary>
        /// <param name="tagPrefix">The tag prefix of the tag we're searching for.</param>
        /// <param name="tagName">The tag name of the tag we're searching for.</param>
        /// <param name="tagRegistrations">The known list of registrations, formed from the directives in the
        /// "web.config" and any &lt;%@ Register %&gt; directives in the markup.</param>
        /// <param name="assemblies">The complete list of known pre-loaded assemblies for reflection.</param>
        /// <param name="matchingTagRegistration">The matching tag registration for this tag prefix/name, if there is one.</param>
        /// <returns>The matching class type, if one exists, or null if there is no matching class type.</returns>
        private static Type FindMatchingControlType(string tagPrefix, string tagName, IEnumerable <TagRegistration> tagRegistrations, AssemblyLoader assemblies, out TagRegistration matchingTagRegistration)
        {
            // Since multiple tag registrations could match this declaration, we have no choice but to walk through
            // all of the tag registrations and just try to find the first one that matches.
            foreach (TagRegistration tagRegistration in tagRegistrations)
            {
                // Try the prefix first.  If that doesn't match, it's definitely not what we're looking for.
                if (string.Compare(tagRegistration.TagPrefix, tagPrefix, StringComparison.InvariantCultureIgnoreCase) != 0)
                {
                    continue;
                }

                switch (tagRegistration.Kind)
                {
                case TagRegistrationKind.SingleUserControl:
                    if (string.Compare(tagRegistration.TagName, tagName, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        // We matched a registered user control, so we have to partially parse it to find out
                        // what namespace in the website assembly is associated with it.
                        Assembly assembly = assemblies.PrimaryAssembly;
                        Type     type     = assembly.GetType(tagRegistration.Typename);

                        // Make sure we found it, and that it's actually a UserControl of some kind.
                        if (type == null || !typeof(System.Web.UI.UserControl).IsAssignableFrom(type))
                        {
                            break;
                        }

                        // We found it, so return it.
                        matchingTagRegistration = tagRegistration;
                        return(type);
                    }
                    break;

                case TagRegistrationKind.Namespace:
                {
                    // This registration describes an entire namespace worth of controls in an assembly somewhere.
                    // So check see if tagName matches a class inside the given namespace in that assembly.
                    Assembly assembly = assemblies[tagRegistration.AssemblyFilename];
                    Type     type     = assembly.GetType(tagRegistration.Namespace + "." + tagName, false, true);

                    // Make sure we found it.
                    if (type == null)
                    {
                        break;
                    }

                    // We found it, so return it.
                    matchingTagRegistration = tagRegistration;
                    return(type);
                }

                case TagRegistrationKind.HtmlControl:
                    // Shouldn't be able to get here.
                    throw new InvalidOperationException("Internal error; got an HtmlControl when attempting to process a registered user control or namespace.");
                }
            }

            matchingTagRegistration = null;
            return(null);
        }
예제 #3
0
        /// <summary>
        /// Load the web.config file found at the given filename.
        /// </summary>
        public void LoadWebConfig(ICompileContext compileContext, string filename, string rootPath)
        {
            compileContext.Verbose("Loading and processing \"{0}\"...", filename);
            compileContext.VerboseNesting++;

            try
            {
                // Load and decode the XML itself.
                XDocument webConfig = XDocument.Load(filename);

                compileContext.Verbose("\"{0}\" loaded as an XDocument.", filename);

                // Find the <add> declarations in the <configuration><system.web><controls> section.
                IEnumerable <XElement> adds = ExtractAddSectionFromWebConfig(webConfig, filename);

                compileContext.Verbose("Found {0} <add> declarations.", adds.Count());

                // Parse the <add> declarations into a TagRegistrations list.
                List <TagRegistration> tagRegistrations = new List <TagRegistration>();
                foreach (XElement add in adds)
                {
                    // Extract all the (important) attributes for this <add> declaration.
                    XAttribute tagPrefixAttribute = add.Attribute("tagPrefix");
                    XAttribute tagNameAttribute   = add.Attribute("tagName");
                    XAttribute srcAttribute       = add.Attribute("src");
                    XAttribute namespaceAttribute = add.Attribute("namespace");
                    XAttribute assemblyAttribute  = add.Attribute("assembly");

                    // Make sure this declaration makes sense; if not, just skip it and hope for the best.
                    bool isValid = ValidateAddElement(compileContext, filename, tagPrefixAttribute, namespaceAttribute, tagNameAttribute, srcAttribute, assemblyAttribute);
                    if (!isValid)
                    {
                        continue;
                    }

                    // It's valid, so decode it.
                    TagRegistration tagRegistration;
                    if (namespaceAttribute != null)
                    {
                        tagRegistration = new TagRegistration
                        {
                            Kind             = TagRegistrationKind.Namespace,
                            TagPrefix        = (tagPrefixAttribute != null ? tagPrefixAttribute.Value : null),
                            AssemblyFilename = (assemblyAttribute != null ? assemblyAttribute.Value : null),
                            Namespace        = namespaceAttribute.Value,
                        };
                        compileContext.Verbose("Registering namespace: <{0}:*> now includes \"{1}\"{2}",
                                               tagRegistration.TagPrefix, tagRegistration.Namespace,
                                               string.IsNullOrEmpty(tagRegistration.AssemblyFilename) ? string.Empty : " in assembly \"" + tagRegistration.AssemblyFilename + "\"");
                    }
                    else
                    {
                        tagRegistration = new TagRegistration
                        {
                            Kind           = TagRegistrationKind.SingleUserControl,
                            TagPrefix      = (tagPrefixAttribute != null ? tagPrefixAttribute.Value : null),
                            TagName        = (tagNameAttribute != null ? tagNameAttribute.Value : null),
                            SourceFilename = (srcAttribute != null ? srcAttribute.Value : null),
                        };
                        compileContext.Verbose("Registering user control: <{0}:{1}> is \"{2}\"",
                                               tagRegistration.TagPrefix, tagRegistration.TagName, tagRegistration.SourceFilename);
                    }

                    // And add it to the list of known tag registrations.
                    tagRegistrations.Add(tagRegistration);
                }

                // If everything was successful, share the results with the world.
                WebConfig        = webConfig;
                TagRegistrations = tagRegistrations;
            }
            finally
            {
                compileContext.VerboseNesting--;
                compileContext.Verbose("Finished processing \"{0}\".", filename);
                compileContext.Verbose("");
            }
        }