private TagRegistration GetHtmlTagRegistration(Assembly systemWebAssembly)
        {
            if (!_htmlTagRegistrations.ContainsKey(systemWebAssembly.FullName))
            {
                var htmlTagRegistration = new TagRegistration
                {
                    Kind             = TagRegistrationKind.HtmlControl,
                    AssemblyFilename = systemWebAssembly.FullName,
                    Namespace        = "System.Web.UI.HtmlControls",
                    TagPrefix        = string.Empty,
                };
                _htmlTagRegistrations.Add(systemWebAssembly.FullName, htmlTagRegistration);
            }

            return(_htmlTagRegistrations[systemWebAssembly.FullName]);
        }
        /// <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, ReferencedAssembliesContext 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.ProjectOutputAssembly;

                        if (assembly == null)
                        {
                            // may not have built the output assembly yet..
                            break;
                        }

                        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.GetAssembly(tagRegistration.AssemblyFilename);

                    Type type = assembly.GetType(tagRegistration.Namespace + "." + tagName, false, true);
                    //Type type = Type.ReflectionOnlyGetType(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);
        }
        /// <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, ReferencedAssembliesContext 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 = GetHtmlTagRegistration(assemblies.SystemWebAssembly);
                ControlType     = FindMatchingHtmlControlType(tag, assemblies);
            }
            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.


            var enumerable
                = allowedTypes as Type[] ?? allowedTypes.ToArray();

            if (!enumerable.Any(t => IsSameOrSubclass(t, 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, enumerable.Count());
                foreach (Type allowedType in enumerable)
                {
                    stringBuilder.AppendFormat("- {0}\r\n", allowedType.FullName);
                }
                throw new InvalidOperationException(stringBuilder.ToString());
            }



            // Extract the [ParseChildren] attribute, if it has one.
            var custAtts = CustomAttributeData.GetCustomAttributes(ControlType).Where(a => a.AttributeType == typeof(System.Web.UI.ParseChildrenAttribute));

            if (custAtts.Any())
            {
                var parseAtt = custAtts.First();

                if (!parseAtt.ConstructorArguments.Any())
                {
                    ParseChildrenAttribute = new ParseChildrenAttribute();
                    //public ParseChildrenAttribute();
                }
                else if (parseAtt.ConstructorArguments.Count == 1)
                {
                    //public ParseChildrenAttribute(bool childrenAsProperties);
                    //public ParseChildrenAttribute(Type childControlType);
                    if (parseAtt.ConstructorArguments[0].ArgumentType == typeof(bool))
                    {
                        bool val = (bool)parseAtt.ConstructorArguments[0].Value;
                        ParseChildrenAttribute = new ParseChildrenAttribute(val);
                    }
                    else if (parseAtt.ConstructorArguments[0].ArgumentType == typeof(Type))
                    {
                        Type val = (Type)parseAtt.ConstructorArguments[0].Value;
                        ParseChildrenAttribute = new ParseChildrenAttribute(val);
                    }
                }
                else
                {
                    //public ParseChildrenAttribute(bool childrenAsProperties, string defaultProperty);
                    bool   val            = (bool)parseAtt.ConstructorArguments[0].Value;
                    String defaultPropVal = (string)parseAtt.ConstructorArguments[1].Value;
                    ParseChildrenAttribute = new ParseChildrenAttribute(val, defaultPropVal);
                }
            }

            //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.
            var controlBuilderAttsData = CustomAttributeData.GetCustomAttributes(ControlType).Where(a => a.AttributeType == typeof(System.Web.UI.ControlBuilderAttribute));

            if (controlBuilderAttsData.Any())
            {
                var controlBuilderAttData = custAtts.First();
                if (controlBuilderAttData.ConstructorArguments.Count() == 1)
                {
                    Type val = (Type)controlBuilderAttData.ConstructorArguments[0].Value;
                    ControlBuilderAttribute = new System.Web.UI.ControlBuilderAttribute(val);
                }
            }

            //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 == GetHtmlTagRegistration(assemblies.SystemWebAssembly).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));
                    }
                }
            }
        }