void SetSubtype(WebSubtype type, WebFormsDirective directive, List <Error> errors)
        {
            if (Subtype != WebSubtype.None)
            {
                errors.Add(new Error(ErrorType.Error, "Unexpected directive " + directive.Name.FullName, directive.Region));
                return;
            }

            Subtype = type;

            InheritedClass = GetAttributeValueCI(directive.Attributes, "inherits");
            if (ClassName == null)
            {
                ClassName = GetAttributeValueCI(directive.Attributes, "classname");
            }
            CodeBehindFile = GetAttributeValueCI(directive.Attributes, "codebehind");
            Language       = GetAttributeValueCI(directive.Attributes, "language");
            CodeFile       = GetAttributeValueCI(directive.Attributes, "codefile");
        }
        void HandleDirective(WebFormsDirective directive, List <Error> errors)
        {
            switch (directive.Name.Name.ToLowerInvariant())
            {
            case "page":
                MasterPageFile = GetAttributeValueCI(directive.Attributes, "masterpagefile");
                SetSubtype(WebSubtype.WebForm, directive, errors);
                break;

            case "control":
                SetSubtype(WebSubtype.WebControl, directive, errors);
                break;

            case "webservice":
                SetSubtype(WebSubtype.WebService, directive, errors);
                break;

            case "webhandler":
                SetSubtype(WebSubtype.WebHandler, directive, errors);
                break;

            case "application":
                SetSubtype(WebSubtype.Global, directive, errors);
                break;

            case "master":
                SetSubtype(WebSubtype.MasterPage, directive, errors);
                break;

            case "mastertype":
                if (MasterPageTypeVPath != null || MasterPageTypeName != null)
                {
                    errors.Add(new Error(ErrorType.Error, "Unexpected second mastertype directive", directive.Region));
                    return;
                }
                MasterPageTypeName  = GetAttributeValueCI(directive.Attributes, "typename");
                MasterPageTypeVPath = GetAttributeValueCI(directive.Attributes, "virtualpath");
                if (string.IsNullOrEmpty(MasterPageTypeName) == string.IsNullOrEmpty(MasterPageTypeVPath))
                {
                    errors.Add(new Error(
                                   ErrorType.Error,
                                   "Mastertype directive must have non-empty 'typename' or 'virtualpath' attribute",
                                   directive.Region
                                   )
                               );
                }
                break;

            case "register":
                string tagPrefix = GetAttributeValueCI(directive.Attributes, "tagprefix");
                string tagName   = GetAttributeValueCI(directive.Attributes, "tagname");
                string src       = GetAttributeValueCI(directive.Attributes, "src");
                string nspace    = GetAttributeValueCI(directive.Attributes, "namespace");
                string assembly  = GetAttributeValueCI(directive.Attributes, "assembly");
                if (!string.IsNullOrEmpty(tagPrefix))
                {
                    if (!string.IsNullOrEmpty(tagName) && !string.IsNullOrEmpty(src))
                    {
                        registeredTags.Add(new ControlRegisterDirective(tagPrefix, tagName, src));
                    }
                    else if (!string.IsNullOrEmpty(nspace) && !string.IsNullOrEmpty(assembly))
                    {
                        registeredTags.Add(new AssemblyRegisterDirective(tagPrefix, nspace, assembly));
                    }
                }
                break;

            case "assembly":
                var assm = new AssemblyDirective(
                    GetAttributeValueCI(directive.Attributes, "name"),
                    GetAttributeValueCI(directive.Attributes, "src"));
                if (assm.IsValid())
                {
                    assemblies.Add(assm);
                }
                else
                {
                    errors.Add(new Error(
                                   ErrorType.Error,
                                   "Assembly directive must have non-empty 'name' or 'src' attribute",
                                   directive.Region
                                   )
                               );
                }
                break;

            case "import":
                string ns = GetAttributeValueCI(directive.Attributes, "namespace");
                if (!string.IsNullOrEmpty(ns))
                {
                    imports.Add(ns);
                }
                else
                {
                    errors.Add(new Error(
                                   ErrorType.Error,
                                   "Import directive must have non-empty 'namespace' attribute",
                                   directive.Region
                                   )
                               );
                }
                break;

            case "implements":
                string interf = GetAttributeValueCI(directive.Attributes, "interface");
                if (!string.IsNullOrEmpty(interf))
                {
                    implements.Add(interf);
                }
                else
                {
                    errors.Add(new Error(
                                   ErrorType.Error,
                                   "Implements directive must have non-empty 'interface' attribute",
                                   directive.Region
                                   )
                               );
                }
                break;

            default:
                break;
            }
        }
        public override XmlParserState PushChar(char c, IXmlParserContext context, ref string rollback)
        {
            var directive = context.Nodes.Peek() as WebFormsDirective;

            if (directive == null || directive.IsComplete)
            {
                directive = new WebFormsDirective(context.LocationMinus(4));                   // 4 == <%@ + current char
                context.Nodes.Push(directive);
            }

            if (c == '<')
            {
                context.LogError("Unexpected '<' in directive.");
                rollback = string.Empty;
                return(Parent);
            }

            Debug.Assert(!directive.IsComplete);

            if (context.StateTag != ENDING && c == '%')
            {
                context.StateTag = ENDING;
                return(null);
            }


            if (context.StateTag == ENDING)
            {
                if (c == '>')
                {
                    //have already checked that directive is not null, i.e. top of stack is our directive
                    context.Nodes.Pop();

                    if (!directive.IsNamed)
                    {
                        context.LogError("Directive closed prematurely.");
                    }
                    else
                    {
                        directive.End(context.Location);
                        if (context.BuildTree)
                        {
                            var container = (XContainer)context.Nodes.Peek();
                            container.AddChildNode(directive);
                        }
                    }
                    return(Parent);
                }
                //ending but not '>'? Error; go to end.
            }
            else if (char.IsLetter(c))
            {
                rollback = string.Empty;
                if (!directive.IsNamed)
                {
                    return(NameState);
                }
                else
                {
                    return(AttributeState);
                }
            }
            else if (char.IsWhiteSpace(c))
            {
                return(null);
            }

            rollback = string.Empty;
            context.LogError("Unexpected character '" + c + "' in tag.");
            return(Parent);
        }