///// <summary>
        ///// Given a tag with runat="server" on an HTML element, find a suitable HTML server control for it.
        ///// </summary>
        ///// <param name="tag">The tag to find an HTML server control for.</param>
        ///// <returns>The matching HTML server control.</returns>
        //private static Type FindMatchingHtmlControlType(Tag tag)
        //{


        //    switch (tag.TagName)
        //    {
        //        case "a": return typeof(System.Web.UI.HtmlControls.HtmlAnchor);
        //        case "button": return typeof(System.Web.UI.HtmlControls.HtmlButton);
        //        case "form": return typeof(System.Web.UI.HtmlControls.HtmlForm);
        //        case "head": return typeof(System.Web.UI.HtmlControls.HtmlHead);
        //        case "img": return typeof(System.Web.UI.HtmlControls.HtmlImage);
        //        case "link": return typeof(System.Web.UI.HtmlControls.HtmlLink);
        //        case "meta": return typeof(System.Web.UI.HtmlControls.HtmlMeta);
        //        case "select": return typeof(System.Web.UI.HtmlControls.HtmlSelect);
        //        case "table": return typeof(System.Web.UI.HtmlControls.HtmlTable);
        //        case "td": return typeof(System.Web.UI.HtmlControls.HtmlTableCell);
        //        case "th": return typeof(System.Web.UI.HtmlControls.HtmlTableCell);
        //        case "tr": return typeof(System.Web.UI.HtmlControls.HtmlTableRow);
        //        case "textarea": return typeof(System.Web.UI.HtmlControls.HtmlTextArea);
        //        case "title": return typeof(System.Web.UI.HtmlControls.HtmlTitle);

        //        case "input":
        //            string type = tag["type"];
        //            if (!string.IsNullOrEmpty(type))
        //            {
        //                switch (type.ToLower())
        //                {
        //                    case "button": return typeof(System.Web.UI.HtmlControls.HtmlInputButton);
        //                    case "checkbox": return typeof(System.Web.UI.HtmlControls.HtmlInputCheckBox);
        //                    case "file": return typeof(System.Web.UI.HtmlControls.HtmlInputFile);
        //                    case "hidden": return typeof(System.Web.UI.HtmlControls.HtmlInputHidden);
        //                    case "image": return typeof(System.Web.UI.HtmlControls.HtmlInputImage);
        //                    case "password": return typeof(System.Web.UI.HtmlControls.HtmlInputPassword);
        //                    case "radio": return typeof(System.Web.UI.HtmlControls.HtmlInputRadioButton);
        //                    case "reset": return typeof(System.Web.UI.HtmlControls.HtmlInputReset);
        //                    case "submit": return typeof(System.Web.UI.HtmlControls.HtmlInputSubmit);
        //                    case "text": return typeof(System.Web.UI.HtmlControls.HtmlInputText);
        //                }
        //            }
        //            return typeof(System.Web.UI.HtmlControls.HtmlGenericControl);

        //        default:
        //            return typeof(System.Web.UI.HtmlControls.HtmlGenericControl);
        //    }
        //}

        /// <summary>
        /// Find all of the properties for this control, via reflection.
        /// </summary>
        /// <returns>A dictionary of properties for the given control type.</returns>
        private static Dictionary <string, ReflectedControlProperty> CollectControlProperties(ICompileContext compileContext, Type controlType, ReflectedControl reflectedControl)
        {
            Dictionary <string, ReflectedControlProperty> controlProperties = new Dictionary <string, ReflectedControlProperty>();

            // We have to include NonPublic properties, since internal, public, or protected properties can all be
            // legally referenced from the markup.
            PropertyInfo[] propertyInfos = controlType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                // Find out what kind of getters and setter this property has.  If it has none, or both of them are private, we can't work with it.
                MethodInfo setMethodInfo      = propertyInfo.GetSetMethod(true);
                MethodInfo getMethodInfo      = propertyInfo.GetGetMethod(true);
                bool       hasUsableSetMethod = (setMethodInfo != null && (setMethodInfo.IsPublic || setMethodInfo.IsFamilyOrAssembly));
                bool       hasUsableGetMethod = (getMethodInfo != null && (getMethodInfo.IsPublic || getMethodInfo.IsFamilyOrAssembly));
                if (!hasUsableSetMethod && !hasUsableGetMethod)
                {
                    continue;
                }

                // We have a public-ish setter.  So add a ReflectedControlProperty instance for this property,
                // since it could be accessible from markup.
                ReflectedControlProperty reflectedControlProperty = new ReflectedControlProperty(reflectedControl, propertyInfo);

                // Add it to the set of known properties for this control.  We don't have the ability to support
                // case differentiation on property names, and ASP.NET will bork if anybody tries.  So if you have
                // multiple properties with the same name that differ only by case, that's just bad mojo, and you
                // should change your controls so that isn't true anymore.
                string lowerName = propertyInfo.Name.ToLower();
                if (controlProperties.ContainsKey(lowerName))
                {
                    ReflectedControlProperty previousProperty  = controlProperties[lowerName];
                    Type         previousPropertyDeclaringType = previousProperty.PropertyInfo.DeclaringType;
                    PropertyInfo baseProperty = previousPropertyDeclaringType.BaseType.GetProperty(lowerName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase);
                    if (baseProperty == null)
                    {
                        compileContext.Warning(string.Format("The server control \"{0}\" contains multiple properties named \"{1}\".  Keeping the {2} declaration and discarding the {3} declaration.",
                                                             controlType.FullName, propertyInfo.Name, controlProperties[lowerName].PropertyInfo.PropertyType.FullName, propertyInfo.PropertyType.FullName));
                    }
                    else
                    {
                        // This appears to be a case of a child class's property shadowing a parent class's property.
                        // That's a safe thing, or should be, so we don't throw out a warning when it happens.
                        compileContext.Verbose(string.Format("The server control \"{0}\" contains multiple properties named \"{1}\" (but one comes from a parent class, so the 'new' keyword was probably involved, and this should be safe).  Keeping the {2} declaration and discarding the {3} declaration.",
                                                             controlType.FullName, propertyInfo.Name, controlProperties[lowerName].PropertyInfo.PropertyType.FullName, propertyInfo.PropertyType.FullName));
                    }
                    continue;
                }
                controlProperties.Add(lowerName, reflectedControlProperty);
            }

            return(controlProperties);
        }
        /// <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));
                    }
                }
            }
        }