001    /*
002     * Copyright (c) 2007 Henri Sivonen
003     * Copyright (c) 2007-2011 Mozilla Foundation
004     * Portions of comments Copyright 2004-2008 Apple Computer, Inc., Mozilla 
005     * Foundation, and Opera Software ASA.
006     *
007     * Permission is hereby granted, free of charge, to any person obtaining a 
008     * copy of this software and associated documentation files (the "Software"), 
009     * to deal in the Software without restriction, including without limitation 
010     * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
011     * and/or sell copies of the Software, and to permit persons to whom the 
012     * Software is furnished to do so, subject to the following conditions:
013     *
014     * The above copyright notice and this permission notice shall be included in 
015     * all copies or substantial portions of the Software.
016     *
017     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
018     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
019     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
020     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
021     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
022     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
023     * DEALINGS IN THE SOFTWARE.
024     */
025    
026    /*
027     * The comments following this one that use the same comment syntax as this 
028     * comment are quotes from the WHATWG HTML 5 spec as of 27 June 2007 
029     * amended as of June 28 2007.
030     * That document came with this statement:
031     * "© Copyright 2004-2007 Apple Computer, Inc., Mozilla Foundation, and 
032     * Opera Software ASA. You are granted a license to use, reproduce and 
033     * create derivative works of this document."
034     */
035    
036    package nu.validator.htmlparser.impl;
037    
038    import java.util.Arrays;
039    import java.util.HashMap;
040    import java.util.Map;
041    
042    import nu.validator.htmlparser.annotation.Auto;
043    import nu.validator.htmlparser.annotation.Const;
044    import nu.validator.htmlparser.annotation.IdType;
045    import nu.validator.htmlparser.annotation.Inline;
046    import nu.validator.htmlparser.annotation.Literal;
047    import nu.validator.htmlparser.annotation.Local;
048    import nu.validator.htmlparser.annotation.NoLength;
049    import nu.validator.htmlparser.annotation.NsUri;
050    import nu.validator.htmlparser.common.DoctypeExpectation;
051    import nu.validator.htmlparser.common.DocumentMode;
052    import nu.validator.htmlparser.common.DocumentModeHandler;
053    import nu.validator.htmlparser.common.Interner;
054    import nu.validator.htmlparser.common.TokenHandler;
055    import nu.validator.htmlparser.common.XmlViolationPolicy;
056    
057    import org.xml.sax.ErrorHandler;
058    import org.xml.sax.SAXException;
059    import org.xml.sax.SAXParseException;
060    
061    public abstract class TreeBuilder<T> implements TokenHandler,
062            TreeBuilderState<T> {
063        
064        /**
065         * Array version of U+FFFD.
066         */
067        private static final @NoLength char[] REPLACEMENT_CHARACTER = { '\uFFFD' };
068        
069        // Start dispatch groups
070    
071        final static int OTHER = 0;
072    
073        final static int A = 1;
074    
075        final static int BASE = 2;
076    
077        final static int BODY = 3;
078    
079        final static int BR = 4;
080    
081        final static int BUTTON = 5;
082    
083        final static int CAPTION = 6;
084    
085        final static int COL = 7;
086    
087        final static int COLGROUP = 8;
088    
089        final static int FORM = 9;
090    
091        final static int FRAME = 10;
092    
093        final static int FRAMESET = 11;
094    
095        final static int IMAGE = 12;
096    
097        final static int INPUT = 13;
098    
099        final static int ISINDEX = 14;
100    
101        final static int LI = 15;
102    
103        final static int LINK_OR_BASEFONT_OR_BGSOUND = 16;
104    
105        final static int MATH = 17;
106    
107        final static int META = 18;
108    
109        final static int SVG = 19;
110    
111        final static int HEAD = 20;
112    
113        final static int HR = 22;
114    
115        final static int HTML = 23;
116    
117        final static int NOBR = 24;
118    
119        final static int NOFRAMES = 25;
120    
121        final static int NOSCRIPT = 26;
122    
123        final static int OPTGROUP = 27;
124    
125        final static int OPTION = 28;
126    
127        final static int P = 29;
128    
129        final static int PLAINTEXT = 30;
130    
131        final static int SCRIPT = 31;
132    
133        final static int SELECT = 32;
134    
135        final static int STYLE = 33;
136    
137        final static int TABLE = 34;
138    
139        final static int TEXTAREA = 35;
140    
141        final static int TITLE = 36;
142    
143        final static int TR = 37;
144    
145        final static int XMP = 38;
146    
147        final static int TBODY_OR_THEAD_OR_TFOOT = 39;
148    
149        final static int TD_OR_TH = 40;
150    
151        final static int DD_OR_DT = 41;
152    
153        final static int H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6 = 42;
154    
155        final static int MARQUEE_OR_APPLET = 43;
156    
157        final static int PRE_OR_LISTING = 44;
158    
159        final static int B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U = 45;
160    
161        final static int UL_OR_OL_OR_DL = 46;
162    
163        final static int IFRAME = 47;
164    
165        final static int EMBED_OR_IMG = 48;
166    
167        final static int AREA_OR_WBR = 49;
168    
169        final static int DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU = 50;
170    
171        final static int ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_NAV_OR_SECTION_OR_SUMMARY = 51;
172    
173        final static int RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR = 52;
174    
175        final static int RT_OR_RP = 53;
176    
177        final static int COMMAND = 54;
178    
179        final static int PARAM_OR_SOURCE_OR_TRACK = 55;
180    
181        final static int MGLYPH_OR_MALIGNMARK = 56;
182    
183        final static int MI_MO_MN_MS_MTEXT = 57;
184    
185        final static int ANNOTATION_XML = 58;
186    
187        final static int FOREIGNOBJECT_OR_DESC = 59;
188    
189        final static int NOEMBED = 60;
190    
191        final static int FIELDSET = 61;
192    
193        final static int OUTPUT_OR_LABEL = 62;
194    
195        final static int OBJECT = 63;
196    
197        final static int FONT = 64;
198    
199        final static int KEYGEN = 65;
200    
201        // start insertion modes
202    
203        private static final int INITIAL = 0;
204    
205        private static final int BEFORE_HTML = 1;
206    
207        private static final int BEFORE_HEAD = 2;
208    
209        private static final int IN_HEAD = 3;
210    
211        private static final int IN_HEAD_NOSCRIPT = 4;
212    
213        private static final int AFTER_HEAD = 5;
214    
215        private static final int IN_BODY = 6;
216    
217        private static final int IN_TABLE = 7;
218    
219        private static final int IN_CAPTION = 8;
220    
221        private static final int IN_COLUMN_GROUP = 9;
222    
223        private static final int IN_TABLE_BODY = 10;
224    
225        private static final int IN_ROW = 11;
226    
227        private static final int IN_CELL = 12;
228    
229        private static final int IN_SELECT = 13;
230    
231        private static final int IN_SELECT_IN_TABLE = 14;
232    
233        private static final int AFTER_BODY = 15;
234    
235        private static final int IN_FRAMESET = 16;
236    
237        private static final int AFTER_FRAMESET = 17;
238    
239        private static final int AFTER_AFTER_BODY = 18;
240    
241        private static final int AFTER_AFTER_FRAMESET = 19;
242    
243        private static final int TEXT = 20;
244    
245        private static final int FRAMESET_OK = 21;
246    
247        // start charset states
248    
249        private static final int CHARSET_INITIAL = 0;
250    
251        private static final int CHARSET_C = 1;
252    
253        private static final int CHARSET_H = 2;
254    
255        private static final int CHARSET_A = 3;
256    
257        private static final int CHARSET_R = 4;
258    
259        private static final int CHARSET_S = 5;
260    
261        private static final int CHARSET_E = 6;
262    
263        private static final int CHARSET_T = 7;
264    
265        private static final int CHARSET_EQUALS = 8;
266    
267        private static final int CHARSET_SINGLE_QUOTED = 9;
268    
269        private static final int CHARSET_DOUBLE_QUOTED = 10;
270    
271        private static final int CHARSET_UNQUOTED = 11;
272    
273        // end pseudo enums
274    
275        // [NOCPP[
276    
277        private final static String[] HTML4_PUBLIC_IDS = {
278                "-//W3C//DTD HTML 4.0 Frameset//EN",
279                "-//W3C//DTD HTML 4.0 Transitional//EN",
280                "-//W3C//DTD HTML 4.0//EN", "-//W3C//DTD HTML 4.01 Frameset//EN",
281                "-//W3C//DTD HTML 4.01 Transitional//EN",
282                "-//W3C//DTD HTML 4.01//EN" };
283    
284        // ]NOCPP]
285    
286        @Literal private final static String[] QUIRKY_PUBLIC_IDS = {
287                "+//silmaril//dtd html pro v0r11 19970101//",
288                "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
289                "-//as//dtd html 3.0 aswedit + extensions//",
290                "-//ietf//dtd html 2.0 level 1//",
291                "-//ietf//dtd html 2.0 level 2//",
292                "-//ietf//dtd html 2.0 strict level 1//",
293                "-//ietf//dtd html 2.0 strict level 2//",
294                "-//ietf//dtd html 2.0 strict//",
295                "-//ietf//dtd html 2.0//",
296                "-//ietf//dtd html 2.1e//",
297                "-//ietf//dtd html 3.0//",
298                "-//ietf//dtd html 3.2 final//",
299                "-//ietf//dtd html 3.2//",
300                "-//ietf//dtd html 3//",
301                "-//ietf//dtd html level 0//",
302                "-//ietf//dtd html level 1//",
303                "-//ietf//dtd html level 2//",
304                "-//ietf//dtd html level 3//",
305                "-//ietf//dtd html strict level 0//",
306                "-//ietf//dtd html strict level 1//",
307                "-//ietf//dtd html strict level 2//",
308                "-//ietf//dtd html strict level 3//",
309                "-//ietf//dtd html strict//",
310                "-//ietf//dtd html//",
311                "-//metrius//dtd metrius presentational//",
312                "-//microsoft//dtd internet explorer 2.0 html strict//",
313                "-//microsoft//dtd internet explorer 2.0 html//",
314                "-//microsoft//dtd internet explorer 2.0 tables//",
315                "-//microsoft//dtd internet explorer 3.0 html strict//",
316                "-//microsoft//dtd internet explorer 3.0 html//",
317                "-//microsoft//dtd internet explorer 3.0 tables//",
318                "-//netscape comm. corp.//dtd html//",
319                "-//netscape comm. corp.//dtd strict html//",
320                "-//o'reilly and associates//dtd html 2.0//",
321                "-//o'reilly and associates//dtd html extended 1.0//",
322                "-//o'reilly and associates//dtd html extended relaxed 1.0//",
323                "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
324                "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
325                "-//spyglass//dtd html 2.0 extended//",
326                "-//sq//dtd html 2.0 hotmetal + extensions//",
327                "-//sun microsystems corp.//dtd hotjava html//",
328                "-//sun microsystems corp.//dtd hotjava strict html//",
329                "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//",
330                "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//",
331                "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//",
332                "-//w3c//dtd html 4.0 transitional//",
333                "-//w3c//dtd html experimental 19960712//",
334                "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//",
335                "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//",
336                "-//webtechs//dtd mozilla html//" };
337    
338        private static final int NOT_FOUND_ON_STACK = Integer.MAX_VALUE;
339    
340        // [NOCPP[
341    
342        private static final @Local String HTML_LOCAL = "html";
343    
344        // ]NOCPP]
345    
346        private int mode = INITIAL;
347    
348        private int originalMode = INITIAL;
349        
350        /**
351         * Used only when moving back to IN_BODY.
352         */
353        private boolean framesetOk = true;
354    
355        protected Tokenizer tokenizer;
356    
357        // [NOCPP[
358    
359        protected ErrorHandler errorHandler;
360    
361        private DocumentModeHandler documentModeHandler;
362    
363        private DoctypeExpectation doctypeExpectation = DoctypeExpectation.HTML;
364    
365        // ]NOCPP]
366    
367        private boolean scriptingEnabled = false;
368    
369        private boolean needToDropLF;
370    
371        // [NOCPP[
372    
373        private boolean wantingComments;
374    
375        // ]NOCPP]
376    
377        private boolean fragment;
378    
379        private @Local String contextName;
380    
381        private @NsUri String contextNamespace;
382    
383        private T contextNode;
384    
385        private @Auto StackNode<T>[] stack;
386    
387        private int currentPtr = -1;
388    
389        private @Auto StackNode<T>[] listOfActiveFormattingElements;
390    
391        private int listPtr = -1;
392    
393        private T formPointer;
394    
395        private T headPointer;
396    
397        /**
398         * Used to work around Gecko limitations. Not used in Java.
399         */
400        private T deepTreeSurrogateParent;
401    
402        protected @Auto char[] charBuffer;
403    
404        protected int charBufferLen = 0;
405    
406        private boolean quirks = false;
407    
408        // [NOCPP[
409    
410        private boolean reportingDoctype = true;
411    
412        private XmlViolationPolicy namePolicy = XmlViolationPolicy.ALTER_INFOSET;
413    
414        private final Map<String, LocatorImpl> idLocations = new HashMap<String, LocatorImpl>();
415    
416        private boolean html4;
417    
418        // ]NOCPP]
419    
420        protected TreeBuilder() {
421            fragment = false;
422        }
423    
424        /**
425         * Reports an condition that would make the infoset incompatible with XML
426         * 1.0 as fatal.
427         * 
428         * @throws SAXException
429         * @throws SAXParseException
430         */
431        protected void fatal() throws SAXException {
432        }
433    
434        // [NOCPP[
435    
436        protected final void fatal(Exception e) throws SAXException {
437            SAXParseException spe = new SAXParseException(e.getMessage(),
438                    tokenizer, e);
439            if (errorHandler != null) {
440                errorHandler.fatalError(spe);
441            }
442            throw spe;
443        }
444    
445        final void fatal(String s) throws SAXException {
446            SAXParseException spe = new SAXParseException(s, tokenizer);
447            if (errorHandler != null) {
448                errorHandler.fatalError(spe);
449            }
450            throw spe;
451        }
452    
453        /**
454         * Reports a Parse Error.
455         * 
456         * @param message
457         *            the message
458         * @throws SAXException
459         */
460        final void err(String message) throws SAXException {
461            if (errorHandler == null) {
462                return;
463            }
464            errNoCheck(message);
465        }
466        
467        /**
468         * Reports a Parse Error without checking if an error handler is present.
469         * 
470         * @param message
471         *            the message
472         * @throws SAXException
473         */
474        final void errNoCheck(String message) throws SAXException {
475            SAXParseException spe = new SAXParseException(message, tokenizer);
476            errorHandler.error(spe);
477        }
478    
479        /**
480         * Reports a stray start tag.
481         * @param name the name of the stray tag
482         * 
483         * @throws SAXException
484         */
485        private void errStrayStartTag(String name) throws SAXException {
486            err("Stray end tag \u201C" + name + "\u201D.");
487        }
488    
489        /**
490         * Reports a stray end tag.
491         * @param name the name of the stray tag
492         * 
493         * @throws SAXException
494         */
495        private void errStrayEndTag(String name) throws SAXException {
496            err("Stray end tag \u201C" + name + "\u201D.");
497        }
498        
499        /**
500         * Reports a state when elements expected to be closed were not.
501         * 
502         * @param eltPos the position of the start tag on the stack of the element
503         * being closed.
504         * @param name the name of the end tag
505         * 
506         * @throws SAXException
507         */
508        private void errUnclosedElements(int eltPos, String name) throws SAXException {
509            errNoCheck("End tag \u201C" + name + "\u201D seen, but there were open elements.");
510            errListUnclosedStartTags(eltPos);
511        }
512    
513        /**
514         * Reports a state when elements expected to be closed ahead of an implied 
515         * end tag but were not.
516         * 
517         * @param eltPos the position of the start tag on the stack of the element
518         * being closed.
519         * @param name the name of the end tag
520         * 
521         * @throws SAXException
522         */
523        private void errUnclosedElementsImplied(int eltPos, String name) throws SAXException {
524            errNoCheck("End tag \u201C" + name + "\u201D implied, but there were open elements.");
525            errListUnclosedStartTags(eltPos);
526        }
527    
528        /**
529         * Reports a state when elements expected to be closed ahead of an implied 
530         * table cell close.
531         * 
532         * @param eltPos the position of the start tag on the stack of the element
533         * being closed.
534         * @throws SAXException
535         */
536        private void errUnclosedElementsCell(int eltPos) throws SAXException {
537            errNoCheck("A table cell was implicitly closed, but there were open elements.");
538            errListUnclosedStartTags(eltPos);
539        }
540        
541        private void errListUnclosedStartTags(int eltPos) throws SAXException {
542            if (currentPtr != -1) {
543                for (int i = currentPtr; i > eltPos; i--) {
544                    reportUnclosedElementNameAndLocation(i);
545                }
546            }
547        }
548    
549        /**
550         * Reports arriving at/near end of document with unclosed elements remaining.
551         * 
552         * @param message
553         *            the message
554         * @throws SAXException
555         */
556        private void errEndWithUnclosedElements(String message) throws SAXException {
557            if (errorHandler == null) {
558                return;
559            }
560            errNoCheck(message);
561            // just report all remaining unclosed elements
562            errListUnclosedStartTags(0);
563        }
564    
565        /**
566         * Reports the name and location of an unclosed element.
567         * 
568         * @throws SAXException
569         */
570        private final void reportUnclosedElementNameAndLocation(int pos) throws SAXException {
571            StackNode<T> node = stack[pos];
572            if (node.isOptionalEndTag()) {
573                return;
574            }
575            TaintableLocatorImpl locator = node.getLocator();
576            if (locator.isTainted()) {
577                return;
578            }
579            locator.markTainted();
580            SAXParseException spe = new SAXParseException(
581                    "Unclosed element \u201C" + node.popName + "\u201D.", locator);
582            errorHandler.error(spe);
583        }
584    
585        /**
586         * Reports a warning
587         * 
588         * @param message
589         *            the message
590         * @throws SAXException
591         */
592        final void warn(String message) throws SAXException {
593            if (errorHandler == null) {
594                return;
595            }
596            SAXParseException spe = new SAXParseException(message, tokenizer);
597            errorHandler.warning(spe);
598        }
599    
600        // ]NOCPP]
601        
602        @SuppressWarnings("unchecked") public final void startTokenization(Tokenizer self) throws SAXException {
603            tokenizer = self;
604            stack = new StackNode[64];
605            listOfActiveFormattingElements = new StackNode[64];
606            needToDropLF = false;
607            originalMode = INITIAL;
608            currentPtr = -1;
609            listPtr = -1;
610            formPointer = null;
611            headPointer = null;
612            deepTreeSurrogateParent = null;
613            // [NOCPP[
614            html4 = false;
615            idLocations.clear();
616            wantingComments = wantsComments();
617            // ]NOCPP]
618            start(fragment);
619            charBufferLen = 0;
620            charBuffer = new char[1024];
621            framesetOk = true;
622            if (fragment) {
623                T elt;
624                if (contextNode != null) {
625                    elt = contextNode;
626                } else {
627                    elt = createHtmlElementSetAsRoot(tokenizer.emptyAttributes());
628                }
629                StackNode<T> node = new StackNode<T>(ElementName.HTML, elt
630                // [NOCPP[
631                        , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
632                // ]NOCPP]
633                );
634                currentPtr++;
635                stack[currentPtr] = node;
636                resetTheInsertionMode();
637                if ("title" == contextName || "textarea" == contextName) {
638                    tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, contextName);
639                } else if ("style" == contextName || "xmp" == contextName
640                        || "iframe" == contextName || "noembed" == contextName
641                        || "noframes" == contextName
642                        || (scriptingEnabled && "noscript" == contextName)) {
643                    tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT, contextName);
644                } else if ("plaintext" == contextName) {
645                    tokenizer.setStateAndEndTagExpectation(Tokenizer.PLAINTEXT, contextName);
646                } else if ("script" == contextName) {
647                    tokenizer.setStateAndEndTagExpectation(Tokenizer.SCRIPT_DATA,
648                            contextName);
649                } else {
650                    tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA, contextName);
651                }
652                contextName = null;
653                contextNode = null;
654            } else {
655                mode = INITIAL;
656            }
657        }
658    
659        public final void doctype(@Local String name, String publicIdentifier,
660                String systemIdentifier, boolean forceQuirks) throws SAXException {
661            needToDropLF = false;
662            if (!isInForeign()) {
663                switch (mode) {
664                    case INITIAL:
665                        // [NOCPP[
666                        if (reportingDoctype) {
667                            // ]NOCPP]
668                            String emptyString = Portability.newEmptyString();
669                            appendDoctypeToDocument(name == null ? "" : name,
670                                    publicIdentifier == null ? emptyString
671                                            : publicIdentifier,
672                                    systemIdentifier == null ? emptyString
673                                            : systemIdentifier);
674                            Portability.releaseString(emptyString);
675                            // [NOCPP[
676                        }
677                        switch (doctypeExpectation) {
678                            case HTML:
679                                // ]NOCPP]
680                                if (isQuirky(name, publicIdentifier,
681                                        systemIdentifier, forceQuirks)) {
682                                    err("Quirky doctype. Expected \u201C<!DOCTYPE html>\u201D.");
683                                    documentModeInternal(DocumentMode.QUIRKS_MODE,
684                                            publicIdentifier, systemIdentifier,
685                                            false);
686                                } else if (isAlmostStandards(publicIdentifier,
687                                        systemIdentifier)) {
688                                    err("Almost standards mode doctype. Expected \u201C<!DOCTYPE html>\u201D.");
689                                    documentModeInternal(
690                                            DocumentMode.ALMOST_STANDARDS_MODE,
691                                            publicIdentifier, systemIdentifier,
692                                            false);
693                                } else {
694                                    // [NOCPP[
695                                    if ((Portability.literalEqualsString(
696                                            "-//W3C//DTD HTML 4.0//EN",
697                                            publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString(
698                                            "http://www.w3.org/TR/REC-html40/strict.dtd",
699                                            systemIdentifier)))
700                                            || (Portability.literalEqualsString(
701                                                    "-//W3C//DTD HTML 4.01//EN",
702                                                    publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString(
703                                                    "http://www.w3.org/TR/html4/strict.dtd",
704                                                    systemIdentifier)))
705                                            || (Portability.literalEqualsString(
706                                                    "-//W3C//DTD XHTML 1.0 Strict//EN",
707                                                    publicIdentifier) && Portability.literalEqualsString(
708                                                    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd",
709                                                    systemIdentifier))
710                                            || (Portability.literalEqualsString(
711                                                    "-//W3C//DTD XHTML 1.1//EN",
712                                                    publicIdentifier) && Portability.literalEqualsString(
713                                                    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd",
714                                                    systemIdentifier))
715    
716                                    ) {
717                                        warn("Obsolete doctype. Expected \u201C<!DOCTYPE html>\u201D.");
718                                    } else if (!((systemIdentifier == null || Portability.literalEqualsString(
719                                            "about:legacy-compat", systemIdentifier)) && publicIdentifier == null)) {
720                                        err("Legacy doctype. Expected \u201C<!DOCTYPE html>\u201D.");
721                                    }
722                                    // ]NOCPP]
723                                    documentModeInternal(
724                                            DocumentMode.STANDARDS_MODE,
725                                            publicIdentifier, systemIdentifier,
726                                            false);
727                                }
728                                // [NOCPP[
729                                break;
730                            case HTML401_STRICT:
731                                html4 = true;
732                                tokenizer.turnOnAdditionalHtml4Errors();
733                                if (isQuirky(name, publicIdentifier,
734                                        systemIdentifier, forceQuirks)) {
735                                    err("Quirky doctype. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
736                                    documentModeInternal(DocumentMode.QUIRKS_MODE,
737                                            publicIdentifier, systemIdentifier,
738                                            true);
739                                } else if (isAlmostStandards(publicIdentifier,
740                                        systemIdentifier)) {
741                                    err("Almost standards mode doctype. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
742                                    documentModeInternal(
743                                            DocumentMode.ALMOST_STANDARDS_MODE,
744                                            publicIdentifier, systemIdentifier,
745                                            true);
746                                } else {
747                                    if ("-//W3C//DTD HTML 4.01//EN".equals(publicIdentifier)) {
748                                        if (!"http://www.w3.org/TR/html4/strict.dtd".equals(systemIdentifier)) {
749                                            warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
750                                        }
751                                    } else {
752                                        err("The doctype was not the HTML 4.01 Strict doctype. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
753                                    }
754                                    documentModeInternal(
755                                            DocumentMode.STANDARDS_MODE,
756                                            publicIdentifier, systemIdentifier,
757                                            true);
758                                }
759                                break;
760                            case HTML401_TRANSITIONAL:
761                                html4 = true;
762                                tokenizer.turnOnAdditionalHtml4Errors();
763                                if (isQuirky(name, publicIdentifier,
764                                        systemIdentifier, forceQuirks)) {
765                                    err("Quirky doctype. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
766                                    documentModeInternal(DocumentMode.QUIRKS_MODE,
767                                            publicIdentifier, systemIdentifier,
768                                            true);
769                                } else if (isAlmostStandards(publicIdentifier,
770                                        systemIdentifier)) {
771                                    if ("-//W3C//DTD HTML 4.01 Transitional//EN".equals(publicIdentifier)
772                                            && systemIdentifier != null) {
773                                        if (!"http://www.w3.org/TR/html4/loose.dtd".equals(systemIdentifier)) {
774                                            warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
775                                        }
776                                    } else {
777                                        err("The doctype was not a non-quirky HTML 4.01 Transitional doctype. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
778                                    }
779                                    documentModeInternal(
780                                            DocumentMode.ALMOST_STANDARDS_MODE,
781                                            publicIdentifier, systemIdentifier,
782                                            true);
783                                } else {
784                                    err("The doctype was not the HTML 4.01 Transitional doctype. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
785                                    documentModeInternal(
786                                            DocumentMode.STANDARDS_MODE,
787                                            publicIdentifier, systemIdentifier,
788                                            true);
789                                }
790                                break;
791                            case AUTO:
792                                html4 = isHtml4Doctype(publicIdentifier);
793                                if (html4) {
794                                    tokenizer.turnOnAdditionalHtml4Errors();
795                                }
796                                if (isQuirky(name, publicIdentifier,
797                                        systemIdentifier, forceQuirks)) {
798                                    err("Quirky doctype. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
799                                    documentModeInternal(DocumentMode.QUIRKS_MODE,
800                                            publicIdentifier, systemIdentifier,
801                                            html4);
802                                } else if (isAlmostStandards(publicIdentifier,
803                                        systemIdentifier)) {
804                                    if ("-//W3C//DTD HTML 4.01 Transitional//EN".equals(publicIdentifier)) {
805                                        if (!"http://www.w3.org/TR/html4/loose.dtd".equals(systemIdentifier)) {
806                                            warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
807                                        }
808                                    } else {
809                                        err("Almost standards mode doctype. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
810                                    }
811                                    documentModeInternal(
812                                            DocumentMode.ALMOST_STANDARDS_MODE,
813                                            publicIdentifier, systemIdentifier,
814                                            html4);
815                                } else {
816                                    if ("-//W3C//DTD HTML 4.01//EN".equals(publicIdentifier)) {
817                                        if (!"http://www.w3.org/TR/html4/strict.dtd".equals(systemIdentifier)) {
818                                            warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
819                                        }
820                                    } else {
821                                        if (!(publicIdentifier == null && systemIdentifier == null)) {
822                                            err("Legacy doctype. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
823                                        }
824                                    }
825                                    documentModeInternal(
826                                            DocumentMode.STANDARDS_MODE,
827                                            publicIdentifier, systemIdentifier,
828                                            html4);
829                                }
830                                break;
831                            case NO_DOCTYPE_ERRORS:
832                                if (isQuirky(name, publicIdentifier,
833                                        systemIdentifier, forceQuirks)) {
834                                    documentModeInternal(DocumentMode.QUIRKS_MODE,
835                                            publicIdentifier, systemIdentifier,
836                                            false);
837                                } else if (isAlmostStandards(publicIdentifier,
838                                        systemIdentifier)) {
839                                    documentModeInternal(
840                                            DocumentMode.ALMOST_STANDARDS_MODE,
841                                            publicIdentifier, systemIdentifier,
842                                            false);
843                                } else {
844                                    documentModeInternal(
845                                            DocumentMode.STANDARDS_MODE,
846                                            publicIdentifier, systemIdentifier,
847                                            false);
848                                }
849                                break;
850                        }
851                        // ]NOCPP]
852    
853                        /*
854                         * 
855                         * Then, switch to the root element mode of the tree
856                         * construction stage.
857                         */
858                        mode = BEFORE_HTML;
859                        return;
860                    default:
861                        break;
862                }
863            }
864            /*
865             * A DOCTYPE token Parse error.
866             */
867            err("Stray doctype.");
868            /*
869             * Ignore the token.
870             */
871            return;
872        }
873    
874        // [NOCPP[
875    
876        private boolean isHtml4Doctype(String publicIdentifier) {
877            if (publicIdentifier != null
878                    && (Arrays.binarySearch(TreeBuilder.HTML4_PUBLIC_IDS,
879                            publicIdentifier) > -1)) {
880                return true;
881            }
882            return false;
883        }
884    
885        // ]NOCPP]
886    
887        public final void comment(@NoLength char[] buf, int start, int length)
888                throws SAXException {
889            needToDropLF = false;
890            // [NOCPP[
891            if (!wantingComments) {
892                return;
893            }
894            // ]NOCPP]
895            if (!isInForeign()) {
896                switch (mode) {
897                    case INITIAL:
898                    case BEFORE_HTML:
899                    case AFTER_AFTER_BODY:
900                    case AFTER_AFTER_FRAMESET:
901                        /*
902                         * A comment token Append a Comment node to the Document
903                         * object with the data attribute set to the data given in
904                         * the comment token.
905                         */
906                        appendCommentToDocument(buf, start, length);
907                        return;
908                    case AFTER_BODY:
909                        /*
910                         * A comment token Append a Comment node to the first
911                         * element in the stack of open elements (the html element),
912                         * with the data attribute set to the data given in the
913                         * comment token.
914                         */
915                        flushCharacters();
916                        appendComment(stack[0].node, buf, start, length);
917                        return;
918                    default:
919                        break;
920                }
921            }
922            /*
923             * A comment token Append a Comment node to the current node with the
924             * data attribute set to the data given in the comment token.
925             */
926            flushCharacters();
927            appendComment(stack[currentPtr].node, buf, start, length);
928            return;
929        }
930    
931        /**
932         * @see nu.validator.htmlparser.common.TokenHandler#characters(char[], int,
933         *      int)
934         */
935        public final void characters(@Const @NoLength char[] buf, int start, int length)
936                throws SAXException {
937            if (needToDropLF) {
938                needToDropLF = false;
939                if (buf[start] == '\n') {
940                    start++;
941                    length--;
942                    if (length == 0) {
943                        return;
944                    }
945                }
946            }
947    
948            // optimize the most common case
949            switch (mode) {
950                case IN_BODY:
951                case IN_CELL:
952                case IN_CAPTION:
953                    if (!isInForeign()) {
954                        reconstructTheActiveFormattingElements();
955                    }
956                    // fall through
957                case TEXT:
958                    accumulateCharacters(buf, start, length);
959                    return;
960                case IN_TABLE:
961                case IN_TABLE_BODY:
962                case IN_ROW:
963                    accumulateCharactersForced(buf, start, length);
964                    return;
965                default:
966                    int end = start + length;
967                    charactersloop: for (int i = start; i < end; i++) {
968                        switch (buf[i]) {
969                            case ' ':
970                            case '\t':
971                            case '\n':
972                            case '\r':
973                            case '\u000C':
974                                /*
975                                 * A character token that is one of one of U+0009
976                                 * CHARACTER TABULATION, U+000A LINE FEED (LF),
977                                 * U+000C FORM FEED (FF), or U+0020 SPACE
978                                 */
979                                switch (mode) {
980                                    case INITIAL:
981                                    case BEFORE_HTML:
982                                    case BEFORE_HEAD:
983                                        /*
984                                         * Ignore the token.
985                                         */
986                                        start = i + 1;
987                                        continue;
988                                    case IN_HEAD:
989                                    case IN_HEAD_NOSCRIPT:
990                                    case AFTER_HEAD:
991                                    case IN_COLUMN_GROUP:
992                                    case IN_FRAMESET:
993                                    case AFTER_FRAMESET:
994                                        /*
995                                         * Append the character to the current node.
996                                         */
997                                        continue;
998                                    case FRAMESET_OK:
999                                    case IN_BODY:
1000                                    case IN_CELL:
1001                                    case IN_CAPTION:
1002                                        if (start < i) {
1003                                            accumulateCharacters(buf, start, i
1004                                                    - start);
1005                                            start = i;
1006                                        }
1007    
1008                                        /*
1009                                         * Reconstruct the active formatting
1010                                         * elements, if any.
1011                                         */
1012                                        if (!isInForeign()) {
1013                                            flushCharacters();
1014                                            reconstructTheActiveFormattingElements();
1015                                        }
1016                                        /*
1017                                         * Append the token's character to the
1018                                         * current node.
1019                                         */
1020                                        break charactersloop;
1021                                    case IN_SELECT:
1022                                    case IN_SELECT_IN_TABLE:
1023                                        break charactersloop;
1024                                    case IN_TABLE:
1025                                    case IN_TABLE_BODY:
1026                                    case IN_ROW:
1027                                        accumulateCharactersForced(buf, i, 1);
1028                                        start = i + 1;
1029                                        continue;
1030                                    case AFTER_BODY:
1031                                    case AFTER_AFTER_BODY:
1032                                    case AFTER_AFTER_FRAMESET:
1033                                        if (start < i) {
1034                                            accumulateCharacters(buf, start, i
1035                                                    - start);
1036                                            start = i;
1037                                        }
1038                                        /*
1039                                         * Reconstruct the active formatting
1040                                         * elements, if any.
1041                                         */
1042                                        flushCharacters();
1043                                        reconstructTheActiveFormattingElements();
1044                                        /*
1045                                         * Append the token's character to the
1046                                         * current node.
1047                                         */
1048                                        continue;
1049                                }
1050                            default:
1051                                /*
1052                                 * A character token that is not one of one of
1053                                 * U+0009 CHARACTER TABULATION, U+000A LINE FEED
1054                                 * (LF), U+000C FORM FEED (FF), or U+0020 SPACE
1055                                 */
1056                                switch (mode) {
1057                                    case INITIAL:
1058                                        /*
1059                                         * Parse error.
1060                                         */
1061                                        // [NOCPP[
1062                                        switch (doctypeExpectation) {
1063                                            case AUTO:
1064                                                err("Non-space characters found without seeing a doctype first. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
1065                                                break;
1066                                            case HTML:
1067                                                err("Non-space characters found without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
1068                                                break;
1069                                            case HTML401_STRICT:
1070                                                err("Non-space characters found without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
1071                                                break;
1072                                            case HTML401_TRANSITIONAL:
1073                                                err("Non-space characters found without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
1074                                                break;
1075                                            case NO_DOCTYPE_ERRORS:
1076                                        }
1077                                        // ]NOCPP]
1078                                        /*
1079                                         * 
1080                                         * Set the document to quirks mode.
1081                                         */
1082                                        documentModeInternal(
1083                                                DocumentMode.QUIRKS_MODE, null,
1084                                                null, false);
1085                                        /*
1086                                         * Then, switch to the root element mode of
1087                                         * the tree construction stage
1088                                         */
1089                                        mode = BEFORE_HTML;
1090                                        /*
1091                                         * and reprocess the current token.
1092                                         */
1093                                        i--;
1094                                        continue;
1095                                    case BEFORE_HTML:
1096                                        /*
1097                                         * Create an HTMLElement node with the tag
1098                                         * name html, in the HTML namespace. Append
1099                                         * it to the Document object.
1100                                         */
1101                                        // No need to flush characters here,
1102                                        // because there's nothing to flush.
1103                                        appendHtmlElementToDocumentAndPush();
1104                                        /* Switch to the main mode */
1105                                        mode = BEFORE_HEAD;
1106                                        /*
1107                                         * reprocess the current token.
1108                                         */
1109                                        i--;
1110                                        continue;
1111                                    case BEFORE_HEAD:
1112                                        if (start < i) {
1113                                            accumulateCharacters(buf, start, i
1114                                                    - start);
1115                                            start = i;
1116                                        }
1117                                        /*
1118                                         * /Act as if a start tag token with the tag
1119                                         * name "head" and no attributes had been
1120                                         * seen,
1121                                         */
1122                                        flushCharacters();
1123                                        appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
1124                                        mode = IN_HEAD;
1125                                        /*
1126                                         * then reprocess the current token.
1127                                         * 
1128                                         * This will result in an empty head element
1129                                         * being generated, with the current token
1130                                         * being reprocessed in the "after head"
1131                                         * insertion mode.
1132                                         */
1133                                        i--;
1134                                        continue;
1135                                    case IN_HEAD:
1136                                        if (start < i) {
1137                                            accumulateCharacters(buf, start, i
1138                                                    - start);
1139                                            start = i;
1140                                        }
1141                                        /*
1142                                         * Act as if an end tag token with the tag
1143                                         * name "head" had been seen,
1144                                         */
1145                                        flushCharacters();
1146                                        pop();
1147                                        mode = AFTER_HEAD;
1148                                        /*
1149                                         * and reprocess the current token.
1150                                         */
1151                                        i--;
1152                                        continue;
1153                                    case IN_HEAD_NOSCRIPT:
1154                                        if (start < i) {
1155                                            accumulateCharacters(buf, start, i
1156                                                    - start);
1157                                            start = i;
1158                                        }
1159                                        /*
1160                                         * Parse error. Act as if an end tag with
1161                                         * the tag name "noscript" had been seen
1162                                         */
1163                                        err("Non-space character inside \u201Cnoscript\u201D inside \u201Chead\u201D.");
1164                                        flushCharacters();
1165                                        pop();
1166                                        mode = IN_HEAD;
1167                                        /*
1168                                         * and reprocess the current token.
1169                                         */
1170                                        i--;
1171                                        continue;
1172                                    case AFTER_HEAD:
1173                                        if (start < i) {
1174                                            accumulateCharacters(buf, start, i
1175                                                    - start);
1176                                            start = i;
1177                                        }
1178                                        /*
1179                                         * Act as if a start tag token with the tag
1180                                         * name "body" and no attributes had been
1181                                         * seen,
1182                                         */
1183                                        flushCharacters();
1184                                        appendToCurrentNodeAndPushBodyElement();
1185                                        mode = FRAMESET_OK;
1186                                        /*
1187                                         * and then reprocess the current token.
1188                                         */
1189                                        i--;
1190                                        continue;
1191                                    case FRAMESET_OK:
1192                                        framesetOk = false;
1193                                        mode = IN_BODY;
1194                                        i--;
1195                                        continue;
1196                                    case IN_BODY:
1197                                    case IN_CELL:
1198                                    case IN_CAPTION:
1199                                        if (start < i) {
1200                                            accumulateCharacters(buf, start, i
1201                                                    - start);
1202                                            start = i;
1203                                        }
1204                                        /*
1205                                         * Reconstruct the active formatting
1206                                         * elements, if any.
1207                                         */
1208                                        if (!isInForeign()) {
1209                                            flushCharacters();
1210                                            reconstructTheActiveFormattingElements();
1211                                        }
1212                                        /*
1213                                         * Append the token's character to the
1214                                         * current node.
1215                                         */
1216                                        break charactersloop;
1217                                    case IN_TABLE:
1218                                    case IN_TABLE_BODY:
1219                                    case IN_ROW:
1220                                        accumulateCharactersForced(buf, i, 1);
1221                                        start = i + 1;
1222                                        continue;
1223                                    case IN_COLUMN_GROUP:
1224                                        if (start < i) {
1225                                            accumulateCharacters(buf, start, i
1226                                                    - start);
1227                                            start = i;
1228                                        }
1229                                        /*
1230                                         * Act as if an end tag with the tag name
1231                                         * "colgroup" had been seen, and then, if
1232                                         * that token wasn't ignored, reprocess the
1233                                         * current token.
1234                                         */
1235                                        if (currentPtr == 0) {
1236                                            err("Non-space in \u201Ccolgroup\u201D when parsing fragment.");
1237                                            start = i + 1;
1238                                            continue;
1239                                        }
1240                                        flushCharacters();
1241                                        pop();
1242                                        mode = IN_TABLE;
1243                                        i--;
1244                                        continue;
1245                                    case IN_SELECT:
1246                                    case IN_SELECT_IN_TABLE:
1247                                        break charactersloop;
1248                                    case AFTER_BODY:
1249                                        err("Non-space character after body.");
1250                                        fatal();
1251                                        mode = framesetOk ? FRAMESET_OK : IN_BODY;
1252                                        i--;
1253                                        continue;
1254                                    case IN_FRAMESET:
1255                                        if (start < i) {
1256                                            accumulateCharacters(buf, start, i
1257                                                    - start);
1258                                            start = i;
1259                                        }
1260                                        /*
1261                                         * Parse error.
1262                                         */
1263                                        err("Non-space in \u201Cframeset\u201D.");
1264                                        /*
1265                                         * Ignore the token.
1266                                         */
1267                                        start = i + 1;
1268                                        continue;
1269                                    case AFTER_FRAMESET:
1270                                        if (start < i) {
1271                                            accumulateCharacters(buf, start, i
1272                                                    - start);
1273                                            start = i;
1274                                        }
1275                                        /*
1276                                         * Parse error.
1277                                         */
1278                                        err("Non-space after \u201Cframeset\u201D.");
1279                                        /*
1280                                         * Ignore the token.
1281                                         */
1282                                        start = i + 1;
1283                                        continue;
1284                                    case AFTER_AFTER_BODY:
1285                                        /*
1286                                         * Parse error.
1287                                         */
1288                                        err("Non-space character in page trailer.");
1289                                        /*
1290                                         * Switch back to the main mode and
1291                                         * reprocess the token.
1292                                         */
1293                                        mode = framesetOk ? FRAMESET_OK : IN_BODY;
1294                                        i--;
1295                                        continue;
1296                                    case AFTER_AFTER_FRAMESET:
1297                                        /*
1298                                         * Parse error.
1299                                         */
1300                                        err("Non-space character in page trailer.");
1301                                        /*
1302                                         * Switch back to the main mode and
1303                                         * reprocess the token.
1304                                         */
1305                                        mode = IN_FRAMESET;
1306                                        i--;
1307                                        continue;
1308                                }
1309                        }
1310                    }
1311                    if (start < end) {
1312                        accumulateCharacters(buf, start, end - start);
1313                    }
1314            }
1315        }
1316    
1317        /**
1318         * @see nu.validator.htmlparser.common.TokenHandler#zeroOriginatingReplacementCharacter()
1319         */
1320        public void zeroOriginatingReplacementCharacter() throws SAXException {
1321            if (mode == TEXT) {
1322                accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
1323                return;
1324            }
1325            if (currentPtr >= 0) {
1326                StackNode<T> stackNode = stack[currentPtr];
1327                if (stackNode.ns == "http://www.w3.org/1999/xhtml") {
1328                    return;
1329                }
1330                if (stackNode.isHtmlIntegrationPoint()) {
1331                    return;
1332                }
1333                if (stackNode.ns == "http://www.w3.org/1998/Math/MathML"
1334                        && stackNode.getGroup() == MI_MO_MN_MS_MTEXT) {
1335                    return;
1336                }
1337                accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
1338            }
1339        }
1340    
1341        public final void eof() throws SAXException {
1342            flushCharacters();
1343            eofloop: for (;;) {
1344                if (isInForeign()) {
1345                    err("End of file in a foreign namespace context.");
1346                    break eofloop;
1347                }
1348                switch (mode) {
1349                    case INITIAL:
1350                        /*
1351                         * Parse error.
1352                         */
1353                        // [NOCPP[
1354                        switch (doctypeExpectation) {
1355                            case AUTO:
1356                                err("End of file seen without seeing a doctype first. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
1357                                break;
1358                            case HTML:
1359                                err("End of file seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
1360                                break;
1361                            case HTML401_STRICT:
1362                                err("End of file seen without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
1363                                break;
1364                            case HTML401_TRANSITIONAL:
1365                                err("End of file seen without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
1366                                break;
1367                            case NO_DOCTYPE_ERRORS:
1368                        }
1369                        // ]NOCPP]
1370                        /*
1371                         * 
1372                         * Set the document to quirks mode.
1373                         */
1374                        documentModeInternal(DocumentMode.QUIRKS_MODE, null, null,
1375                                false);
1376                        /*
1377                         * Then, switch to the root element mode of the tree
1378                         * construction stage
1379                         */
1380                        mode = BEFORE_HTML;
1381                        /*
1382                         * and reprocess the current token.
1383                         */
1384                        continue;
1385                    case BEFORE_HTML:
1386                        /*
1387                         * Create an HTMLElement node with the tag name html, in the
1388                         * HTML namespace. Append it to the Document object.
1389                         */
1390                        appendHtmlElementToDocumentAndPush();
1391                        // XXX application cache manifest
1392                        /* Switch to the main mode */
1393                        mode = BEFORE_HEAD;
1394                        /*
1395                         * reprocess the current token.
1396                         */
1397                        continue;
1398                    case BEFORE_HEAD:
1399                        appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
1400                        mode = IN_HEAD;
1401                        continue;
1402                    case IN_HEAD:
1403                        if (errorHandler != null && currentPtr > 1) {
1404                            errEndWithUnclosedElements("End of file seen and there were open elements.");
1405                        }
1406                        while (currentPtr > 0) {
1407                            popOnEof();
1408                        }
1409                        mode = AFTER_HEAD;
1410                        continue;
1411                    case IN_HEAD_NOSCRIPT:
1412                        errEndWithUnclosedElements("End of file seen and there were open elements.");
1413                        while (currentPtr > 1) {
1414                            popOnEof();
1415                        }
1416                        mode = IN_HEAD;
1417                        continue;
1418                    case AFTER_HEAD:
1419                        appendToCurrentNodeAndPushBodyElement();
1420                        mode = IN_BODY;
1421                        continue;
1422                    case IN_COLUMN_GROUP:
1423                        if (currentPtr == 0) {
1424                            assert fragment;
1425                            break eofloop;
1426                        } else {
1427                            popOnEof();
1428                            mode = IN_TABLE;
1429                            continue;
1430                        }
1431                    case FRAMESET_OK:
1432                    case IN_CAPTION:
1433                    case IN_CELL:
1434                    case IN_BODY:
1435                        // [NOCPP[
1436                        openelementloop: for (int i = currentPtr; i >= 0; i--) {
1437                            int group = stack[i].getGroup();
1438                            switch (group) {
1439                                case DD_OR_DT:
1440                                case LI:
1441                                case P:
1442                                case TBODY_OR_THEAD_OR_TFOOT:
1443                                case TD_OR_TH:
1444                                case BODY:
1445                                case HTML:
1446                                    break;
1447                                default:
1448                                    errEndWithUnclosedElements("End of file seen and there were open elements.");
1449                                    break openelementloop;
1450                            }
1451                        }
1452                        // ]NOCPP]
1453                        break eofloop;
1454                    case TEXT:
1455                        if (errorHandler != null) {
1456                            errNoCheck("End of file seen when expecting text or an end tag.");
1457                            errListUnclosedStartTags(0);
1458                        }
1459                        // XXX mark script as already executed
1460                        if (originalMode == AFTER_HEAD) {
1461                            popOnEof();
1462                        }
1463                        popOnEof();
1464                        mode = originalMode;
1465                        continue;
1466                    case IN_TABLE_BODY:
1467                    case IN_ROW:
1468                    case IN_TABLE:
1469                    case IN_SELECT:
1470                    case IN_SELECT_IN_TABLE:
1471                    case IN_FRAMESET:
1472                        if (errorHandler != null && currentPtr > 0) {
1473                            errEndWithUnclosedElements("End of file seen and there were open elements.");
1474                        }
1475                        break eofloop;
1476                    case AFTER_BODY:
1477                    case AFTER_FRAMESET:
1478                    case AFTER_AFTER_BODY:
1479                    case AFTER_AFTER_FRAMESET:
1480                    default:
1481                        // [NOCPP[
1482                        if (currentPtr == 0) { // This silliness is here to poison
1483                            // buggy compiler optimizations in
1484                            // GWT
1485                            System.currentTimeMillis();
1486                        }
1487                        // ]NOCPP]
1488                        break eofloop;
1489                }
1490            }
1491            while (currentPtr > 0) {
1492                popOnEof();
1493            }
1494            if (!fragment) {
1495                popOnEof();
1496            }
1497            /* Stop parsing. */
1498        }
1499    
1500        /**
1501         * @see nu.validator.htmlparser.common.TokenHandler#endTokenization()
1502         */
1503        public final void endTokenization() throws SAXException {
1504            formPointer = null;
1505            headPointer = null;
1506            deepTreeSurrogateParent = null;
1507            if (stack != null) {
1508                while (currentPtr > -1) {
1509                    stack[currentPtr].release();
1510                    currentPtr--;
1511                }
1512                stack = null;
1513            }
1514            if (listOfActiveFormattingElements != null) {
1515                while (listPtr > -1) {
1516                    if (listOfActiveFormattingElements[listPtr] != null) {
1517                        listOfActiveFormattingElements[listPtr].release();
1518                    }
1519                    listPtr--;
1520                }
1521                listOfActiveFormattingElements = null;
1522            }
1523            // [NOCPP[
1524            idLocations.clear();
1525            // ]NOCPP]
1526            charBuffer = null;
1527            end();
1528        }
1529    
1530        public final void startTag(ElementName elementName,
1531                HtmlAttributes attributes, boolean selfClosing) throws SAXException {
1532            flushCharacters();
1533    
1534            // [NOCPP[
1535            if (errorHandler != null) {
1536                // ID uniqueness
1537                @IdType String id = attributes.getId();
1538                if (id != null) {
1539                    LocatorImpl oldLoc = idLocations.get(id);
1540                    if (oldLoc != null) {
1541                        err("Duplicate ID \u201C" + id + "\u201D.");
1542                        errorHandler.warning(new SAXParseException(
1543                                "The first occurrence of ID \u201C" + id
1544                                + "\u201D was here.", oldLoc));
1545                    } else {
1546                        idLocations.put(id, new LocatorImpl(tokenizer));
1547                    }
1548                }
1549            }
1550            // ]NOCPP]
1551    
1552            int eltPos;
1553            needToDropLF = false;
1554            starttagloop: for (;;) {
1555                int group = elementName.getGroup();
1556                @Local String name = elementName.name;
1557                if (isInForeign()) {
1558                    StackNode<T> currentNode = stack[currentPtr];
1559                    @NsUri String currNs = currentNode.ns;
1560                    if (!(currentNode.isHtmlIntegrationPoint() || (currNs == "http://www.w3.org/1998/Math/MathML" && ((currentNode.getGroup() == MI_MO_MN_MS_MTEXT && group != MGLYPH_OR_MALIGNMARK) || (currentNode.getGroup() == ANNOTATION_XML && group == SVG))))) {
1561                        switch (group) {
1562                            case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
1563                            case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
1564                            case BODY:
1565                            case BR:
1566                            case RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR:
1567                            case DD_OR_DT:
1568                            case UL_OR_OL_OR_DL:
1569                            case EMBED_OR_IMG:
1570                            case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
1571                            case HEAD:
1572                            case HR:
1573                            case LI:
1574                            case META:
1575                            case NOBR:
1576                            case P:
1577                            case PRE_OR_LISTING:
1578                            case TABLE:
1579                                err("HTML start tag \u201C"
1580                                        + name
1581                                        + "\u201D in a foreign namespace context.");
1582                                while (!isSpecialParentInForeign(stack[currentPtr])) {
1583                                    pop();
1584                                }
1585                                continue starttagloop;
1586                            case FONT:
1587                                if (attributes.contains(AttributeName.COLOR)
1588                                        || attributes.contains(AttributeName.FACE)
1589                                        || attributes.contains(AttributeName.SIZE)) {
1590                                    err("HTML start tag \u201C"
1591                                            + name
1592                                            + "\u201D in a foreign namespace context.");
1593                                    while (!isSpecialParentInForeign(stack[currentPtr])) {
1594                                        pop();
1595                                    }
1596                                    continue starttagloop;
1597                                }
1598                                // else fall thru
1599                            default:
1600                                if ("http://www.w3.org/2000/svg" == currNs) {
1601                                    attributes.adjustForSvg();
1602                                    if (selfClosing) {
1603                                        appendVoidElementToCurrentMayFosterSVG(
1604                                                elementName, attributes);
1605                                        selfClosing = false;
1606                                    } else {
1607                                        appendToCurrentNodeAndPushElementMayFosterSVG(
1608                                                elementName, attributes);
1609                                    }
1610                                    attributes = null; // CPP
1611                                    break starttagloop;
1612                                } else {
1613                                    attributes.adjustForMath();
1614                                    if (selfClosing) {
1615                                        appendVoidElementToCurrentMayFosterMathML(
1616                                                elementName, attributes);
1617                                        selfClosing = false;
1618                                    } else {
1619                                        appendToCurrentNodeAndPushElementMayFosterMathML(
1620                                                elementName, attributes);
1621                                    }
1622                                    attributes = null; // CPP
1623                                    break starttagloop;
1624                                }
1625                        } // switch
1626                    } // foreignObject / annotation-xml
1627                }
1628                switch (mode) {
1629                    case IN_TABLE_BODY:
1630                        switch (group) {
1631                            case TR:
1632                                clearStackBackTo(findLastInTableScopeOrRootTbodyTheadTfoot());
1633                                appendToCurrentNodeAndPushElement(
1634                                        elementName,
1635                                        attributes);
1636                                mode = IN_ROW;
1637                                attributes = null; // CPP
1638                                break starttagloop;
1639                            case TD_OR_TH:
1640                                err("\u201C" + name
1641                                        + "\u201D start tag in table body.");
1642                                clearStackBackTo(findLastInTableScopeOrRootTbodyTheadTfoot());
1643                                appendToCurrentNodeAndPushElement(
1644                                        ElementName.TR,
1645                                        HtmlAttributes.EMPTY_ATTRIBUTES);
1646                                mode = IN_ROW;
1647                                continue;
1648                            case CAPTION:
1649                            case COL:
1650                            case COLGROUP:
1651                            case TBODY_OR_THEAD_OR_TFOOT:
1652                                eltPos = findLastInTableScopeOrRootTbodyTheadTfoot();
1653                                if (eltPos == 0) {
1654                                    errStrayStartTag(name);
1655                                    break starttagloop;
1656                                } else {
1657                                    clearStackBackTo(eltPos);
1658                                    pop();
1659                                    mode = IN_TABLE;
1660                                    continue;
1661                                }
1662                            default:
1663                                // fall through to IN_TABLE
1664                        }
1665                    case IN_ROW:
1666                        switch (group) {
1667                            case TD_OR_TH:
1668                                clearStackBackTo(findLastOrRoot(TreeBuilder.TR));
1669                                appendToCurrentNodeAndPushElement(
1670                                        elementName,
1671                                        attributes);
1672                                mode = IN_CELL;
1673                                insertMarker();
1674                                attributes = null; // CPP
1675                                break starttagloop;
1676                            case CAPTION:
1677                            case COL:
1678                            case COLGROUP:
1679                            case TBODY_OR_THEAD_OR_TFOOT:
1680                            case TR:
1681                                eltPos = findLastOrRoot(TreeBuilder.TR);
1682                                if (eltPos == 0) {
1683                                    assert fragment;
1684                                    err("No table row to close.");
1685                                    break starttagloop;
1686                                }
1687                                clearStackBackTo(eltPos);
1688                                pop();
1689                                mode = IN_TABLE_BODY;
1690                                continue;
1691                            default:
1692                                // fall through to IN_TABLE
1693                        }
1694                    case IN_TABLE:
1695                        intableloop: for (;;) {
1696                            switch (group) {
1697                                case CAPTION:
1698                                    clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1699                                    insertMarker();
1700                                    appendToCurrentNodeAndPushElement(
1701                                            elementName,
1702                                            attributes);
1703                                    mode = IN_CAPTION;
1704                                    attributes = null; // CPP
1705                                    break starttagloop;
1706                                case COLGROUP:
1707                                    clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1708                                    appendToCurrentNodeAndPushElement(
1709                                            elementName,
1710                                            attributes);
1711                                    mode = IN_COLUMN_GROUP;
1712                                    attributes = null; // CPP
1713                                    break starttagloop;
1714                                case COL:
1715                                    clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1716                                    appendToCurrentNodeAndPushElement(
1717                                            ElementName.COLGROUP,
1718                                            HtmlAttributes.EMPTY_ATTRIBUTES);
1719                                    mode = IN_COLUMN_GROUP;
1720                                    continue starttagloop;
1721                                case TBODY_OR_THEAD_OR_TFOOT:
1722                                    clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1723                                    appendToCurrentNodeAndPushElement(
1724                                            elementName,
1725                                            attributes);
1726                                    mode = IN_TABLE_BODY;
1727                                    attributes = null; // CPP
1728                                    break starttagloop;
1729                                case TR:
1730                                case TD_OR_TH:
1731                                    clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1732                                    appendToCurrentNodeAndPushElement(
1733                                            ElementName.TBODY,
1734                                            HtmlAttributes.EMPTY_ATTRIBUTES);
1735                                    mode = IN_TABLE_BODY;
1736                                    continue starttagloop;
1737                                case TABLE:
1738                                    err("Start tag for \u201Ctable\u201D seen but the previous \u201Ctable\u201D is still open.");
1739                                    eltPos = findLastInTableScope(name);
1740                                    if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1741                                        assert fragment;
1742                                        break starttagloop;
1743                                    }
1744                                    generateImpliedEndTags();
1745                                    // XXX is the next if dead code?
1746                                    if (errorHandler != null && !isCurrent("table")) {
1747                                        errNoCheck("Unclosed elements on stack.");
1748                                    }
1749                                    while (currentPtr >= eltPos) {
1750                                        pop();
1751                                    }
1752                                    resetTheInsertionMode();
1753                                    continue starttagloop;
1754                                case SCRIPT:
1755                                    // XXX need to manage much more stuff
1756                                    // here if
1757                                    // supporting
1758                                    // document.write()
1759                                    appendToCurrentNodeAndPushElement(
1760                                            elementName,
1761                                            attributes);
1762                                    originalMode = mode;
1763                                    mode = TEXT;
1764                                    tokenizer.setStateAndEndTagExpectation(
1765                                            Tokenizer.SCRIPT_DATA, elementName);
1766                                    attributes = null; // CPP
1767                                    break starttagloop;
1768                                case STYLE:
1769                                    appendToCurrentNodeAndPushElement(
1770                                            elementName,
1771                                            attributes);
1772                                    originalMode = mode;
1773                                    mode = TEXT;
1774                                    tokenizer.setStateAndEndTagExpectation(
1775                                            Tokenizer.RAWTEXT, elementName);
1776                                    attributes = null; // CPP
1777                                    break starttagloop;
1778                                case INPUT:
1779                                    if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
1780                                            "hidden",
1781                                            attributes.getValue(AttributeName.TYPE))) {
1782                                        break intableloop;
1783                                    }
1784                                    appendVoidElementToCurrent(
1785                                            name, attributes,
1786                                            formPointer);
1787                                    selfClosing = false;
1788                                    attributes = null; // CPP
1789                                    break starttagloop;
1790                                case FORM:
1791                                    if (formPointer != null) {
1792                                        err("Saw a \u201Cform\u201D start tag, but there was already an active \u201Cform\u201D element. Nested forms are not allowed. Ignoring the tag.");
1793                                        break starttagloop;
1794                                    } else {
1795                                        err("Start tag \u201Cform\u201D seen in \u201Ctable\u201D.");
1796                                        appendVoidFormToCurrent(attributes);
1797                                        attributes = null; // CPP
1798                                        break starttagloop;
1799                                    }
1800                                default:
1801                                    err("Start tag \u201C" + name
1802                                            + "\u201D seen in \u201Ctable\u201D.");
1803                                    // fall through to IN_BODY
1804                                    break intableloop;
1805                            }
1806                        }
1807                    case IN_CAPTION:
1808                        switch (group) {
1809                            case CAPTION:
1810                            case COL:
1811                            case COLGROUP:
1812                            case TBODY_OR_THEAD_OR_TFOOT:
1813                            case TR:
1814                            case TD_OR_TH:
1815                                errStrayStartTag(name);
1816                                eltPos = findLastInTableScope("caption");
1817                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1818                                    break starttagloop;
1819                                }
1820                                generateImpliedEndTags();
1821                                if (errorHandler != null && currentPtr != eltPos) {
1822                                    errNoCheck("Unclosed elements on stack.");
1823                                }
1824                                while (currentPtr >= eltPos) {
1825                                    pop();
1826                                }
1827                                clearTheListOfActiveFormattingElementsUpToTheLastMarker();
1828                                mode = IN_TABLE;
1829                                continue;
1830                            default:
1831                                // fall through to IN_BODY
1832                        }
1833                    case IN_CELL:
1834                        switch (group) {
1835                            case CAPTION:
1836                            case COL:
1837                            case COLGROUP:
1838                            case TBODY_OR_THEAD_OR_TFOOT:
1839                            case TR:
1840                            case TD_OR_TH:
1841                                eltPos = findLastInTableScopeTdTh();
1842                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1843                                    err("No cell to close.");
1844                                    break starttagloop;
1845                                } else {
1846                                    closeTheCell(eltPos);
1847                                    continue;
1848                                }
1849                            default:
1850                                // fall through to IN_BODY
1851                        }
1852                    case FRAMESET_OK:
1853                        switch (group) {
1854                            case FRAMESET:
1855                                if (mode == FRAMESET_OK) {
1856                                    if (currentPtr == 0 || stack[1].getGroup() != BODY) {
1857                                        assert fragment;
1858                                        errStrayStartTag(name);
1859                                        break starttagloop;
1860                                    } else {
1861                                        err("\u201Cframeset\u201D start tag seen.");
1862                                        detachFromParent(stack[1].node);
1863                                        while (currentPtr > 0) {
1864                                            pop();
1865                                        }
1866                                        appendToCurrentNodeAndPushElement(
1867                                                elementName,
1868                                                attributes);
1869                                        mode = IN_FRAMESET;
1870                                        attributes = null; // CPP
1871                                        break starttagloop;
1872                                    }
1873                                } else {
1874                                    errStrayStartTag(name);
1875                                    break starttagloop;
1876                                }
1877                                // NOT falling through!
1878                            case PRE_OR_LISTING:
1879                            case LI:
1880                            case DD_OR_DT:
1881                            case BUTTON:
1882                            case MARQUEE_OR_APPLET:
1883                            case OBJECT:
1884                            case TABLE:
1885                            case AREA_OR_WBR:
1886                            case BR:
1887                            case EMBED_OR_IMG:
1888                            case INPUT:
1889                            case KEYGEN:
1890                            case HR:
1891                            case TEXTAREA:
1892                            case XMP:
1893                            case IFRAME:
1894                            case SELECT:
1895                                if (mode == FRAMESET_OK
1896                                        && !(group == INPUT && Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
1897                                                "hidden",
1898                                                attributes.getValue(AttributeName.TYPE)))) {
1899                                    framesetOk = false;
1900                                    mode = IN_BODY;
1901                                }
1902                                // fall through to IN_BODY
1903                            default:
1904                                // fall through to IN_BODY
1905                        }
1906                    case IN_BODY:
1907                        inbodyloop: for (;;) {
1908                            switch (group) {
1909                                case HTML:
1910                                    errStrayStartTag(name);
1911                                    if (!fragment) {
1912                                        addAttributesToHtml(attributes);
1913                                        attributes = null; // CPP
1914                                    }
1915                                    break starttagloop;
1916                                case BASE:
1917                                case LINK_OR_BASEFONT_OR_BGSOUND:
1918                                case META:
1919                                case STYLE:
1920                                case SCRIPT:
1921                                case TITLE:
1922                                case COMMAND:
1923                                    // Fall through to IN_HEAD
1924                                    break inbodyloop;
1925                                case BODY:
1926                                    if (currentPtr == 0
1927                                            || stack[1].getGroup() != BODY) {
1928                                        assert fragment;
1929                                        errStrayStartTag(name);
1930                                        break starttagloop;
1931                                    }
1932                                    err("\u201Cbody\u201D start tag found but the \u201Cbody\u201D element is already open.");
1933                                    framesetOk = false;
1934                                    if (mode == FRAMESET_OK) {
1935                                        mode = IN_BODY;
1936                                    }
1937                                    if (addAttributesToBody(attributes)) {
1938                                        attributes = null; // CPP
1939                                    }
1940                                    break starttagloop;
1941                                case P:
1942                                case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
1943                                case UL_OR_OL_OR_DL:
1944                                case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_NAV_OR_SECTION_OR_SUMMARY:
1945                                    implicitlyCloseP();
1946                                    appendToCurrentNodeAndPushElementMayFoster(
1947                                            elementName,
1948                                            attributes);
1949                                    attributes = null; // CPP
1950                                    break starttagloop;
1951                                case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
1952                                    implicitlyCloseP();
1953                                    if (stack[currentPtr].getGroup() == H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) {
1954                                        err("Heading cannot be a child of another heading.");
1955                                        pop();
1956                                    }
1957                                    appendToCurrentNodeAndPushElementMayFoster(
1958                                            elementName,
1959                                            attributes);
1960                                    attributes = null; // CPP
1961                                    break starttagloop;
1962                                case FIELDSET:
1963                                    implicitlyCloseP();
1964                                    appendToCurrentNodeAndPushElementMayFoster(
1965                                            elementName,
1966                                            attributes, formPointer);
1967                                    attributes = null; // CPP
1968                                    break starttagloop;
1969                                case PRE_OR_LISTING:
1970                                    implicitlyCloseP();
1971                                    appendToCurrentNodeAndPushElementMayFoster(
1972                                            elementName,
1973                                            attributes);
1974                                    needToDropLF = true;
1975                                    attributes = null; // CPP
1976                                    break starttagloop;
1977                                case FORM:
1978                                    if (formPointer != null) {
1979                                        err("Saw a \u201Cform\u201D start tag, but there was already an active \u201Cform\u201D element. Nested forms are not allowed. Ignoring the tag.");
1980                                        break starttagloop;
1981                                    } else {
1982                                        implicitlyCloseP();
1983                                        appendToCurrentNodeAndPushFormElementMayFoster(attributes);
1984                                        attributes = null; // CPP
1985                                        break starttagloop;
1986                                    }
1987                                case LI:
1988                                case DD_OR_DT:
1989                                    eltPos = currentPtr;
1990                                    for (;;) {
1991                                        StackNode<T> node = stack[eltPos]; // weak
1992                                        // ref
1993                                        if (node.getGroup() == group) { // LI or
1994                                            // DD_OR_DT
1995                                            generateImpliedEndTagsExceptFor(node.name);
1996                                            if (errorHandler != null
1997                                                    && eltPos != currentPtr) {
1998                                                errNoCheck("Unclosed elements inside a list.");
1999                                            }
2000                                            while (currentPtr >= eltPos) {
2001                                                pop();
2002                                            }
2003                                            break;
2004                                        } else if (node.isScoping()
2005                                                || (node.isSpecial()
2006                                                        && node.name != "p"
2007                                                        && node.name != "address" && node.name != "div")) {
2008                                            break;
2009                                        }
2010                                        eltPos--;
2011                                    }
2012                                    implicitlyCloseP();
2013                                    appendToCurrentNodeAndPushElementMayFoster(
2014                                            elementName,
2015                                            attributes);
2016                                    attributes = null; // CPP
2017                                    break starttagloop;
2018                                case PLAINTEXT:
2019                                    implicitlyCloseP();
2020                                    appendToCurrentNodeAndPushElementMayFoster(
2021                                            elementName,
2022                                            attributes);
2023                                    tokenizer.setStateAndEndTagExpectation(
2024                                            Tokenizer.PLAINTEXT, elementName);
2025                                    attributes = null; // CPP
2026                                    break starttagloop;
2027                                case A:
2028                                    int activeAPos = findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker("a");
2029                                    if (activeAPos != -1) {
2030                                        err("An \u201Ca\u201D start tag seen with already an active \u201Ca\u201D element.");
2031                                        StackNode<T> activeA = listOfActiveFormattingElements[activeAPos];
2032                                        activeA.retain();
2033                                        adoptionAgencyEndTag("a");
2034                                        removeFromStack(activeA);
2035                                        activeAPos = findInListOfActiveFormattingElements(activeA);
2036                                        if (activeAPos != -1) {
2037                                            removeFromListOfActiveFormattingElements(activeAPos);
2038                                        }
2039                                        activeA.release();
2040                                    }
2041                                    reconstructTheActiveFormattingElements();
2042                                    appendToCurrentNodeAndPushFormattingElementMayFoster(
2043                                            elementName,
2044                                            attributes);
2045                                    attributes = null; // CPP
2046                                    break starttagloop;
2047                                case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
2048                                case FONT:
2049                                    reconstructTheActiveFormattingElements();
2050                                    maybeForgetEarlierDuplicateFormattingElement(elementName.name, attributes);
2051                                    appendToCurrentNodeAndPushFormattingElementMayFoster(
2052                                            elementName,
2053                                            attributes);
2054                                    attributes = null; // CPP
2055                                    break starttagloop;
2056                                case NOBR:
2057                                    reconstructTheActiveFormattingElements();
2058                                    if (TreeBuilder.NOT_FOUND_ON_STACK != findLastInScope("nobr")) {
2059                                        err("\u201Cnobr\u201D start tag seen when there was an open \u201Cnobr\u201D element in scope.");
2060                                        adoptionAgencyEndTag("nobr");
2061                                    }
2062                                    appendToCurrentNodeAndPushFormattingElementMayFoster(
2063                                            elementName,
2064                                            attributes);
2065                                    attributes = null; // CPP
2066                                    break starttagloop;
2067                                case BUTTON:
2068                                    eltPos = findLastInScope(name);
2069                                    if (eltPos != TreeBuilder.NOT_FOUND_ON_STACK) {
2070                                        err("\u201Cbutton\u201D start tag seen when there was an open \u201Cbutton\u201D element in scope.");
2071    
2072                                        generateImpliedEndTags();
2073                                        if (errorHandler != null
2074                                                && !isCurrent(name)) {
2075                                            errUnclosedElementsImplied(eltPos, name);
2076                                        }
2077                                        while (currentPtr >= eltPos) {
2078                                            pop();
2079                                        }
2080                                        continue starttagloop;
2081                                    } else {
2082                                        reconstructTheActiveFormattingElements();
2083                                        appendToCurrentNodeAndPushElementMayFoster(
2084                                                elementName,
2085                                                attributes, formPointer);
2086                                        attributes = null; // CPP
2087                                        break starttagloop;
2088                                    }
2089                                case OBJECT:
2090                                    reconstructTheActiveFormattingElements();
2091                                    appendToCurrentNodeAndPushElementMayFoster(
2092                                            elementName,
2093                                            attributes, formPointer);
2094                                    insertMarker();
2095                                    attributes = null; // CPP
2096                                    break starttagloop;
2097                                case MARQUEE_OR_APPLET:
2098                                    reconstructTheActiveFormattingElements();
2099                                    appendToCurrentNodeAndPushElementMayFoster(
2100                                            elementName,
2101                                            attributes);
2102                                    insertMarker();
2103                                    attributes = null; // CPP
2104                                    break starttagloop;
2105                                case TABLE:
2106                                    // The only quirk. Blame Hixie and
2107                                    // Acid2.
2108                                    if (!quirks) {
2109                                        implicitlyCloseP();
2110                                    }
2111                                    appendToCurrentNodeAndPushElementMayFoster(
2112                                            elementName,
2113                                            attributes);
2114                                    mode = IN_TABLE;
2115                                    attributes = null; // CPP
2116                                    break starttagloop;
2117                                case BR:
2118                                case EMBED_OR_IMG:
2119                                case AREA_OR_WBR:
2120                                    reconstructTheActiveFormattingElements();
2121                                    // FALL THROUGH to PARAM_OR_SOURCE_OR_TRACK
2122                                case PARAM_OR_SOURCE_OR_TRACK:
2123                                    appendVoidElementToCurrentMayFoster(
2124                                            elementName,
2125                                            attributes);
2126                                    selfClosing = false;
2127                                    attributes = null; // CPP
2128                                    break starttagloop;
2129                                case HR:
2130                                    implicitlyCloseP();
2131                                    appendVoidElementToCurrentMayFoster(
2132                                            elementName,
2133                                            attributes);
2134                                    selfClosing = false;
2135                                    attributes = null; // CPP
2136                                    break starttagloop;
2137                                case IMAGE:
2138                                    err("Saw a start tag \u201Cimage\u201D.");
2139                                    elementName = ElementName.IMG;
2140                                    continue starttagloop;
2141                                case KEYGEN:
2142                                case INPUT:
2143                                    reconstructTheActiveFormattingElements();
2144                                    appendVoidElementToCurrentMayFoster(
2145                                            name, attributes,
2146                                            formPointer);
2147                                    selfClosing = false;
2148                                    attributes = null; // CPP
2149                                    break starttagloop;
2150                                case ISINDEX:
2151                                    err("\u201Cisindex\u201D seen.");
2152                                    if (formPointer != null) {
2153                                        break starttagloop;
2154                                    }
2155                                    implicitlyCloseP();
2156                                    HtmlAttributes formAttrs = new HtmlAttributes(0);
2157                                    int actionIndex = attributes.getIndex(AttributeName.ACTION);
2158                                    if (actionIndex > -1) {
2159                                        formAttrs.addAttribute(
2160                                                AttributeName.ACTION,
2161                                                attributes.getValue(actionIndex)
2162                                                // [NOCPP[
2163                                                , XmlViolationPolicy.ALLOW
2164                                        // ]NOCPP]
2165                                        );
2166                                    }
2167                                    appendToCurrentNodeAndPushFormElementMayFoster(formAttrs);
2168                                    appendVoidElementToCurrentMayFoster(
2169                                            ElementName.HR,
2170                                            HtmlAttributes.EMPTY_ATTRIBUTES);
2171                                    appendToCurrentNodeAndPushElementMayFoster(
2172                                            ElementName.LABEL,
2173                                            HtmlAttributes.EMPTY_ATTRIBUTES);
2174                                    int promptIndex = attributes.getIndex(AttributeName.PROMPT);
2175                                    if (promptIndex > -1) {
2176                                        @Auto char[] prompt = Portability.newCharArrayFromString(attributes.getValue(promptIndex));
2177                                        appendCharacters(stack[currentPtr].node,
2178                                                prompt, 0, prompt.length);
2179                                    } else {
2180                                        appendIsindexPrompt(stack[currentPtr].node);
2181                                    }
2182                                    HtmlAttributes inputAttributes = new HtmlAttributes(
2183                                            0);
2184                                    inputAttributes.addAttribute(
2185                                            AttributeName.NAME,
2186                                            Portability.newStringFromLiteral("isindex")
2187                                            // [NOCPP[
2188                                            , XmlViolationPolicy.ALLOW
2189                                    // ]NOCPP]
2190                                    );
2191                                    for (int i = 0; i < attributes.getLength(); i++) {
2192                                        AttributeName attributeQName = attributes.getAttributeName(i);
2193                                        if (AttributeName.NAME == attributeQName
2194                                                || AttributeName.PROMPT == attributeQName) {
2195                                            attributes.releaseValue(i);
2196                                        } else if (AttributeName.ACTION != attributeQName) {
2197                                            inputAttributes.addAttribute(
2198                                                    attributeQName,
2199                                                    attributes.getValue(i)
2200                                                    // [NOCPP[
2201                                                    , XmlViolationPolicy.ALLOW
2202                                            // ]NOCPP]
2203    
2204                                            );
2205                                        }
2206                                    }
2207                                    attributes.clearWithoutReleasingContents();
2208                                    appendVoidElementToCurrentMayFoster(
2209                                            "input",
2210                                            inputAttributes, formPointer);
2211                                    pop(); // label
2212                                    appendVoidElementToCurrentMayFoster(
2213                                            ElementName.HR,
2214                                            HtmlAttributes.EMPTY_ATTRIBUTES);
2215                                    pop(); // form
2216                                    selfClosing = false;
2217                                    // Portability.delete(formAttrs);
2218                                    // Portability.delete(inputAttributes);
2219                                    // Don't delete attributes, they are deleted
2220                                    // later
2221                                    break starttagloop;
2222                                case TEXTAREA:
2223                                    appendToCurrentNodeAndPushElementMayFoster(
2224                                            elementName,
2225                                            attributes, formPointer);
2226                                    tokenizer.setStateAndEndTagExpectation(
2227                                            Tokenizer.RCDATA, elementName);
2228                                    originalMode = mode;
2229                                    mode = TEXT;
2230                                    needToDropLF = true;
2231                                    attributes = null; // CPP
2232                                    break starttagloop;
2233                                case XMP:
2234                                    implicitlyCloseP();
2235                                    reconstructTheActiveFormattingElements();
2236                                    appendToCurrentNodeAndPushElementMayFoster(
2237                                            elementName,
2238                                            attributes);
2239                                    originalMode = mode;
2240                                    mode = TEXT;
2241                                    tokenizer.setStateAndEndTagExpectation(
2242                                            Tokenizer.RAWTEXT, elementName);
2243                                    attributes = null; // CPP
2244                                    break starttagloop;
2245                                case NOSCRIPT:
2246                                    if (!scriptingEnabled) {
2247                                        reconstructTheActiveFormattingElements();
2248                                        appendToCurrentNodeAndPushElementMayFoster(
2249                                                elementName,
2250                                                attributes);
2251                                        attributes = null; // CPP
2252                                        break starttagloop;
2253                                    } else {
2254                                        // fall through
2255                                    }
2256                                case NOFRAMES:
2257                                case IFRAME:
2258                                case NOEMBED:
2259                                    appendToCurrentNodeAndPushElementMayFoster(
2260                                            elementName,
2261                                            attributes);
2262                                    originalMode = mode;
2263                                    mode = TEXT;
2264                                    tokenizer.setStateAndEndTagExpectation(
2265                                            Tokenizer.RAWTEXT, elementName);
2266                                    attributes = null; // CPP
2267                                    break starttagloop;
2268                                case SELECT:
2269                                    reconstructTheActiveFormattingElements();
2270                                    appendToCurrentNodeAndPushElementMayFoster(
2271                                            elementName,
2272                                            attributes, formPointer);
2273                                    switch (mode) {
2274                                        case IN_TABLE:
2275                                        case IN_CAPTION:
2276                                        case IN_COLUMN_GROUP:
2277                                        case IN_TABLE_BODY:
2278                                        case IN_ROW:
2279                                        case IN_CELL:
2280                                            mode = IN_SELECT_IN_TABLE;
2281                                            break;
2282                                        default:
2283                                            mode = IN_SELECT;
2284                                            break;
2285                                    }
2286                                    attributes = null; // CPP
2287                                    break starttagloop;
2288                                case OPTGROUP:
2289                                case OPTION:
2290                                    if (isCurrent("option")) {
2291                                        pop();
2292                                    }
2293                                    reconstructTheActiveFormattingElements();
2294                                    appendToCurrentNodeAndPushElementMayFoster(
2295                                            elementName,
2296                                            attributes);
2297                                    attributes = null; // CPP
2298                                    break starttagloop;
2299                                case RT_OR_RP:
2300                                    /*
2301                                     * If the stack of open elements has a ruby
2302                                     * element in scope, then generate implied end
2303                                     * tags. If the current node is not then a ruby
2304                                     * element, this is a parse error; pop all the
2305                                     * nodes from the current node up to the node
2306                                     * immediately before the bottommost ruby
2307                                     * element on the stack of open elements.
2308                                     * 
2309                                     * Insert an HTML element for the token.
2310                                     */
2311                                    eltPos = findLastInScope("ruby");
2312                                    if (eltPos != NOT_FOUND_ON_STACK) {
2313                                        generateImpliedEndTags();
2314                                    }
2315                                    if (eltPos != currentPtr) {
2316                                        if (errorHandler != null) {
2317                                            if (eltPos != NOT_FOUND_ON_STACK) {
2318    
2319                                                errNoCheck("Start tag \u201C"
2320                                                        + name
2321                                                        + "\u201D seen without a \u201Cruby\u201D element being open.");
2322                                            } else {
2323                                                errNoCheck("Unclosed children in \u201Cruby\u201D.");
2324                                            }
2325                                        }
2326                                        while (currentPtr > eltPos) {
2327                                            pop();
2328                                        }
2329                                    }
2330                                    appendToCurrentNodeAndPushElementMayFoster(
2331                                            elementName,
2332                                            attributes);
2333                                    attributes = null; // CPP
2334                                    break starttagloop;
2335                                case MATH:
2336                                    reconstructTheActiveFormattingElements();
2337                                    attributes.adjustForMath();
2338                                    if (selfClosing) {
2339                                        appendVoidElementToCurrentMayFosterMathML(
2340                                                elementName, attributes);
2341                                        selfClosing = false;
2342                                    } else {
2343                                        appendToCurrentNodeAndPushElementMayFosterMathML(
2344                                                elementName, attributes);
2345                                    }
2346                                    attributes = null; // CPP
2347                                    break starttagloop;
2348                                case SVG:
2349                                    reconstructTheActiveFormattingElements();
2350                                    attributes.adjustForSvg();
2351                                    if (selfClosing) {
2352                                        appendVoidElementToCurrentMayFosterSVG(
2353                                                elementName,
2354                                                attributes);
2355                                        selfClosing = false;
2356                                    } else {
2357                                        appendToCurrentNodeAndPushElementMayFosterSVG(
2358                                                elementName, attributes);
2359                                    }
2360                                    attributes = null; // CPP
2361                                    break starttagloop;
2362                                case CAPTION:
2363                                case COL:
2364                                case COLGROUP:
2365                                case TBODY_OR_THEAD_OR_TFOOT:
2366                                case TR:
2367                                case TD_OR_TH:
2368                                case FRAME:
2369                                case FRAMESET:
2370                                case HEAD:
2371                                    errStrayStartTag(name);
2372                                    break starttagloop;
2373                                case OUTPUT_OR_LABEL:
2374                                    reconstructTheActiveFormattingElements();
2375                                    appendToCurrentNodeAndPushElementMayFoster(
2376                                            elementName,
2377                                            attributes, formPointer);
2378                                    attributes = null; // CPP
2379                                    break starttagloop;
2380                                default:
2381                                    reconstructTheActiveFormattingElements();
2382                                    appendToCurrentNodeAndPushElementMayFoster(
2383                                            elementName,
2384                                            attributes);
2385                                    attributes = null; // CPP
2386                                    break starttagloop;
2387                            }
2388                        }
2389                    case IN_HEAD:
2390                        inheadloop: for (;;) {
2391                            switch (group) {
2392                                case HTML:
2393                                    errStrayStartTag(name);
2394                                    if (!fragment) {
2395                                        addAttributesToHtml(attributes);
2396                                        attributes = null; // CPP
2397                                    }
2398                                    break starttagloop;
2399                                case BASE:
2400                                case COMMAND:
2401                                    appendVoidElementToCurrentMayFoster(
2402                                            elementName,
2403                                            attributes);
2404                                    selfClosing = false;
2405                                    attributes = null; // CPP
2406                                    break starttagloop;
2407                                case META:
2408                                case LINK_OR_BASEFONT_OR_BGSOUND:
2409                                    // Fall through to IN_HEAD_NOSCRIPT
2410                                    break inheadloop;
2411                                case TITLE:
2412                                    appendToCurrentNodeAndPushElementMayFoster(
2413                                            elementName,
2414                                            attributes);
2415                                    originalMode = mode;
2416                                    mode = TEXT;
2417                                    tokenizer.setStateAndEndTagExpectation(
2418                                            Tokenizer.RCDATA, elementName);
2419                                    attributes = null; // CPP
2420                                    break starttagloop;
2421                                case NOSCRIPT:
2422                                    if (scriptingEnabled) {
2423                                        appendToCurrentNodeAndPushElement(
2424                                                elementName,
2425                                                attributes);
2426                                        originalMode = mode;
2427                                        mode = TEXT;
2428                                        tokenizer.setStateAndEndTagExpectation(
2429                                                Tokenizer.RAWTEXT, elementName);
2430                                    } else {
2431                                        appendToCurrentNodeAndPushElementMayFoster(
2432                                                elementName,
2433                                                attributes);
2434                                        mode = IN_HEAD_NOSCRIPT;
2435                                    }
2436                                    attributes = null; // CPP
2437                                    break starttagloop;
2438                                case SCRIPT:
2439                                    // XXX need to manage much more stuff
2440                                    // here if
2441                                    // supporting
2442                                    // document.write()
2443                                    appendToCurrentNodeAndPushElementMayFoster(
2444                                            elementName,
2445                                            attributes);
2446                                    originalMode = mode;
2447                                    mode = TEXT;
2448                                    tokenizer.setStateAndEndTagExpectation(
2449                                            Tokenizer.SCRIPT_DATA, elementName);
2450                                    attributes = null; // CPP
2451                                    break starttagloop;
2452                                case STYLE:
2453                                case NOFRAMES:
2454                                    appendToCurrentNodeAndPushElementMayFoster(
2455                                            elementName,
2456                                            attributes);
2457                                    originalMode = mode;
2458                                    mode = TEXT;
2459                                    tokenizer.setStateAndEndTagExpectation(
2460                                            Tokenizer.RAWTEXT, elementName);
2461                                    attributes = null; // CPP
2462                                    break starttagloop;
2463                                case HEAD:
2464                                    /* Parse error. */
2465                                    err("Start tag for \u201Chead\u201D seen when \u201Chead\u201D was already open.");
2466                                    /* Ignore the token. */
2467                                    break starttagloop;
2468                                default:
2469                                    pop();
2470                                    mode = AFTER_HEAD;
2471                                    continue starttagloop;
2472                            }
2473                        }
2474                    case IN_HEAD_NOSCRIPT:
2475                        switch (group) {
2476                            case HTML:
2477                                // XXX did Hixie really mean to omit "base"
2478                                // here?
2479                                errStrayStartTag(name);
2480                                if (!fragment) {
2481                                    addAttributesToHtml(attributes);
2482                                    attributes = null; // CPP
2483                                }
2484                                break starttagloop;
2485                            case LINK_OR_BASEFONT_OR_BGSOUND:
2486                                appendVoidElementToCurrentMayFoster(
2487                                        elementName,
2488                                        attributes);
2489                                selfClosing = false;
2490                                attributes = null; // CPP
2491                                break starttagloop;
2492                            case META:
2493                                checkMetaCharset(attributes);
2494                                appendVoidElementToCurrentMayFoster(
2495                                        elementName,
2496                                        attributes);
2497                                selfClosing = false;
2498                                attributes = null; // CPP
2499                                break starttagloop;
2500                            case STYLE:
2501                            case NOFRAMES:
2502                                appendToCurrentNodeAndPushElement(
2503                                        elementName,
2504                                        attributes);
2505                                originalMode = mode;
2506                                mode = TEXT;
2507                                tokenizer.setStateAndEndTagExpectation(
2508                                        Tokenizer.RAWTEXT, elementName);
2509                                attributes = null; // CPP
2510                                break starttagloop;
2511                            case HEAD:
2512                                err("Start tag for \u201Chead\u201D seen when \u201Chead\u201D was already open.");
2513                                break starttagloop;
2514                            case NOSCRIPT:
2515                                err("Start tag for \u201Cnoscript\u201D seen when \u201Cnoscript\u201D was already open.");
2516                                break starttagloop;
2517                            default:
2518                                err("Bad start tag in \u201C" + name
2519                                        + "\u201D in \u201Chead\u201D.");
2520                                pop();
2521                                mode = IN_HEAD;
2522                                continue;
2523                        }
2524                    case IN_COLUMN_GROUP:
2525                        switch (group) {
2526                            case HTML:
2527                                errStrayStartTag(name);
2528                                if (!fragment) {
2529                                    addAttributesToHtml(attributes);
2530                                    attributes = null; // CPP
2531                                }
2532                                break starttagloop;
2533                            case COL:
2534                                appendVoidElementToCurrentMayFoster(
2535                                        elementName,
2536                                        attributes);
2537                                selfClosing = false;
2538                                attributes = null; // CPP
2539                                break starttagloop;
2540                            default:
2541                                if (currentPtr == 0) {
2542                                    assert fragment;
2543                                    err("Garbage in \u201Ccolgroup\u201D fragment.");
2544                                    break starttagloop;
2545                                }
2546                                pop();
2547                                mode = IN_TABLE;
2548                                continue;
2549                        }
2550                    case IN_SELECT_IN_TABLE:
2551                        switch (group) {
2552                            case CAPTION:
2553                            case TBODY_OR_THEAD_OR_TFOOT:
2554                            case TR:
2555                            case TD_OR_TH:
2556                            case TABLE:
2557                                err("\u201C"
2558                                        + name
2559                                        + "\u201D start tag with \u201Cselect\u201D open.");
2560                                eltPos = findLastInTableScope("select");
2561                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2562                                    assert fragment;
2563                                    break starttagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
2564                                }
2565                                while (currentPtr >= eltPos) {
2566                                    pop();
2567                                }
2568                                resetTheInsertionMode();
2569                                continue;
2570                            default:
2571                                // fall through to IN_SELECT
2572                        }
2573                    case IN_SELECT:
2574                        switch (group) {
2575                            case HTML:
2576                                errStrayStartTag(name);
2577                                if (!fragment) {
2578                                    addAttributesToHtml(attributes);
2579                                    attributes = null; // CPP
2580                                }
2581                                break starttagloop;
2582                            case OPTION:
2583                                if (isCurrent("option")) {
2584                                    pop();
2585                                }
2586                                appendToCurrentNodeAndPushElement(
2587                                        elementName,
2588                                        attributes);
2589                                attributes = null; // CPP
2590                                break starttagloop;
2591                            case OPTGROUP:
2592                                if (isCurrent("option")) {
2593                                    pop();
2594                                }
2595                                if (isCurrent("optgroup")) {
2596                                    pop();
2597                                }
2598                                appendToCurrentNodeAndPushElement(
2599                                        elementName,
2600                                        attributes);
2601                                attributes = null; // CPP
2602                                break starttagloop;
2603                            case SELECT:
2604                                err("\u201Cselect\u201D start tag where end tag expected.");
2605                                eltPos = findLastInTableScope(name);
2606                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2607                                    assert fragment;
2608                                    err("No \u201Cselect\u201D in table scope.");
2609                                    break starttagloop;
2610                                } else {
2611                                    while (currentPtr >= eltPos) {
2612                                        pop();
2613                                    }
2614                                    resetTheInsertionMode();
2615                                    break starttagloop;
2616                                }
2617                            case INPUT:
2618                            case TEXTAREA:
2619                            case KEYGEN:
2620                                err("\u201C"
2621                                        + name
2622                                        + "\u201D start tag seen in \u201Cselect\2201D.");
2623                                eltPos = findLastInTableScope("select");
2624                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2625                                    assert fragment;
2626                                    break starttagloop;
2627                                }
2628                                while (currentPtr >= eltPos) {
2629                                    pop();
2630                                }
2631                                resetTheInsertionMode();
2632                                continue;
2633                            case SCRIPT:
2634                                // XXX need to manage much more stuff
2635                                // here if
2636                                // supporting
2637                                // document.write()
2638                                appendToCurrentNodeAndPushElementMayFoster(
2639                                        elementName,
2640                                        attributes);
2641                                originalMode = mode;
2642                                mode = TEXT;
2643                                tokenizer.setStateAndEndTagExpectation(
2644                                        Tokenizer.SCRIPT_DATA, elementName);
2645                                attributes = null; // CPP
2646                                break starttagloop;
2647                            default:
2648                                errStrayStartTag(name);
2649                                break starttagloop;
2650                        }
2651                    case AFTER_BODY:
2652                        switch (group) {
2653                            case HTML:
2654                                errStrayStartTag(name);
2655                                if (!fragment) {
2656                                    addAttributesToHtml(attributes);
2657                                    attributes = null; // CPP
2658                                }
2659                                break starttagloop;
2660                            default:
2661                                errStrayStartTag(name);
2662                                mode = framesetOk ? FRAMESET_OK : IN_BODY;
2663                                continue;
2664                        }
2665                    case IN_FRAMESET:
2666                        switch (group) {
2667                            case FRAMESET:
2668                                appendToCurrentNodeAndPushElement(
2669                                        elementName,
2670                                        attributes);
2671                                attributes = null; // CPP
2672                                break starttagloop;
2673                            case FRAME:
2674                                appendVoidElementToCurrentMayFoster(
2675                                        elementName,
2676                                        attributes);
2677                                selfClosing = false;
2678                                attributes = null; // CPP
2679                                break starttagloop;
2680                            default:
2681                                // fall through to AFTER_FRAMESET
2682                        }
2683                    case AFTER_FRAMESET:
2684                        switch (group) {
2685                            case HTML:
2686                                errStrayStartTag(name);
2687                                if (!fragment) {
2688                                    addAttributesToHtml(attributes);
2689                                    attributes = null; // CPP
2690                                }
2691                                break starttagloop;
2692                            case NOFRAMES:
2693                                appendToCurrentNodeAndPushElement(
2694                                        elementName,
2695                                        attributes);
2696                                originalMode = mode;
2697                                mode = TEXT;
2698                                tokenizer.setStateAndEndTagExpectation(
2699                                        Tokenizer.RAWTEXT, elementName);
2700                                attributes = null; // CPP
2701                                break starttagloop;
2702                            default:
2703                                errStrayStartTag(name);
2704                                break starttagloop;
2705                        }
2706                    case INITIAL:
2707                        /*
2708                         * Parse error.
2709                         */
2710                        // [NOCPP[
2711                        switch (doctypeExpectation) {
2712                            case AUTO:
2713                                err("Start tag seen without seeing a doctype first. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
2714                                break;
2715                            case HTML:
2716                                err("Start tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
2717                                break;
2718                            case HTML401_STRICT:
2719                                err("Start tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
2720                                break;
2721                            case HTML401_TRANSITIONAL:
2722                                err("Start tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
2723                                break;
2724                            case NO_DOCTYPE_ERRORS:
2725                        }
2726                        // ]NOCPP]
2727                        /*
2728                         * 
2729                         * Set the document to quirks mode.
2730                         */
2731                        documentModeInternal(DocumentMode.QUIRKS_MODE, null, null,
2732                                false);
2733                        /*
2734                         * Then, switch to the root element mode of the tree
2735                         * construction stage
2736                         */
2737                        mode = BEFORE_HTML;
2738                        /*
2739                         * and reprocess the current token.
2740                         */
2741                        continue;
2742                    case BEFORE_HTML:
2743                        switch (group) {
2744                            case HTML:
2745                                // optimize error check and streaming SAX by
2746                                // hoisting
2747                                // "html" handling here.
2748                                if (attributes == HtmlAttributes.EMPTY_ATTRIBUTES) {
2749                                    // This has the right magic side effect
2750                                    // that
2751                                    // it
2752                                    // makes attributes in SAX Tree mutable.
2753                                    appendHtmlElementToDocumentAndPush();
2754                                } else {
2755                                    appendHtmlElementToDocumentAndPush(attributes);
2756                                }
2757                                // XXX application cache should fire here
2758                                mode = BEFORE_HEAD;
2759                                attributes = null; // CPP
2760                                break starttagloop;
2761                            default:
2762                                /*
2763                                 * Create an HTMLElement node with the tag name
2764                                 * html, in the HTML namespace. Append it to the
2765                                 * Document object.
2766                                 */
2767                                appendHtmlElementToDocumentAndPush();
2768                                /* Switch to the main mode */
2769                                mode = BEFORE_HEAD;
2770                                /*
2771                                 * reprocess the current token.
2772                                 */
2773                                continue;
2774                        }
2775                    case BEFORE_HEAD:
2776                        switch (group) {
2777                            case HTML:
2778                                errStrayStartTag(name);
2779                                if (!fragment) {
2780                                    addAttributesToHtml(attributes);
2781                                    attributes = null; // CPP
2782                                }
2783                                break starttagloop;
2784                            case HEAD:
2785                                /*
2786                                 * A start tag whose tag name is "head"
2787                                 * 
2788                                 * Create an element for the token.
2789                                 * 
2790                                 * Set the head element pointer to this new element
2791                                 * node.
2792                                 * 
2793                                 * Append the new element to the current node and
2794                                 * push it onto the stack of open elements.
2795                                 */
2796                                appendToCurrentNodeAndPushHeadElement(attributes);
2797                                /*
2798                                 * Change the insertion mode to "in head".
2799                                 */
2800                                mode = IN_HEAD;
2801                                attributes = null; // CPP
2802                                break starttagloop;
2803                            default:
2804                                /*
2805                                 * Any other start tag token
2806                                 * 
2807                                 * Act as if a start tag token with the tag name
2808                                 * "head" and no attributes had been seen,
2809                                 */
2810                                appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
2811                                mode = IN_HEAD;
2812                                /*
2813                                 * then reprocess the current token.
2814                                 * 
2815                                 * This will result in an empty head element being
2816                                 * generated, with the current token being
2817                                 * reprocessed in the "after head" insertion mode.
2818                                 */
2819                                continue;
2820                        }
2821                    case AFTER_HEAD:
2822                        switch (group) {
2823                            case HTML:
2824                                errStrayStartTag(name);
2825                                if (!fragment) {
2826                                    addAttributesToHtml(attributes);
2827                                    attributes = null; // CPP
2828                                }
2829                                break starttagloop;
2830                            case BODY:
2831                                if (attributes.getLength() == 0) {
2832                                    // This has the right magic side effect
2833                                    // that
2834                                    // it
2835                                    // makes attributes in SAX Tree mutable.
2836                                    appendToCurrentNodeAndPushBodyElement();
2837                                } else {
2838                                    appendToCurrentNodeAndPushBodyElement(attributes);
2839                                }
2840                                framesetOk = false;
2841                                mode = IN_BODY;
2842                                attributes = null; // CPP
2843                                break starttagloop;
2844                            case FRAMESET:
2845                                appendToCurrentNodeAndPushElement(
2846                                        elementName,
2847                                        attributes);
2848                                mode = IN_FRAMESET;
2849                                attributes = null; // CPP
2850                                break starttagloop;
2851                            case BASE:
2852                                err("\u201Cbase\u201D element outside \u201Chead\u201D.");
2853                                pushHeadPointerOntoStack();
2854                                appendVoidElementToCurrentMayFoster(
2855                                        elementName,
2856                                        attributes);
2857                                selfClosing = false;
2858                                pop(); // head
2859                                attributes = null; // CPP
2860                                break starttagloop;
2861                            case LINK_OR_BASEFONT_OR_BGSOUND:
2862                                err("\u201Clink\u201D element outside \u201Chead\u201D.");
2863                                pushHeadPointerOntoStack();
2864                                appendVoidElementToCurrentMayFoster(
2865                                        elementName,
2866                                        attributes);
2867                                selfClosing = false;
2868                                pop(); // head
2869                                attributes = null; // CPP
2870                                break starttagloop;
2871                            case META:
2872                                err("\u201Cmeta\u201D element outside \u201Chead\u201D.");
2873                                checkMetaCharset(attributes);
2874                                pushHeadPointerOntoStack();
2875                                appendVoidElementToCurrentMayFoster(
2876                                        elementName,
2877                                        attributes);
2878                                selfClosing = false;
2879                                pop(); // head
2880                                attributes = null; // CPP
2881                                break starttagloop;
2882                            case SCRIPT:
2883                                err("\u201Cscript\u201D element between \u201Chead\u201D and \u201Cbody\u201D.");
2884                                pushHeadPointerOntoStack();
2885                                appendToCurrentNodeAndPushElement(
2886                                        elementName,
2887                                        attributes);
2888                                originalMode = mode;
2889                                mode = TEXT;
2890                                tokenizer.setStateAndEndTagExpectation(
2891                                        Tokenizer.SCRIPT_DATA, elementName);
2892                                attributes = null; // CPP
2893                                break starttagloop;
2894                            case STYLE:
2895                            case NOFRAMES:
2896                                err("\u201C"
2897                                        + name
2898                                        + "\u201D element between \u201Chead\u201D and \u201Cbody\u201D.");
2899                                pushHeadPointerOntoStack();
2900                                appendToCurrentNodeAndPushElement(
2901                                        elementName,
2902                                        attributes);
2903                                originalMode = mode;
2904                                mode = TEXT;
2905                                tokenizer.setStateAndEndTagExpectation(
2906                                        Tokenizer.RAWTEXT, elementName);
2907                                attributes = null; // CPP
2908                                break starttagloop;
2909                            case TITLE:
2910                                err("\u201Ctitle\u201D element outside \u201Chead\u201D.");
2911                                pushHeadPointerOntoStack();
2912                                appendToCurrentNodeAndPushElement(
2913                                        elementName,
2914                                        attributes);
2915                                originalMode = mode;
2916                                mode = TEXT;
2917                                tokenizer.setStateAndEndTagExpectation(
2918                                        Tokenizer.RCDATA, elementName);
2919                                attributes = null; // CPP
2920                                break starttagloop;
2921                            case HEAD:
2922                                errStrayStartTag(name);
2923                                break starttagloop;
2924                            default:
2925                                appendToCurrentNodeAndPushBodyElement();
2926                                mode = FRAMESET_OK;
2927                                continue;
2928                        }
2929                    case AFTER_AFTER_BODY:
2930                        switch (group) {
2931                            case HTML:
2932                                errStrayStartTag(name);
2933                                if (!fragment) {
2934                                    addAttributesToHtml(attributes);
2935                                    attributes = null; // CPP
2936                                }
2937                                break starttagloop;
2938                            default:
2939                                errStrayStartTag(name);
2940                                fatal();
2941                                mode = framesetOk ? FRAMESET_OK : IN_BODY;
2942                                continue;
2943                        }
2944                    case AFTER_AFTER_FRAMESET:
2945                        switch (group) {
2946                            case HTML:
2947                                errStrayStartTag(name);
2948                                if (!fragment) {
2949                                    addAttributesToHtml(attributes);
2950                                    attributes = null; // CPP
2951                                }
2952                                break starttagloop;
2953                            case NOFRAMES:
2954                                appendToCurrentNodeAndPushElementMayFoster(
2955                                        elementName,
2956                                        attributes);
2957                                originalMode = mode;
2958                                mode = TEXT;
2959                                tokenizer.setStateAndEndTagExpectation(
2960                                        Tokenizer.SCRIPT_DATA, elementName);
2961                                attributes = null; // CPP
2962                                break starttagloop;
2963                            default:
2964                                errStrayStartTag(name);
2965                                break starttagloop;
2966                        }
2967                    case TEXT:
2968                        assert false;
2969                        break starttagloop; // Avoid infinite loop if the assertion
2970                                            // fails
2971                }
2972            }
2973            if (errorHandler != null && selfClosing) {
2974                errNoCheck("Self-closing syntax (\u201C/>\u201D) used on a non-void HTML element. Ignoring the slash and treating as a start tag.");
2975            }
2976            if (attributes != HtmlAttributes.EMPTY_ATTRIBUTES) {
2977                Portability.delete(attributes);
2978            }
2979        }
2980    
2981        private boolean isSpecialParentInForeign(StackNode<T> stackNode) {
2982            @NsUri String ns = stackNode.ns;
2983            return ("http://www.w3.org/1999/xhtml" == ns)
2984                    || (stackNode.isHtmlIntegrationPoint())
2985                    || (("http://www.w3.org/1998/Math/MathML" == ns) && (stackNode.getGroup() == MI_MO_MN_MS_MTEXT));
2986        }
2987    
2988        /**
2989         * 
2990         * <p>
2991         * C++ memory note: The return value must be released.
2992         * 
2993         * @return
2994         * @throws SAXException
2995         * @throws StopSniffingException
2996         */
2997        public static String extractCharsetFromContent(String attributeValue) {
2998            // This is a bit ugly. Converting the string to char array in order to
2999            // make the portability layer smaller.
3000            int charsetState = CHARSET_INITIAL;
3001            int start = -1;
3002            int end = -1;
3003            @Auto char[] buffer = Portability.newCharArrayFromString(attributeValue);
3004    
3005            charsetloop: for (int i = 0; i < buffer.length; i++) {
3006                char c = buffer[i];
3007                switch (charsetState) {
3008                    case CHARSET_INITIAL:
3009                        switch (c) {
3010                            case 'c':
3011                            case 'C':
3012                                charsetState = CHARSET_C;
3013                                continue;
3014                            default:
3015                                continue;
3016                        }
3017                    case CHARSET_C:
3018                        switch (c) {
3019                            case 'h':
3020                            case 'H':
3021                                charsetState = CHARSET_H;
3022                                continue;
3023                            default:
3024                                charsetState = CHARSET_INITIAL;
3025                                continue;
3026                        }
3027                    case CHARSET_H:
3028                        switch (c) {
3029                            case 'a':
3030                            case 'A':
3031                                charsetState = CHARSET_A;
3032                                continue;
3033                            default:
3034                                charsetState = CHARSET_INITIAL;
3035                                continue;
3036                        }
3037                    case CHARSET_A:
3038                        switch (c) {
3039                            case 'r':
3040                            case 'R':
3041                                charsetState = CHARSET_R;
3042                                continue;
3043                            default:
3044                                charsetState = CHARSET_INITIAL;
3045                                continue;
3046                        }
3047                    case CHARSET_R:
3048                        switch (c) {
3049                            case 's':
3050                            case 'S':
3051                                charsetState = CHARSET_S;
3052                                continue;
3053                            default:
3054                                charsetState = CHARSET_INITIAL;
3055                                continue;
3056                        }
3057                    case CHARSET_S:
3058                        switch (c) {
3059                            case 'e':
3060                            case 'E':
3061                                charsetState = CHARSET_E;
3062                                continue;
3063                            default:
3064                                charsetState = CHARSET_INITIAL;
3065                                continue;
3066                        }
3067                    case CHARSET_E:
3068                        switch (c) {
3069                            case 't':
3070                            case 'T':
3071                                charsetState = CHARSET_T;
3072                                continue;
3073                            default:
3074                                charsetState = CHARSET_INITIAL;
3075                                continue;
3076                        }
3077                    case CHARSET_T:
3078                        switch (c) {
3079                            case '\t':
3080                            case '\n':
3081                            case '\u000C':
3082                            case '\r':
3083                            case ' ':
3084                                continue;
3085                            case '=':
3086                                charsetState = CHARSET_EQUALS;
3087                                continue;
3088                            default:
3089                                return null;
3090                        }
3091                    case CHARSET_EQUALS:
3092                        switch (c) {
3093                            case '\t':
3094                            case '\n':
3095                            case '\u000C':
3096                            case '\r':
3097                            case ' ':
3098                                continue;
3099                            case '\'':
3100                                start = i + 1;
3101                                charsetState = CHARSET_SINGLE_QUOTED;
3102                                continue;
3103                            case '\"':
3104                                start = i + 1;
3105                                charsetState = CHARSET_DOUBLE_QUOTED;
3106                                continue;
3107                            default:
3108                                start = i;
3109                                charsetState = CHARSET_UNQUOTED;
3110                                continue;
3111                        }
3112                    case CHARSET_SINGLE_QUOTED:
3113                        switch (c) {
3114                            case '\'':
3115                                end = i;
3116                                break charsetloop;
3117                            default:
3118                                continue;
3119                        }
3120                    case CHARSET_DOUBLE_QUOTED:
3121                        switch (c) {
3122                            case '\"':
3123                                end = i;
3124                                break charsetloop;
3125                            default:
3126                                continue;
3127                        }
3128                    case CHARSET_UNQUOTED:
3129                        switch (c) {
3130                            case '\t':
3131                            case '\n':
3132                            case '\u000C':
3133                            case '\r':
3134                            case ' ':
3135                            case ';':
3136                                end = i;
3137                                break charsetloop;
3138                            default:
3139                                continue;
3140                        }
3141                }
3142            }
3143            String charset = null;
3144            if (start != -1) {
3145                if (end == -1) {
3146                    end = buffer.length;
3147                }
3148                charset = Portability.newStringFromBuffer(buffer, start, end
3149                        - start);
3150            }
3151            return charset;
3152        }
3153    
3154        private void checkMetaCharset(HtmlAttributes attributes)
3155                throws SAXException {
3156            String charset = attributes.getValue(AttributeName.CHARSET);
3157            if (charset != null) {
3158                if (tokenizer.internalEncodingDeclaration(charset)) {
3159                    requestSuspension();
3160                    return;
3161                }            
3162                return;
3163            }
3164            if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
3165                    "content-type",
3166                    attributes.getValue(AttributeName.HTTP_EQUIV))) {
3167                return;
3168            }
3169            String content = attributes.getValue(AttributeName.CONTENT);
3170            if (content != null) {
3171                String extract = TreeBuilder.extractCharsetFromContent(content);
3172                // remember not to return early without releasing the string
3173                if (extract != null) {
3174                    if (tokenizer.internalEncodingDeclaration(extract)) {
3175                        requestSuspension();
3176                    }                
3177                }
3178                Portability.releaseString(extract);
3179            }
3180        }
3181    
3182        public final void endTag(ElementName elementName) throws SAXException {
3183            flushCharacters();
3184            needToDropLF = false;
3185            int eltPos;
3186            int group = elementName.getGroup();
3187            @Local String name = elementName.name;
3188            endtagloop: for (;;) {
3189                if (isInForeign()) {
3190                    if (errorHandler != null && stack[currentPtr].name != name) {
3191                        errNoCheck("End tag \u201C"
3192                                + name
3193                                + "\u201D did not match the name of the current open element (\u201C"
3194                                + stack[currentPtr].popName + "\u201D).");
3195                    }
3196                    eltPos = currentPtr;
3197                    for (;;) {
3198                        if (stack[eltPos].name == name) {
3199                            while (currentPtr >= eltPos) {
3200                                pop();
3201                            }
3202                            break endtagloop;
3203                        }
3204                        if (stack[--eltPos].ns == "http://www.w3.org/1999/xhtml") {
3205                            break;
3206                        }
3207                    }
3208                }
3209                switch (mode) {
3210                    case IN_ROW:
3211                        switch (group) {
3212                            case TR:
3213                                eltPos = findLastOrRoot(TreeBuilder.TR);
3214                                if (eltPos == 0) {
3215                                    assert fragment;
3216                                    err("No table row to close.");
3217                                    break endtagloop;
3218                                }
3219                                clearStackBackTo(eltPos);
3220                                pop();
3221                                mode = IN_TABLE_BODY;
3222                                break endtagloop;
3223                            case TABLE:
3224                                eltPos = findLastOrRoot(TreeBuilder.TR);
3225                                if (eltPos == 0) {
3226                                    assert fragment;
3227                                    err("No table row to close.");
3228                                    break endtagloop;
3229                                }
3230                                clearStackBackTo(eltPos);
3231                                pop();
3232                                mode = IN_TABLE_BODY;
3233                                continue;
3234                            case TBODY_OR_THEAD_OR_TFOOT:
3235                                if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
3236                                    errStrayEndTag(name);
3237                                    break endtagloop;
3238                                }
3239                                eltPos = findLastOrRoot(TreeBuilder.TR);
3240                                if (eltPos == 0) {
3241                                    assert fragment;
3242                                    err("No table row to close.");
3243                                    break endtagloop;
3244                                }
3245                                clearStackBackTo(eltPos);
3246                                pop();
3247                                mode = IN_TABLE_BODY;
3248                                continue;
3249                            case BODY:
3250                            case CAPTION:
3251                            case COL:
3252                            case COLGROUP:
3253                            case HTML:
3254                            case TD_OR_TH:
3255                                errStrayEndTag(name);
3256                                break endtagloop;
3257                            default:
3258                                // fall through to IN_TABLE
3259                        }
3260                    case IN_TABLE_BODY:
3261                        switch (group) {
3262                            case TBODY_OR_THEAD_OR_TFOOT:
3263                                eltPos = findLastOrRoot(name);
3264                                if (eltPos == 0) {
3265                                    errStrayEndTag(name);
3266                                    break endtagloop;
3267                                }
3268                                clearStackBackTo(eltPos);
3269                                pop();
3270                                mode = IN_TABLE;
3271                                break endtagloop;
3272                            case TABLE:
3273                                eltPos = findLastInTableScopeOrRootTbodyTheadTfoot();
3274                                if (eltPos == 0) {
3275                                    assert fragment;
3276                                    errStrayEndTag(name);
3277                                    break endtagloop;
3278                                }
3279                                clearStackBackTo(eltPos);
3280                                pop();
3281                                mode = IN_TABLE;
3282                                continue;
3283                            case BODY:
3284                            case CAPTION:
3285                            case COL:
3286                            case COLGROUP:
3287                            case HTML:
3288                            case TD_OR_TH:
3289                            case TR:
3290                                errStrayEndTag(name);
3291                                break endtagloop;
3292                            default:
3293                                // fall through to IN_TABLE
3294                        }
3295                    case IN_TABLE:
3296                        switch (group) {
3297                            case TABLE:
3298                                eltPos = findLast("table");
3299                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3300                                    assert fragment;
3301                                    errStrayEndTag(name);
3302                                    break endtagloop;
3303                                }
3304                                while (currentPtr >= eltPos) {
3305                                    pop();
3306                                }
3307                                resetTheInsertionMode();
3308                                break endtagloop;
3309                            case BODY:
3310                            case CAPTION:
3311                            case COL:
3312                            case COLGROUP:
3313                            case HTML:
3314                            case TBODY_OR_THEAD_OR_TFOOT:
3315                            case TD_OR_TH:
3316                            case TR:
3317                                errStrayEndTag(name);
3318                                break endtagloop;
3319                            default:
3320                                errStrayEndTag(name);
3321                                // fall through to IN_BODY
3322                        }
3323                    case IN_CAPTION:
3324                        switch (group) {
3325                            case CAPTION:
3326                                eltPos = findLastInTableScope("caption");
3327                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3328                                    break endtagloop;
3329                                }
3330                                generateImpliedEndTags();
3331                                if (errorHandler != null && currentPtr != eltPos) {
3332                                    errUnclosedElements(eltPos, name);
3333                                }
3334                                while (currentPtr >= eltPos) {
3335                                    pop();
3336                                }
3337                                clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3338                                mode = IN_TABLE;
3339                                break endtagloop;
3340                            case TABLE:
3341                                err("\u201Ctable\u201D closed but \u201Ccaption\u201D was still open.");
3342                                eltPos = findLastInTableScope("caption");
3343                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3344                                    break endtagloop;
3345                                }
3346                                generateImpliedEndTags();
3347                                if (errorHandler != null && currentPtr != eltPos) {
3348                                    errUnclosedElements(eltPos, name);
3349                                }
3350                                while (currentPtr >= eltPos) {
3351                                    pop();
3352                                }
3353                                clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3354                                mode = IN_TABLE;
3355                                continue;
3356                            case BODY:
3357                            case COL:
3358                            case COLGROUP:
3359                            case HTML:
3360                            case TBODY_OR_THEAD_OR_TFOOT:
3361                            case TD_OR_TH:
3362                            case TR:
3363                                errStrayEndTag(name);
3364                                break endtagloop;
3365                            default:
3366                                // fall through to IN_BODY
3367                        }
3368                    case IN_CELL:
3369                        switch (group) {
3370                            case TD_OR_TH:
3371                                eltPos = findLastInTableScope(name);
3372                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3373                                    errStrayEndTag(name);
3374                                    break endtagloop;
3375                                }
3376                                generateImpliedEndTags();
3377                                if (errorHandler != null && !isCurrent(name)) {
3378                                    errUnclosedElements(eltPos, name);
3379                                }
3380                                while (currentPtr >= eltPos) {
3381                                    pop();
3382                                }
3383                                clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3384                                mode = IN_ROW;
3385                                break endtagloop;
3386                            case TABLE:
3387                            case TBODY_OR_THEAD_OR_TFOOT:
3388                            case TR:
3389                                if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
3390                                    errStrayEndTag(name);
3391                                    break endtagloop;
3392                                }
3393                                closeTheCell(findLastInTableScopeTdTh());
3394                                continue;
3395                            case BODY:
3396                            case CAPTION:
3397                            case COL:
3398                            case COLGROUP:
3399                            case HTML:
3400                                errStrayEndTag(name);
3401                                break endtagloop;
3402                            default:
3403                                // fall through to IN_BODY
3404                        }
3405                    case FRAMESET_OK:
3406                    case IN_BODY:
3407                        switch (group) {
3408                            case BODY:
3409                                if (!isSecondOnStackBody()) {
3410                                    assert fragment;
3411                                    errStrayEndTag(name);
3412                                    break endtagloop;
3413                                }
3414                                assert currentPtr >= 1;
3415                                if (errorHandler != null) {
3416                                    uncloseloop1: for (int i = 2; i <= currentPtr; i++) {
3417                                        switch (stack[i].getGroup()) {
3418                                            case DD_OR_DT:
3419                                            case LI:
3420                                            case OPTGROUP:
3421                                            case OPTION: // is this possible?
3422                                            case P:
3423                                            case RT_OR_RP:
3424                                            case TD_OR_TH:
3425                                            case TBODY_OR_THEAD_OR_TFOOT:
3426                                                break;
3427                                            default:
3428                                                errEndWithUnclosedElements("End tag for \u201Cbody\u201D seen but there were unclosed elements.");
3429                                                break uncloseloop1;
3430                                        }
3431                                    }
3432                                }
3433                                mode = AFTER_BODY;
3434                                break endtagloop;
3435                            case HTML:
3436                                if (!isSecondOnStackBody()) {
3437                                    assert fragment;
3438                                    errStrayEndTag(name);
3439                                    break endtagloop;
3440                                }
3441                                if (errorHandler != null) {
3442                                    uncloseloop2: for (int i = 0; i <= currentPtr; i++) {
3443                                        switch (stack[i].getGroup()) {
3444                                            case DD_OR_DT:
3445                                            case LI:
3446                                            case P:
3447                                            case TBODY_OR_THEAD_OR_TFOOT:
3448                                            case TD_OR_TH:
3449                                            case BODY:
3450                                            case HTML:
3451                                                break;
3452                                            default:
3453                                                errEndWithUnclosedElements("End tag for \u201Chtml\u201D seen but there were unclosed elements.");
3454                                                break uncloseloop2;
3455                                        }
3456                                    }
3457                                }
3458                                mode = AFTER_BODY;
3459                                continue;
3460                            case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
3461                            case UL_OR_OL_OR_DL:
3462                            case PRE_OR_LISTING:
3463                            case FIELDSET:
3464                            case BUTTON:
3465                            case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_NAV_OR_SECTION_OR_SUMMARY:
3466                                eltPos = findLastInScope(name);
3467                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3468                                    errStrayEndTag(name);
3469                                } else {
3470                                    generateImpliedEndTags();
3471                                    if (errorHandler != null && !isCurrent(name)) {
3472                                        errUnclosedElements(eltPos, name);
3473                                    }
3474                                    while (currentPtr >= eltPos) {
3475                                        pop();
3476                                    }
3477                                }
3478                                break endtagloop;
3479                            case FORM:
3480                                if (formPointer == null) {
3481                                    errStrayEndTag(name);
3482                                    break endtagloop;
3483                                }
3484                                formPointer = null;
3485                                eltPos = findLastInScope(name);
3486                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3487                                    errStrayEndTag(name);
3488                                    break endtagloop;
3489                                }
3490                                generateImpliedEndTags();
3491                                if (errorHandler != null && !isCurrent(name)) {
3492                                    errUnclosedElements(eltPos, name);
3493                                }
3494                                removeFromStack(eltPos);
3495                                break endtagloop;
3496                            case P:
3497                                eltPos = findLastInButtonScope("p");
3498                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3499                                    err("No \u201Cp\u201D element in scope but a \u201Cp\u201D end tag seen.");
3500                                    // XXX Can the 'in foreign' case happen anymore?
3501                                    if (isInForeign()) {
3502                                        err("HTML start tag \u201C"
3503                                                + name
3504                                                + "\u201D in a foreign namespace context.");
3505                                        while (stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
3506                                            pop();
3507                                        }
3508                                    }
3509                                    appendVoidElementToCurrentMayFoster(
3510                                            elementName,
3511                                            HtmlAttributes.EMPTY_ATTRIBUTES);
3512                                    break endtagloop;
3513                                }
3514                                generateImpliedEndTagsExceptFor("p");
3515                                assert eltPos != TreeBuilder.NOT_FOUND_ON_STACK;
3516                                if (errorHandler != null && eltPos != currentPtr) {
3517                                    errUnclosedElements(eltPos, name);
3518                                }
3519                                while (currentPtr >= eltPos) {
3520                                    pop();
3521                                }
3522                                break endtagloop;
3523                            case LI:
3524                                eltPos = findLastInListScope(name);
3525                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3526                                    err("No \u201Cli\u201D element in list scope but a \u201Cli\u201D end tag seen.");
3527                                } else {
3528                                    generateImpliedEndTagsExceptFor(name);
3529                                    if (errorHandler != null
3530                                            && eltPos != currentPtr) {
3531                                        errUnclosedElements(eltPos, name);
3532                                    }
3533                                    while (currentPtr >= eltPos) {
3534                                        pop();
3535                                    }
3536                                }
3537                                break endtagloop;
3538                            case DD_OR_DT:
3539                                eltPos = findLastInScope(name);
3540                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3541                                    err("No \u201C"
3542                                            + name
3543                                            + "\u201D element in scope but a \u201C"
3544                                            + name + "\u201D end tag seen.");
3545                                } else {
3546                                    generateImpliedEndTagsExceptFor(name);
3547                                    if (errorHandler != null
3548                                            && eltPos != currentPtr) {
3549                                        errUnclosedElements(eltPos, name);
3550                                    }
3551                                    while (currentPtr >= eltPos) {
3552                                        pop();
3553                                    }
3554                                }
3555                                break endtagloop;
3556                            case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
3557                                eltPos = findLastInScopeHn();
3558                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3559                                    errStrayEndTag(name);
3560                                } else {
3561                                    generateImpliedEndTags();
3562                                    if (errorHandler != null && !isCurrent(name)) {
3563                                        errUnclosedElements(eltPos, name);
3564                                    }
3565                                    while (currentPtr >= eltPos) {
3566                                        pop();
3567                                    }
3568                                }
3569                                break endtagloop;
3570                            case OBJECT:
3571                            case MARQUEE_OR_APPLET:
3572                                eltPos = findLastInScope(name);
3573                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3574                                    errStrayEndTag(name);
3575                                } else {
3576                                    generateImpliedEndTags();
3577                                    if (errorHandler != null && !isCurrent(name)) {
3578                                        errUnclosedElements(eltPos, name);
3579                                    }
3580                                    while (currentPtr >= eltPos) {
3581                                        pop();
3582                                    }
3583                                    clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3584                                }
3585                                break endtagloop;
3586                            case BR:
3587                                err("End tag \u201Cbr\u201D.");
3588                                if (isInForeign()) {
3589                                    err("HTML start tag \u201C"
3590                                            + name
3591                                            + "\u201D in a foreign namespace context.");
3592                                    while (stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
3593                                        pop();
3594                                    }
3595                                }
3596                                reconstructTheActiveFormattingElements();
3597                                appendVoidElementToCurrentMayFoster(
3598                                        elementName,
3599                                        HtmlAttributes.EMPTY_ATTRIBUTES);
3600                                break endtagloop;
3601                            case AREA_OR_WBR:
3602                            case PARAM_OR_SOURCE_OR_TRACK:
3603                            case EMBED_OR_IMG:
3604                            case IMAGE:
3605                            case INPUT:
3606                            case KEYGEN: // XXX??
3607                            case HR:
3608                            case ISINDEX:
3609                            case IFRAME:
3610                            case NOEMBED: // XXX???
3611                            case NOFRAMES: // XXX??
3612                            case SELECT:
3613                            case TABLE:
3614                            case TEXTAREA: // XXX??
3615                                errStrayEndTag(name);
3616                                break endtagloop;
3617                            case NOSCRIPT:
3618                                if (scriptingEnabled) {
3619                                    errStrayEndTag(name);
3620                                    break endtagloop;
3621                                } else {
3622                                    // fall through
3623                                }
3624                            case A:
3625                            case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
3626                            case FONT:
3627                            case NOBR:
3628                                if (adoptionAgencyEndTag(name)) {
3629                                    break endtagloop;
3630                                }
3631                                // else handle like any other tag
3632                            default:
3633                                if (isCurrent(name)) {
3634                                    pop();
3635                                    break endtagloop;
3636                                }
3637    
3638                                eltPos = currentPtr;
3639                                for (;;) {
3640                                    StackNode<T> node = stack[eltPos];
3641                                    if (node.name == name) {
3642                                        generateImpliedEndTags();
3643                                        if (errorHandler != null
3644                                                && !isCurrent(name)) {
3645                                            errUnclosedElements(eltPos, name);
3646                                        }
3647                                        while (currentPtr >= eltPos) {
3648                                            pop();
3649                                        }
3650                                        break endtagloop;
3651                                    } else if (node.isSpecial()) {
3652                                        errStrayEndTag(name);
3653                                        break endtagloop;
3654                                    }
3655                                    eltPos--;
3656                                }
3657                        }
3658                    case IN_COLUMN_GROUP:
3659                        switch (group) {
3660                            case COLGROUP:
3661                                if (currentPtr == 0) {
3662                                    assert fragment;
3663                                    err("Garbage in \u201Ccolgroup\u201D fragment.");
3664                                    break endtagloop;
3665                                }
3666                                pop();
3667                                mode = IN_TABLE;
3668                                break endtagloop;
3669                            case COL:
3670                                errStrayEndTag(name);
3671                                break endtagloop;
3672                            default:
3673                                if (currentPtr == 0) {
3674                                    assert fragment;
3675                                    err("Garbage in \u201Ccolgroup\u201D fragment.");
3676                                    break endtagloop;
3677                                }
3678                                pop();
3679                                mode = IN_TABLE;
3680                                continue;
3681                        }
3682                    case IN_SELECT_IN_TABLE:
3683                        switch (group) {
3684                            case CAPTION:
3685                            case TABLE:
3686                            case TBODY_OR_THEAD_OR_TFOOT:
3687                            case TR:
3688                            case TD_OR_TH:
3689                                err("\u201C"
3690                                        + name
3691                                        + "\u201D end tag with \u201Cselect\u201D open.");
3692                                if (findLastInTableScope(name) != TreeBuilder.NOT_FOUND_ON_STACK) {
3693                                    eltPos = findLastInTableScope("select");
3694                                    if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3695                                        assert fragment;
3696                                        break endtagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
3697                                    }
3698                                    while (currentPtr >= eltPos) {
3699                                        pop();
3700                                    }
3701                                    resetTheInsertionMode();
3702                                    continue;
3703                                } else {
3704                                    break endtagloop;
3705                                }
3706                            default:
3707                                // fall through to IN_SELECT
3708                        }
3709                    case IN_SELECT:
3710                        switch (group) {
3711                            case OPTION:
3712                                if (isCurrent("option")) {
3713                                    pop();
3714                                    break endtagloop;
3715                                } else {
3716                                    errStrayEndTag(name);
3717                                    break endtagloop;
3718                                }
3719                            case OPTGROUP:
3720                                if (isCurrent("option")
3721                                        && "optgroup" == stack[currentPtr - 1].name) {
3722                                    pop();
3723                                }
3724                                if (isCurrent("optgroup")) {
3725                                    pop();
3726                                } else {
3727                                    errStrayEndTag(name);
3728                                }
3729                                break endtagloop;
3730                            case SELECT:
3731                                eltPos = findLastInTableScope("select");
3732                                if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3733                                    assert fragment;
3734                                    errStrayEndTag(name);
3735                                    break endtagloop;
3736                                }
3737                                while (currentPtr >= eltPos) {
3738                                    pop();
3739                                }
3740                                resetTheInsertionMode();
3741                                break endtagloop;
3742                            default:
3743                                errStrayEndTag(name);
3744                                break endtagloop;
3745                        }
3746                    case AFTER_BODY:
3747                        switch (group) {
3748                            case HTML:
3749                                if (fragment) {
3750                                    errStrayEndTag(name);
3751                                    break endtagloop;
3752                                } else {
3753                                    mode = AFTER_AFTER_BODY;
3754                                    break endtagloop;
3755                                }
3756                            default:
3757                                err("Saw an end tag after \u201Cbody\u201D had been closed.");
3758                                mode = framesetOk ? FRAMESET_OK : IN_BODY;
3759                                continue;
3760                        }
3761                    case IN_FRAMESET:
3762                        switch (group) {
3763                            case FRAMESET:
3764                                if (currentPtr == 0) {
3765                                    assert fragment;
3766                                    errStrayEndTag(name);
3767                                    break endtagloop;
3768                                }
3769                                pop();
3770                                if ((!fragment) && !isCurrent("frameset")) {
3771                                    mode = AFTER_FRAMESET;
3772                                }
3773                                break endtagloop;
3774                            default:
3775                                errStrayEndTag(name);
3776                                break endtagloop;
3777                        }
3778                    case AFTER_FRAMESET:
3779                        switch (group) {
3780                            case HTML:
3781                                mode = AFTER_AFTER_FRAMESET;
3782                                break endtagloop;
3783                            default:
3784                                errStrayEndTag(name);
3785                                break endtagloop;
3786                        }
3787                    case INITIAL:
3788                        /*
3789                         * Parse error.
3790                         */
3791                        // [NOCPP[
3792                        switch (doctypeExpectation) {
3793                            case AUTO:
3794                                err("End tag seen without seeing a doctype first. Expected e.g. \u201C<!DOCTYPE html>\u201D.");
3795                                break;
3796                            case HTML:
3797                                err("End tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
3798                                break;
3799                            case HTML401_STRICT:
3800                                err("End tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\u201D.");
3801                                break;
3802                            case HTML401_TRANSITIONAL:
3803                                err("End tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\u201D.");
3804                                break;
3805                            case NO_DOCTYPE_ERRORS:
3806                        }
3807                        // ]NOCPP]
3808                        /*
3809                         * 
3810                         * Set the document to quirks mode.
3811                         */
3812                        documentModeInternal(DocumentMode.QUIRKS_MODE, null, null,
3813                                false);
3814                        /*
3815                         * Then, switch to the root element mode of the tree
3816                         * construction stage
3817                         */
3818                        mode = BEFORE_HTML;
3819                        /*
3820                         * and reprocess the current token.
3821                         */
3822                        continue;
3823                    case BEFORE_HTML:
3824                        switch (group) {
3825                            case HEAD:
3826                            case BR:
3827                            case HTML:
3828                            case BODY:
3829                                /*
3830                                 * Create an HTMLElement node with the tag name
3831                                 * html, in the HTML namespace. Append it to the
3832                                 * Document object.
3833                                 */
3834                                appendHtmlElementToDocumentAndPush();
3835                                /* Switch to the main mode */
3836                                mode = BEFORE_HEAD;
3837                                /*
3838                                 * reprocess the current token.
3839                                 */
3840                                continue;
3841                            default:
3842                                errStrayEndTag(name);
3843                                break endtagloop;
3844                        }
3845                    case BEFORE_HEAD:
3846                        switch (group) {
3847                            case HEAD:
3848                            case BR:
3849                            case HTML:
3850                            case BODY:
3851                                appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
3852                                mode = IN_HEAD;
3853                                continue;
3854                            default:
3855                                errStrayEndTag(name);
3856                                break endtagloop;
3857                        }
3858                    case IN_HEAD:
3859                        switch (group) {
3860                            case HEAD:
3861                                pop();
3862                                mode = AFTER_HEAD;
3863                                break endtagloop;
3864                            case BR:
3865                            case HTML:
3866                            case BODY:
3867                                pop();
3868                                mode = AFTER_HEAD;
3869                                continue;
3870                            default:
3871                                errStrayEndTag(name);
3872                                break endtagloop;
3873                        }
3874                    case IN_HEAD_NOSCRIPT:
3875                        switch (group) {
3876                            case NOSCRIPT:
3877                                pop();
3878                                mode = IN_HEAD;
3879                                break endtagloop;
3880                            case BR:
3881                                errStrayEndTag(name);
3882                                pop();
3883                                mode = IN_HEAD;
3884                                continue;
3885                            default:
3886                                errStrayEndTag(name);
3887                                break endtagloop;
3888                        }
3889                    case AFTER_HEAD:
3890                        switch (group) {
3891                            case HTML:
3892                            case BODY:
3893                            case BR:
3894                                appendToCurrentNodeAndPushBodyElement();
3895                                mode = FRAMESET_OK;
3896                                continue;
3897                            default:
3898                                errStrayEndTag(name);
3899                                break endtagloop;
3900                        }
3901                    case AFTER_AFTER_BODY:
3902                        errStrayEndTag(name);
3903                        mode = framesetOk ? FRAMESET_OK : IN_BODY;
3904                        continue;
3905                    case AFTER_AFTER_FRAMESET:
3906                        errStrayEndTag(name);
3907                        mode = IN_FRAMESET;
3908                        continue;
3909                    case TEXT:
3910                        // XXX need to manage insertion point here
3911                        pop();
3912                        if (originalMode == AFTER_HEAD) {
3913                            silentPop();
3914                        }
3915                        mode = originalMode;
3916                        break endtagloop;
3917                }
3918            } // endtagloop
3919        }
3920    
3921        private int findLastInTableScopeOrRootTbodyTheadTfoot() {
3922            for (int i = currentPtr; i > 0; i--) {
3923                if (stack[i].getGroup() == TreeBuilder.TBODY_OR_THEAD_OR_TFOOT) {
3924                    return i;
3925                }
3926            }
3927            return 0;
3928        }
3929    
3930        private int findLast(@Local String name) {
3931            for (int i = currentPtr; i > 0; i--) {
3932                if (stack[i].name == name) {
3933                    return i;
3934                }
3935            }
3936            return TreeBuilder.NOT_FOUND_ON_STACK;
3937        }
3938    
3939        private int findLastInTableScope(@Local String name) {
3940            for (int i = currentPtr; i > 0; i--) {
3941                if (stack[i].name == name) {
3942                    return i;
3943                } else if (stack[i].name == "table") {
3944                    return TreeBuilder.NOT_FOUND_ON_STACK;
3945                }
3946            }
3947            return TreeBuilder.NOT_FOUND_ON_STACK;
3948        }
3949    
3950        private int findLastInButtonScope(@Local String name) {
3951            for (int i = currentPtr; i > 0; i--) {
3952                if (stack[i].name == name) {
3953                    return i;
3954                } else if (stack[i].isScoping() || stack[i].name == "button") {
3955                    return TreeBuilder.NOT_FOUND_ON_STACK;
3956                }
3957            }
3958            return TreeBuilder.NOT_FOUND_ON_STACK;
3959        }
3960    
3961        private int findLastInScope(@Local String name) {
3962            for (int i = currentPtr; i > 0; i--) {
3963                if (stack[i].name == name) {
3964                    return i;
3965                } else if (stack[i].isScoping()) {
3966                    return TreeBuilder.NOT_FOUND_ON_STACK;
3967                }
3968            }
3969            return TreeBuilder.NOT_FOUND_ON_STACK;
3970        }
3971    
3972        private int findLastInListScope(@Local String name) {
3973            for (int i = currentPtr; i > 0; i--) {
3974                if (stack[i].name == name) {
3975                    return i;
3976                } else if (stack[i].isScoping() || stack[i].name == "ul" || stack[i].name == "ol") {
3977                    return TreeBuilder.NOT_FOUND_ON_STACK;
3978                }
3979            }
3980            return TreeBuilder.NOT_FOUND_ON_STACK;
3981        }
3982        
3983        private int findLastInScopeHn() {
3984            for (int i = currentPtr; i > 0; i--) {
3985                if (stack[i].getGroup() == TreeBuilder.H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) {
3986                    return i;
3987                } else if (stack[i].isScoping()) {
3988                    return TreeBuilder.NOT_FOUND_ON_STACK;
3989                }
3990            }
3991            return TreeBuilder.NOT_FOUND_ON_STACK;
3992        }
3993    
3994        private void generateImpliedEndTagsExceptFor(@Local String name)
3995                throws SAXException {
3996            for (;;) {
3997                StackNode<T> node = stack[currentPtr];
3998                switch (node.getGroup()) {
3999                    case P:
4000                    case LI:
4001                    case DD_OR_DT:
4002                    case OPTION:
4003                    case OPTGROUP:
4004                    case RT_OR_RP:
4005                        if (node.name == name) {
4006                            return;
4007                        }
4008                        pop();
4009                        continue;
4010                    default:
4011                        return;
4012                }
4013            }
4014        }
4015    
4016        private void generateImpliedEndTags() throws SAXException {
4017            for (;;) {
4018                switch (stack[currentPtr].getGroup()) {
4019                    case P:
4020                    case LI:
4021                    case DD_OR_DT:
4022                    case OPTION:
4023                    case OPTGROUP:
4024                    case RT_OR_RP:
4025                        pop();
4026                        continue;
4027                    default:
4028                        return;
4029                }
4030            }
4031        }
4032    
4033        private boolean isSecondOnStackBody() {
4034            return currentPtr >= 1 && stack[1].getGroup() == TreeBuilder.BODY;
4035        }
4036    
4037        private void documentModeInternal(DocumentMode m, String publicIdentifier,
4038                String systemIdentifier, boolean html4SpecificAdditionalErrorChecks)
4039                throws SAXException {
4040            quirks = (m == DocumentMode.QUIRKS_MODE);
4041            if (documentModeHandler != null) {
4042                documentModeHandler.documentMode(
4043                        m
4044                        // [NOCPP[
4045                        , publicIdentifier, systemIdentifier,
4046                        html4SpecificAdditionalErrorChecks
4047                // ]NOCPP]
4048                );
4049            }
4050            // [NOCPP[
4051            documentMode(m, publicIdentifier, systemIdentifier,
4052                    html4SpecificAdditionalErrorChecks);
4053            // ]NOCPP]
4054        }
4055    
4056        private boolean isAlmostStandards(String publicIdentifier,
4057                String systemIdentifier) {
4058            if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4059                    "-//w3c//dtd xhtml 1.0 transitional//en", publicIdentifier)) {
4060                return true;
4061            }
4062            if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4063                    "-//w3c//dtd xhtml 1.0 frameset//en", publicIdentifier)) {
4064                return true;
4065            }
4066            if (systemIdentifier != null) {
4067                if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4068                        "-//w3c//dtd html 4.01 transitional//en", publicIdentifier)) {
4069                    return true;
4070                }
4071                if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4072                        "-//w3c//dtd html 4.01 frameset//en", publicIdentifier)) {
4073                    return true;
4074                }
4075            }
4076            return false;
4077        }
4078    
4079        private boolean isQuirky(@Local String name, String publicIdentifier,
4080                String systemIdentifier, boolean forceQuirks) {
4081            if (forceQuirks) {
4082                return true;
4083            }
4084            if (name != HTML_LOCAL) {
4085                return true;
4086            }
4087            if (publicIdentifier != null) {
4088                for (int i = 0; i < TreeBuilder.QUIRKY_PUBLIC_IDS.length; i++) {
4089                    if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4090                            TreeBuilder.QUIRKY_PUBLIC_IDS[i], publicIdentifier)) {
4091                        return true;
4092                    }
4093                }
4094                if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4095                        "-//w3o//dtd w3 html strict 3.0//en//", publicIdentifier)
4096                        || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4097                                "-/w3c/dtd html 4.0 transitional/en",
4098                                publicIdentifier)
4099                        || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4100                                "html", publicIdentifier)) {
4101                    return true;
4102                }
4103            }
4104            if (systemIdentifier == null) {
4105                if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4106                        "-//w3c//dtd html 4.01 transitional//en", publicIdentifier)) {
4107                    return true;
4108                } else if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4109                        "-//w3c//dtd html 4.01 frameset//en", publicIdentifier)) {
4110                    return true;
4111                }
4112            } else if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4113                    "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd",
4114                    systemIdentifier)) {
4115                return true;
4116            }
4117            return false;
4118        }
4119    
4120        private void closeTheCell(int eltPos) throws SAXException {
4121            generateImpliedEndTags();
4122            if (errorHandler != null && eltPos != currentPtr) {
4123                errUnclosedElementsCell(eltPos);
4124            }
4125            while (currentPtr >= eltPos) {
4126                pop();
4127            }
4128            clearTheListOfActiveFormattingElementsUpToTheLastMarker();
4129            mode = IN_ROW;
4130            return;
4131        }
4132    
4133        private int findLastInTableScopeTdTh() {
4134            for (int i = currentPtr; i > 0; i--) {
4135                @Local String name = stack[i].name;
4136                if ("td" == name || "th" == name) {
4137                    return i;
4138                } else if (name == "table") {
4139                    return TreeBuilder.NOT_FOUND_ON_STACK;
4140                }
4141            }
4142            return TreeBuilder.NOT_FOUND_ON_STACK;
4143        }
4144    
4145        private void clearStackBackTo(int eltPos) throws SAXException {
4146            while (currentPtr > eltPos) { // > not >= intentional
4147                pop();
4148            }
4149        }
4150    
4151        private void resetTheInsertionMode() {
4152            StackNode<T> node;
4153            @Local String name;
4154            @NsUri String ns;
4155            for (int i = currentPtr; i >= 0; i--) {
4156                node = stack[i];
4157                name = node.name;
4158                ns = node.ns;
4159                if (i == 0) {
4160                    if (!(contextNamespace == "http://www.w3.org/1999/xhtml" && (contextName == "td" || contextName == "th"))) {
4161                        name = contextName;
4162                        ns = contextNamespace;
4163                    } else {
4164                        mode = framesetOk ? FRAMESET_OK : IN_BODY; // XXX from Hixie's email
4165                        return;
4166                    }
4167                }
4168                if ("select" == name) {
4169                    mode = IN_SELECT;
4170                    return;
4171                } else if ("td" == name || "th" == name) {
4172                    mode = IN_CELL;
4173                    return;
4174                } else if ("tr" == name) {
4175                    mode = IN_ROW;
4176                    return;
4177                } else if ("tbody" == name || "thead" == name || "tfoot" == name) {
4178                    mode = IN_TABLE_BODY;
4179                    return;
4180                } else if ("caption" == name) {
4181                    mode = IN_CAPTION;
4182                    return;
4183                } else if ("colgroup" == name) {
4184                    mode = IN_COLUMN_GROUP;
4185                    return;
4186                } else if ("table" == name) {
4187                    mode = IN_TABLE;
4188                    return;
4189                } else if ("http://www.w3.org/1999/xhtml" != ns) {
4190                    mode = framesetOk ? FRAMESET_OK : IN_BODY;
4191                    return;
4192                } else if ("head" == name) {
4193                    mode = framesetOk ? FRAMESET_OK : IN_BODY; // really
4194                    return;
4195                } else if ("body" == name) {
4196                    mode = framesetOk ? FRAMESET_OK : IN_BODY;
4197                    return;
4198                } else if ("frameset" == name) {
4199                    mode = IN_FRAMESET;
4200                    return;
4201                } else if ("html" == name) {
4202                    if (headPointer == null) {
4203                        mode = BEFORE_HEAD;
4204                    } else {
4205                        mode = AFTER_HEAD;
4206                    }
4207                    return;
4208                } else if (i == 0) {
4209                    mode = framesetOk ? FRAMESET_OK : IN_BODY;
4210                    return;
4211                }
4212            }
4213        }
4214    
4215        /**
4216         * @throws SAXException
4217         * 
4218         */
4219        private void implicitlyCloseP() throws SAXException {
4220            int eltPos = findLastInButtonScope("p");
4221            if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
4222                return;
4223            }
4224            generateImpliedEndTagsExceptFor("p");
4225            if (errorHandler != null && eltPos != currentPtr) {
4226                errUnclosedElementsImplied(eltPos, "p");
4227            }
4228            while (currentPtr >= eltPos) {
4229                pop();
4230            }
4231        }
4232    
4233        private boolean clearLastStackSlot() {
4234            stack[currentPtr] = null;
4235            return true;
4236        }
4237    
4238        private boolean clearLastListSlot() {
4239            listOfActiveFormattingElements[listPtr] = null;
4240            return true;
4241        }
4242    
4243        @SuppressWarnings("unchecked") private void push(StackNode<T> node) throws SAXException {
4244            currentPtr++;
4245            if (currentPtr == stack.length) {
4246                StackNode<T>[] newStack = new StackNode[stack.length + 64];
4247                System.arraycopy(stack, 0, newStack, 0, stack.length);
4248                stack = newStack;
4249            }
4250            stack[currentPtr] = node;
4251            elementPushed(node.ns, node.popName, node.node);
4252        }
4253    
4254        @SuppressWarnings("unchecked") private void silentPush(StackNode<T> node) throws SAXException {
4255            currentPtr++;
4256            if (currentPtr == stack.length) {
4257                StackNode<T>[] newStack = new StackNode[stack.length + 64];
4258                System.arraycopy(stack, 0, newStack, 0, stack.length);
4259                stack = newStack;
4260            }
4261            stack[currentPtr] = node;
4262        }
4263    
4264        @SuppressWarnings("unchecked") private void append(StackNode<T> node) {
4265            listPtr++;
4266            if (listPtr == listOfActiveFormattingElements.length) {
4267                StackNode<T>[] newList = new StackNode[listOfActiveFormattingElements.length + 64];
4268                System.arraycopy(listOfActiveFormattingElements, 0, newList, 0,
4269                        listOfActiveFormattingElements.length);
4270                listOfActiveFormattingElements = newList;
4271            }
4272            listOfActiveFormattingElements[listPtr] = node;
4273        }
4274    
4275        @Inline private void insertMarker() {
4276            append(null);
4277        }
4278    
4279        private void clearTheListOfActiveFormattingElementsUpToTheLastMarker() {
4280            while (listPtr > -1) {
4281                if (listOfActiveFormattingElements[listPtr] == null) {
4282                    --listPtr;
4283                    return;
4284                }
4285                listOfActiveFormattingElements[listPtr].release();
4286                --listPtr;
4287            }
4288        }
4289    
4290        @Inline private boolean isCurrent(@Local String name) {
4291            return name == stack[currentPtr].name;
4292        }
4293    
4294        private void removeFromStack(int pos) throws SAXException {
4295            if (currentPtr == pos) {
4296                pop();
4297            } else {
4298                fatal();
4299                stack[pos].release();
4300                System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
4301                assert clearLastStackSlot();
4302                currentPtr--;
4303            }
4304        }
4305    
4306        private void removeFromStack(StackNode<T> node) throws SAXException {
4307            if (stack[currentPtr] == node) {
4308                pop();
4309            } else {
4310                int pos = currentPtr - 1;
4311                while (pos >= 0 && stack[pos] != node) {
4312                    pos--;
4313                }
4314                if (pos == -1) {
4315                    // dead code?
4316                    return;
4317                }
4318                fatal();
4319                node.release();
4320                System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
4321                currentPtr--;
4322            }
4323        }
4324    
4325        private void removeFromListOfActiveFormattingElements(int pos) {
4326            assert listOfActiveFormattingElements[pos] != null;
4327            listOfActiveFormattingElements[pos].release();
4328            if (pos == listPtr) {
4329                assert clearLastListSlot();
4330                listPtr--;
4331                return;
4332            }
4333            assert pos < listPtr;
4334            System.arraycopy(listOfActiveFormattingElements, pos + 1,
4335                    listOfActiveFormattingElements, pos, listPtr - pos);
4336            assert clearLastListSlot();
4337            listPtr--;
4338        }
4339    
4340        private boolean adoptionAgencyEndTag(@Local String name) throws SAXException {
4341            // If you crash around here, perhaps some stack node variable claimed to
4342            // be a weak ref isn't.
4343            for (int i = 0; i < 8; ++i) {
4344                int formattingEltListPos = listPtr;
4345                while (formattingEltListPos > -1) {
4346                    StackNode<T> listNode = listOfActiveFormattingElements[formattingEltListPos]; // weak
4347                                                                                                  // ref
4348                    if (listNode == null) {
4349                        formattingEltListPos = -1;
4350                        break;
4351                    } else if (listNode.name == name) {
4352                        break;
4353                    }
4354                    formattingEltListPos--;
4355                }
4356                if (formattingEltListPos == -1) {
4357                    return false;
4358                }
4359                StackNode<T> formattingElt = listOfActiveFormattingElements[formattingEltListPos]; // this
4360                // *looks*
4361                // like
4362                // a
4363                // weak
4364                // ref
4365                // to
4366                // the
4367                // list
4368                // of
4369                // formatting
4370                // elements
4371                int formattingEltStackPos = currentPtr;
4372                boolean inScope = true;
4373                while (formattingEltStackPos > -1) {
4374                    StackNode<T> node = stack[formattingEltStackPos]; // weak ref
4375                    if (node == formattingElt) {
4376                        break;
4377                    } else if (node.isScoping()) {
4378                        inScope = false;
4379                    }
4380                    formattingEltStackPos--;
4381                }
4382                if (formattingEltStackPos == -1) {
4383                    err("No element \u201C" + name + "\u201D to close.");
4384                    removeFromListOfActiveFormattingElements(formattingEltListPos);
4385                    return true;
4386                }
4387                if (!inScope) {
4388                    err("No element \u201C" + name + "\u201D to close.");
4389                    return true;
4390                }
4391                // stackPos now points to the formatting element and it is in scope
4392                if (errorHandler != null && formattingEltStackPos != currentPtr) {
4393                    errNoCheck("End tag \u201C" + name + "\u201D violates nesting rules.");
4394                }
4395                int furthestBlockPos = formattingEltStackPos + 1;
4396                while (furthestBlockPos <= currentPtr) {
4397                    StackNode<T> node = stack[furthestBlockPos]; // weak ref
4398                    if (node.isSpecial()) {
4399                        break;
4400                    }
4401                    furthestBlockPos++;
4402                }
4403                if (furthestBlockPos > currentPtr) {
4404                    // no furthest block
4405                    while (currentPtr >= formattingEltStackPos) {
4406                        pop();
4407                    }
4408                    removeFromListOfActiveFormattingElements(formattingEltListPos);
4409                    return true;
4410                }
4411                StackNode<T> commonAncestor = stack[formattingEltStackPos - 1]; // weak
4412                // ref
4413                StackNode<T> furthestBlock = stack[furthestBlockPos]; // weak ref
4414                // detachFromParent(furthestBlock.node); XXX AAA CHANGE
4415                int bookmark = formattingEltListPos;
4416                int nodePos = furthestBlockPos;
4417                StackNode<T> lastNode = furthestBlock; // weak ref
4418                for (int j = 0; j < 3; ++j) {
4419                    nodePos--;
4420                    StackNode<T> node = stack[nodePos]; // weak ref
4421                    int nodeListPos = findInListOfActiveFormattingElements(node);
4422                    if (nodeListPos == -1) {
4423                        assert formattingEltStackPos < nodePos;
4424                        assert bookmark < nodePos;
4425                        assert furthestBlockPos > nodePos;
4426                        removeFromStack(nodePos); // node is now a bad pointer in
4427                        // C++
4428                        furthestBlockPos--;
4429                        continue;
4430                    }
4431                    // now node is both on stack and in the list
4432                    if (nodePos == formattingEltStackPos) {
4433                        break;
4434                    }
4435                    if (nodePos == furthestBlockPos) {
4436                        bookmark = nodeListPos + 1;
4437                    }
4438                    // if (hasChildren(node.node)) { XXX AAA CHANGE
4439                    assert node == listOfActiveFormattingElements[nodeListPos];
4440                    assert node == stack[nodePos];
4441                    T clone = createElement("http://www.w3.org/1999/xhtml",
4442                            node.name, node.attributes.cloneAttributes(null));
4443                    StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
4444                            node.name, clone, node.popName, node.attributes
4445                            // [NOCPP[
4446                            , node.getLocator()
4447                    // ]NOCPP]       
4448                    ); // creation
4449                    // ownership
4450                    // goes
4451                    // to
4452                    // stack
4453                    node.dropAttributes(); // adopt ownership to newNode
4454                    stack[nodePos] = newNode;
4455                    newNode.retain(); // retain for list
4456                    listOfActiveFormattingElements[nodeListPos] = newNode;
4457                    node.release(); // release from stack
4458                    node.release(); // release from list
4459                    node = newNode;
4460                    // } XXX AAA CHANGE
4461                    detachFromParent(lastNode.node);
4462                    appendElement(lastNode.node, node.node);
4463                    lastNode = node;
4464                }
4465                if (commonAncestor.isFosterParenting()) {
4466                    fatal();
4467                    detachFromParent(lastNode.node);
4468                    insertIntoFosterParent(lastNode.node);
4469                } else {
4470                    detachFromParent(lastNode.node);
4471                    appendElement(lastNode.node, commonAncestor.node);
4472                }
4473                T clone = createElement("http://www.w3.org/1999/xhtml",
4474                        formattingElt.name,
4475                        formattingElt.attributes.cloneAttributes(null));
4476                StackNode<T> formattingClone = new StackNode<T>(
4477                        formattingElt.getFlags(), formattingElt.ns,
4478                        formattingElt.name, clone, formattingElt.popName,
4479                        formattingElt.attributes
4480                        // [NOCPP[
4481                        , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4482                // ]NOCPP]
4483                ); // Ownership
4484                // transfers
4485                // to
4486                // stack
4487                // below
4488                formattingElt.dropAttributes(); // transfer ownership to
4489                                                // formattingClone
4490                appendChildrenToNewParent(furthestBlock.node, clone);
4491                appendElement(clone, furthestBlock.node);
4492                removeFromListOfActiveFormattingElements(formattingEltListPos);
4493                insertIntoListOfActiveFormattingElements(formattingClone, bookmark);
4494                assert formattingEltStackPos < furthestBlockPos;
4495                removeFromStack(formattingEltStackPos);
4496                // furthestBlockPos is now off by one and points to the slot after
4497                // it
4498                insertIntoStack(formattingClone, furthestBlockPos);
4499            }
4500            return true;
4501        }
4502    
4503        private void insertIntoStack(StackNode<T> node, int position)
4504                throws SAXException {
4505            assert currentPtr + 1 < stack.length;
4506            assert position <= currentPtr + 1;
4507            if (position == currentPtr + 1) {
4508                push(node);
4509            } else {
4510                System.arraycopy(stack, position, stack, position + 1,
4511                        (currentPtr - position) + 1);
4512                currentPtr++;
4513                stack[position] = node;
4514            }
4515        }
4516    
4517        private void insertIntoListOfActiveFormattingElements(
4518                StackNode<T> formattingClone, int bookmark) {
4519            formattingClone.retain();
4520            assert listPtr + 1 < listOfActiveFormattingElements.length;
4521            if (bookmark <= listPtr) {
4522                System.arraycopy(listOfActiveFormattingElements, bookmark,
4523                        listOfActiveFormattingElements, bookmark + 1,
4524                        (listPtr - bookmark) + 1);
4525            }
4526            listPtr++;
4527            listOfActiveFormattingElements[bookmark] = formattingClone;
4528        }
4529    
4530        private int findInListOfActiveFormattingElements(StackNode<T> node) {
4531            for (int i = listPtr; i >= 0; i--) {
4532                if (node == listOfActiveFormattingElements[i]) {
4533                    return i;
4534                }
4535            }
4536            return -1;
4537        }
4538    
4539        private int findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker(
4540                @Local String name) {
4541            for (int i = listPtr; i >= 0; i--) {
4542                StackNode<T> node = listOfActiveFormattingElements[i];
4543                if (node == null) {
4544                    return -1;
4545                } else if (node.name == name) {
4546                    return i;
4547                }
4548            }
4549            return -1;
4550        }
4551    
4552    
4553        private void maybeForgetEarlierDuplicateFormattingElement(
4554                @Local String name, HtmlAttributes attributes) throws SAXException {
4555            int candidate = -1;
4556            int count = 0;
4557            for (int i = listPtr; i >= 0; i--) {
4558                StackNode<T> node = listOfActiveFormattingElements[i];
4559                if (node == null) {
4560                    break;
4561                }
4562                if (node.name == name && node.attributes.equalsAnother(attributes)) {
4563                    candidate = i;
4564                    ++count;
4565                }
4566            }
4567            if (count >= 3) {
4568                removeFromListOfActiveFormattingElements(candidate);
4569            }
4570        }
4571        
4572        private int findLastOrRoot(@Local String name) {
4573            for (int i = currentPtr; i > 0; i--) {
4574                if (stack[i].name == name) {
4575                    return i;
4576                }
4577            }
4578            return 0;
4579        }
4580    
4581        private int findLastOrRoot(int group) {
4582            for (int i = currentPtr; i > 0; i--) {
4583                if (stack[i].getGroup() == group) {
4584                    return i;
4585                }
4586            }
4587            return 0;
4588        }
4589    
4590        /**
4591         * Attempt to add attribute to the body element.
4592         * @param attributes the attributes
4593         * @return <code>true</code> iff the attributes were added
4594         * @throws SAXException
4595         */
4596        private boolean addAttributesToBody(HtmlAttributes attributes)
4597                throws SAXException {
4598            // [NOCPP[
4599            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4600            // ]NOCPP]
4601            if (currentPtr >= 1) {
4602                StackNode<T> body = stack[1];
4603                if (body.getGroup() == TreeBuilder.BODY) {
4604                    addAttributesToElement(body.node, attributes);
4605                    return true;
4606                }
4607            }
4608            return false;
4609        }
4610    
4611        private void addAttributesToHtml(HtmlAttributes attributes)
4612                throws SAXException {
4613            // [NOCPP[
4614            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4615            // ]NOCPP]
4616            addAttributesToElement(stack[0].node, attributes);
4617        }
4618    
4619        private void pushHeadPointerOntoStack() throws SAXException {
4620            assert headPointer != null;
4621            assert !fragment;
4622            assert mode == AFTER_HEAD;
4623            fatal();
4624            silentPush(new StackNode<T>(ElementName.HEAD, headPointer
4625            // [NOCPP[
4626                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4627            // ]NOCPP]
4628            ));
4629        }
4630    
4631        /**
4632         * @throws SAXException
4633         * 
4634         */
4635        private void reconstructTheActiveFormattingElements() throws SAXException {
4636            if (listPtr == -1) {
4637                return;
4638            }
4639            StackNode<T> mostRecent = listOfActiveFormattingElements[listPtr];
4640            if (mostRecent == null || isInStack(mostRecent)) {
4641                return;
4642            }
4643            int entryPos = listPtr;
4644            for (;;) {
4645                entryPos--;
4646                if (entryPos == -1) {
4647                    break;
4648                }
4649                if (listOfActiveFormattingElements[entryPos] == null) {
4650                    break;
4651                }
4652                if (isInStack(listOfActiveFormattingElements[entryPos])) {
4653                    break;
4654                }
4655            }
4656            while (entryPos < listPtr) {
4657                entryPos++;
4658                StackNode<T> entry = listOfActiveFormattingElements[entryPos];
4659                T clone = createElement("http://www.w3.org/1999/xhtml", entry.name,
4660                        entry.attributes.cloneAttributes(null));
4661                StackNode<T> entryClone = new StackNode<T>(entry.getFlags(),
4662                        entry.ns, entry.name, clone, entry.popName,
4663                        entry.attributes
4664                        // [NOCPP[
4665                        , entry.getLocator()
4666                // ]NOCPP]
4667                );
4668                entry.dropAttributes(); // transfer ownership to entryClone
4669                StackNode<T> currentNode = stack[currentPtr];
4670                if (currentNode.isFosterParenting()) {
4671                    insertIntoFosterParent(clone);
4672                } else {
4673                    appendElement(clone, currentNode.node);
4674                }
4675                push(entryClone);
4676                // stack takes ownership of the local variable
4677                listOfActiveFormattingElements[entryPos] = entryClone;
4678                // overwriting the old entry on the list, so release & retain
4679                entry.release();
4680                entryClone.retain();
4681            }
4682        }
4683    
4684        private void insertIntoFosterParent(T child) throws SAXException {
4685            int eltPos = findLastOrRoot(TreeBuilder.TABLE);
4686            StackNode<T> node = stack[eltPos];
4687            T elt = node.node;
4688            if (eltPos == 0) {
4689                appendElement(child, elt);
4690                return;
4691            }
4692            insertFosterParentedChild(child, elt, stack[eltPos - 1].node);
4693        }
4694    
4695        private boolean isInStack(StackNode<T> node) {
4696            for (int i = currentPtr; i >= 0; i--) {
4697                if (stack[i] == node) {
4698                    return true;
4699                }
4700            }
4701            return false;
4702        }
4703    
4704        private void pop() throws SAXException {
4705            StackNode<T> node = stack[currentPtr];
4706            assert clearLastStackSlot();
4707            currentPtr--;
4708            elementPopped(node.ns, node.popName, node.node);
4709            node.release();
4710        }
4711    
4712        private void silentPop() throws SAXException {
4713            StackNode<T> node = stack[currentPtr];
4714            assert clearLastStackSlot();
4715            currentPtr--;
4716            node.release();
4717        }
4718    
4719        private void popOnEof() throws SAXException {
4720            StackNode<T> node = stack[currentPtr];
4721            assert clearLastStackSlot();
4722            currentPtr--;
4723            markMalformedIfScript(node.node);
4724            elementPopped(node.ns, node.popName, node.node);
4725            node.release();
4726        }
4727    
4728        // [NOCPP[
4729        private void checkAttributes(HtmlAttributes attributes, @NsUri String ns)
4730                throws SAXException {
4731            if (errorHandler != null) {
4732                int len = attributes.getXmlnsLength();
4733                for (int i = 0; i < len; i++) {
4734                    AttributeName name = attributes.getXmlnsAttributeName(i);
4735                    if (name == AttributeName.XMLNS) {
4736                        if (html4) {
4737                            err("Attribute \u201Cxmlns\u201D not allowed here. (HTML4-only error.)");
4738                        } else {
4739                            String xmlns = attributes.getXmlnsValue(i);
4740                            if (!ns.equals(xmlns)) {
4741                                err("Bad value \u201C"
4742                                        + xmlns
4743                                        + "\u201D for the attribute \u201Cxmlns\u201D (only \u201C"
4744                                        + ns + "\u201D permitted here).");
4745                                switch (namePolicy) {
4746                                    case ALTER_INFOSET:
4747                                        // fall through
4748                                    case ALLOW:
4749                                        warn("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0.");
4750                                        break;
4751                                    case FATAL:
4752                                        fatal("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0.");
4753                                        break;
4754                                }
4755                            }
4756                        }
4757                    } else if (ns != "http://www.w3.org/1999/xhtml"
4758                            && name == AttributeName.XMLNS_XLINK) {
4759                        String xmlns = attributes.getXmlnsValue(i);
4760                        if (!"http://www.w3.org/1999/xlink".equals(xmlns)) {
4761                            err("Bad value \u201C"
4762                                    + xmlns
4763                                    + "\u201D for the attribute \u201Cxmlns:link\u201D (only \u201Chttp://www.w3.org/1999/xlink\u201D permitted here).");
4764                            switch (namePolicy) {
4765                                case ALTER_INFOSET:
4766                                    // fall through
4767                                case ALLOW:
4768                                    warn("Attribute \u201Cxmlns:xlink\u201D with the value \u201Chttp://www.w3org/1999/xlink\u201D is not serializable as XML 1.0 without changing document semantics.");
4769                                    break;
4770                                case FATAL:
4771                                    fatal("Attribute \u201Cxmlns:xlink\u201D with the value \u201Chttp://www.w3org/1999/xlink\u201D is not serializable as XML 1.0 without changing document semantics.");
4772                                    break;
4773                            }
4774                        }
4775                    } else {
4776                        err("Attribute \u201C" + attributes.getXmlnsLocalName(i)
4777                                + "\u201D not allowed here.");
4778                        switch (namePolicy) {
4779                            case ALTER_INFOSET:
4780                                // fall through
4781                            case ALLOW:
4782                                warn("Attribute with the local name \u201C"
4783                                        + attributes.getXmlnsLocalName(i)
4784                                        + "\u201D is not serializable as XML 1.0.");
4785                                break;
4786                            case FATAL:
4787                                fatal("Attribute with the local name \u201C"
4788                                        + attributes.getXmlnsLocalName(i)
4789                                        + "\u201D is not serializable as XML 1.0.");
4790                                break;
4791                        }
4792                    }
4793                }
4794            }
4795            attributes.processNonNcNames(this, namePolicy);
4796        }
4797    
4798        private String checkPopName(@Local String name) throws SAXException {
4799            if (NCName.isNCName(name)) {
4800                return name;
4801            } else {
4802                switch (namePolicy) {
4803                    case ALLOW:
4804                        warn("Element name \u201C" + name
4805                                + "\u201D cannot be represented as XML 1.0.");
4806                        return name;
4807                    case ALTER_INFOSET:
4808                        warn("Element name \u201C" + name
4809                                + "\u201D cannot be represented as XML 1.0.");
4810                        return NCName.escapeName(name);
4811                    case FATAL:
4812                        fatal("Element name \u201C" + name
4813                                + "\u201D cannot be represented as XML 1.0.");
4814                }
4815            }
4816            return null; // keep compiler happy
4817        }
4818    
4819        // ]NOCPP]
4820    
4821        private void appendHtmlElementToDocumentAndPush(HtmlAttributes attributes)
4822                throws SAXException {
4823            // [NOCPP[
4824            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4825            // ]NOCPP]
4826            T elt = createHtmlElementSetAsRoot(attributes);
4827            StackNode<T> node = new StackNode<T>(ElementName.HTML,
4828                    elt
4829                    // [NOCPP[
4830                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4831            // ]NOCPP]
4832            );
4833            push(node);
4834        }
4835    
4836        private void appendHtmlElementToDocumentAndPush() throws SAXException {
4837            appendHtmlElementToDocumentAndPush(tokenizer.emptyAttributes());
4838        }
4839    
4840        private void appendToCurrentNodeAndPushHeadElement(HtmlAttributes attributes)
4841                throws SAXException {
4842            // [NOCPP[
4843            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4844            // ]NOCPP]
4845            T elt = createElement("http://www.w3.org/1999/xhtml", "head",
4846                    attributes);
4847            appendElement(elt, stack[currentPtr].node);
4848            headPointer = elt;
4849            StackNode<T> node = new StackNode<T>(ElementName.HEAD,
4850                    elt
4851                    // [NOCPP[
4852                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4853            // ]NOCPP]
4854            );
4855            push(node);
4856        }
4857    
4858        private void appendToCurrentNodeAndPushBodyElement(HtmlAttributes attributes)
4859                throws SAXException {
4860            appendToCurrentNodeAndPushElement(ElementName.BODY,
4861                    attributes);
4862        }
4863    
4864        private void appendToCurrentNodeAndPushBodyElement() throws SAXException {
4865            appendToCurrentNodeAndPushBodyElement(tokenizer.emptyAttributes());
4866        }
4867    
4868        private void appendToCurrentNodeAndPushFormElementMayFoster(
4869                HtmlAttributes attributes) throws SAXException {
4870            // [NOCPP[
4871            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4872            // ]NOCPP]
4873            T elt = createElement("http://www.w3.org/1999/xhtml", "form",
4874                    attributes);
4875            formPointer = elt;
4876            StackNode<T> current = stack[currentPtr];
4877            if (current.isFosterParenting()) {
4878                fatal();
4879                insertIntoFosterParent(elt);
4880            } else {
4881                appendElement(elt, current.node);
4882            }
4883            StackNode<T> node = new StackNode<T>(ElementName.FORM,
4884                    elt
4885                    // [NOCPP[
4886                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4887            // ]NOCPP]
4888            );
4889            push(node);
4890        }
4891    
4892        private void appendToCurrentNodeAndPushFormattingElementMayFoster(
4893                ElementName elementName, HtmlAttributes attributes)
4894                throws SAXException {
4895            // [NOCPP[
4896            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4897            // ]NOCPP]
4898            // This method can't be called for custom elements
4899            T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes);
4900            StackNode<T> current = stack[currentPtr];
4901            if (current.isFosterParenting()) {
4902                fatal();
4903                insertIntoFosterParent(elt);
4904            } else {
4905                appendElement(elt, current.node);
4906            }
4907            StackNode<T> node = new StackNode<T>(elementName, elt, attributes.cloneAttributes(null)
4908                    // [NOCPP[
4909                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4910            // ]NOCPP]
4911            );
4912            push(node);
4913            append(node);
4914            node.retain(); // append doesn't retain itself
4915        }
4916    
4917        private void appendToCurrentNodeAndPushElement(ElementName elementName,
4918                HtmlAttributes attributes)
4919                throws SAXException {
4920            // [NOCPP[
4921            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4922            // ]NOCPP]
4923            // This method can't be called for custom elements
4924            T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes);
4925            appendElement(elt, stack[currentPtr].node);
4926            StackNode<T> node = new StackNode<T>(elementName, elt
4927                    // [NOCPP[
4928                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4929            // ]NOCPP]
4930            );
4931            push(node);
4932        }
4933    
4934        private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
4935                HtmlAttributes attributes)
4936                throws SAXException {
4937            @Local String popName = elementName.name;
4938            // [NOCPP[
4939            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4940            if (elementName.isCustom()) {
4941                popName = checkPopName(popName);
4942            }
4943            // ]NOCPP]
4944            T elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes);
4945            StackNode<T> current = stack[currentPtr];
4946            if (current.isFosterParenting()) {
4947                fatal();
4948                insertIntoFosterParent(elt);
4949            } else {
4950                appendElement(elt, current.node);
4951            }
4952            StackNode<T> node = new StackNode<T>(elementName, elt, popName
4953                    // [NOCPP[
4954                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4955            // ]NOCPP]
4956            );
4957            push(node);
4958        }
4959    
4960        private void appendToCurrentNodeAndPushElementMayFosterMathML(
4961                ElementName elementName, HtmlAttributes attributes)
4962                throws SAXException {
4963            @Local String popName = elementName.name;
4964            // [NOCPP[
4965            checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
4966            if (elementName.isCustom()) {
4967                popName = checkPopName(popName);
4968            }
4969            // ]NOCPP]
4970            T elt = createElement("http://www.w3.org/1998/Math/MathML", popName,
4971                    attributes);
4972            StackNode<T> current = stack[currentPtr];
4973            if (current.isFosterParenting()) {
4974                fatal();
4975                insertIntoFosterParent(elt);
4976            } else {
4977                appendElement(elt, current.node);
4978            }
4979            boolean markAsHtmlIntegrationPoint = false;
4980            if (ElementName.ANNOTATION_XML == elementName
4981                    && annotationXmlEncodingPermitsHtml(attributes)) {
4982                markAsHtmlIntegrationPoint = true;
4983            }
4984            StackNode<T> node = new StackNode<T>(elementName, elt, popName,
4985                    markAsHtmlIntegrationPoint
4986                    // [NOCPP[
4987                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4988            // ]NOCPP]
4989            );
4990            push(node);
4991        }
4992    
4993        private boolean annotationXmlEncodingPermitsHtml(HtmlAttributes attributes) {
4994            String encoding = attributes.getValue(AttributeName.ENCODING);
4995            if (encoding == null) {
4996                return false;
4997            }
4998            return Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4999                    "application/xhtml+xml", encoding)
5000                    || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
5001                            "text/html", encoding);
5002        }
5003    
5004        private void appendToCurrentNodeAndPushElementMayFosterSVG(
5005                ElementName elementName, HtmlAttributes attributes)
5006                throws SAXException {
5007            @Local String popName = elementName.camelCaseName;
5008            // [NOCPP[
5009            checkAttributes(attributes, "http://www.w3.org/2000/svg");
5010            if (elementName.isCustom()) {
5011                popName = checkPopName(popName);
5012            }
5013            // ]NOCPP]
5014            T elt = createElement("http://www.w3.org/2000/svg", popName, attributes);
5015            StackNode<T> current = stack[currentPtr];
5016            if (current.isFosterParenting()) {
5017                fatal();
5018                insertIntoFosterParent(elt);
5019            } else {
5020                appendElement(elt, current.node);
5021            }
5022            StackNode<T> node = new StackNode<T>(elementName, popName, elt
5023                    // [NOCPP[
5024                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5025            // ]NOCPP]
5026            );
5027            push(node);
5028        }
5029    
5030        private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
5031                HtmlAttributes attributes, T form)
5032                throws SAXException {
5033            // [NOCPP[
5034            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5035            // ]NOCPP]
5036            // Can't be called for custom elements
5037            T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes, fragment ? null
5038                    : form);
5039            StackNode<T> current = stack[currentPtr];
5040            if (current.isFosterParenting()) {
5041                fatal();
5042                insertIntoFosterParent(elt);
5043            } else {
5044                appendElement(elt, current.node);
5045            }
5046            StackNode<T> node = new StackNode<T>(elementName, elt
5047                    // [NOCPP[
5048                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5049            // ]NOCPP]
5050            );
5051            push(node);
5052        }
5053    
5054        private void appendVoidElementToCurrentMayFoster(
5055                @Local String name, HtmlAttributes attributes, T form) throws SAXException {
5056            // [NOCPP[
5057            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5058            // ]NOCPP]
5059            // Can't be called for custom elements
5060            T elt = createElement("http://www.w3.org/1999/xhtml", name, attributes, fragment ? null : form);
5061            StackNode<T> current = stack[currentPtr];
5062            if (current.isFosterParenting()) {
5063                fatal();
5064                insertIntoFosterParent(elt);
5065            } else {
5066                appendElement(elt, current.node);
5067            }
5068            elementPushed("http://www.w3.org/1999/xhtml", name, elt);
5069            elementPopped("http://www.w3.org/1999/xhtml", name, elt);
5070        }
5071    
5072        private void appendVoidElementToCurrentMayFoster(
5073                ElementName elementName, HtmlAttributes attributes)
5074                throws SAXException {
5075            @Local String popName = elementName.name;
5076            // [NOCPP[
5077            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5078            if (elementName.isCustom()) {
5079                popName = checkPopName(popName);
5080            }
5081            // ]NOCPP]
5082            T elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes);
5083            StackNode<T> current = stack[currentPtr];
5084            if (current.isFosterParenting()) {
5085                fatal();
5086                insertIntoFosterParent(elt);
5087            } else {
5088                appendElement(elt, current.node);
5089            }
5090            elementPushed("http://www.w3.org/1999/xhtml", popName, elt);
5091            elementPopped("http://www.w3.org/1999/xhtml", popName, elt);
5092        }
5093    
5094        private void appendVoidElementToCurrentMayFosterSVG(
5095                ElementName elementName, HtmlAttributes attributes)
5096                throws SAXException {
5097            @Local String popName = elementName.camelCaseName;
5098            // [NOCPP[
5099            checkAttributes(attributes, "http://www.w3.org/2000/svg");
5100            if (elementName.isCustom()) {
5101                popName = checkPopName(popName);
5102            }
5103            // ]NOCPP]
5104            T elt = createElement("http://www.w3.org/2000/svg", popName, attributes);
5105            StackNode<T> current = stack[currentPtr];
5106            if (current.isFosterParenting()) {
5107                fatal();
5108                insertIntoFosterParent(elt);
5109            } else {
5110                appendElement(elt, current.node);
5111            }
5112            elementPushed("http://www.w3.org/2000/svg", popName, elt);
5113            elementPopped("http://www.w3.org/2000/svg", popName, elt);
5114        }
5115    
5116        private void appendVoidElementToCurrentMayFosterMathML(
5117                ElementName elementName, HtmlAttributes attributes)
5118                throws SAXException {
5119            @Local String popName = elementName.name;
5120            // [NOCPP[
5121            checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
5122            if (elementName.isCustom()) {
5123                popName = checkPopName(popName);
5124            }
5125            // ]NOCPP]
5126            T elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes);
5127            StackNode<T> current = stack[currentPtr];
5128            if (current.isFosterParenting()) {
5129                fatal();
5130                insertIntoFosterParent(elt);
5131            } else {
5132                appendElement(elt, current.node);
5133            }
5134            elementPushed("http://www.w3.org/1998/Math/MathML", popName, elt);
5135            elementPopped("http://www.w3.org/1998/Math/MathML", popName, elt);
5136        }
5137    
5138        private void appendVoidElementToCurrent(
5139                @Local String name, HtmlAttributes attributes, T form) throws SAXException {
5140            // [NOCPP[
5141            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5142            // ]NOCPP]
5143            // Can't be called for custom elements
5144            T elt = createElement("http://www.w3.org/1999/xhtml", name, attributes, fragment ? null : form);
5145            StackNode<T> current = stack[currentPtr];
5146            appendElement(elt, current.node);
5147            elementPushed("http://www.w3.org/1999/xhtml", name, elt);
5148            elementPopped("http://www.w3.org/1999/xhtml", name, elt);
5149        }
5150    
5151        private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXException {
5152            // [NOCPP[
5153            checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5154            // ]NOCPP]
5155            T elt = createElement("http://www.w3.org/1999/xhtml", "form",
5156                    attributes);
5157            formPointer = elt;
5158            // ownership transferred to form pointer
5159            StackNode<T> current = stack[currentPtr];
5160            appendElement(elt, current.node);
5161            elementPushed("http://www.w3.org/1999/xhtml", "form", elt);
5162            elementPopped("http://www.w3.org/1999/xhtml", "form", elt);
5163        }
5164    
5165        // [NOCPP[
5166        
5167        private final void accumulateCharactersForced(@Const @NoLength char[] buf,
5168                int start, int length) throws SAXException {
5169            int newLen = charBufferLen + length;
5170            if (newLen > charBuffer.length) {
5171                char[] newBuf = new char[newLen];
5172                System.arraycopy(charBuffer, 0, newBuf, 0, charBufferLen);
5173                charBuffer = newBuf;
5174            }
5175            System.arraycopy(buf, start, charBuffer, charBufferLen, length);
5176            charBufferLen = newLen;
5177        }
5178        
5179        // ]NOCPP]
5180        
5181        protected void accumulateCharacters(@Const @NoLength char[] buf, int start,
5182                int length) throws SAXException {
5183            appendCharacters(stack[currentPtr].node, buf, start, length);
5184        }
5185    
5186        // ------------------------------- //
5187    
5188        protected final void requestSuspension() {
5189            tokenizer.requestSuspension();
5190        }
5191    
5192        protected abstract T createElement(@NsUri String ns, @Local String name,
5193                HtmlAttributes attributes) throws SAXException;
5194    
5195        protected T createElement(@NsUri String ns, @Local String name,
5196                HtmlAttributes attributes, T form) throws SAXException {
5197            return createElement("http://www.w3.org/1999/xhtml", name, attributes);
5198        }
5199    
5200        protected abstract T createHtmlElementSetAsRoot(HtmlAttributes attributes)
5201                throws SAXException;
5202    
5203        protected abstract void detachFromParent(T element) throws SAXException;
5204    
5205        protected abstract boolean hasChildren(T element) throws SAXException;
5206    
5207        protected abstract void appendElement(T child, T newParent)
5208                throws SAXException;
5209    
5210        protected abstract void appendChildrenToNewParent(T oldParent, T newParent)
5211                throws SAXException;
5212    
5213        protected abstract void insertFosterParentedChild(T child, T table,
5214                T stackParent) throws SAXException;
5215    
5216        protected abstract void insertFosterParentedCharacters(
5217                @NoLength char[] buf, int start, int length, T table, T stackParent)
5218                throws SAXException;
5219    
5220        protected abstract void appendCharacters(T parent, @NoLength char[] buf,
5221                int start, int length) throws SAXException;
5222    
5223        protected abstract void appendIsindexPrompt(T parent) throws SAXException;
5224        
5225        protected abstract void appendComment(T parent, @NoLength char[] buf,
5226                int start, int length) throws SAXException;
5227    
5228        protected abstract void appendCommentToDocument(@NoLength char[] buf,
5229                int start, int length) throws SAXException;
5230    
5231        protected abstract void addAttributesToElement(T element,
5232                HtmlAttributes attributes) throws SAXException;
5233    
5234        protected void markMalformedIfScript(T elt) throws SAXException {
5235    
5236        }
5237    
5238        protected void start(boolean fragmentMode) throws SAXException {
5239    
5240        }
5241    
5242        protected void end() throws SAXException {
5243    
5244        }
5245    
5246        protected void appendDoctypeToDocument(@Local String name,
5247                String publicIdentifier, String systemIdentifier)
5248                throws SAXException {
5249    
5250        }
5251    
5252        protected void elementPushed(@NsUri String ns, @Local String name, T node)
5253                throws SAXException {
5254    
5255        }
5256    
5257        protected void elementPopped(@NsUri String ns, @Local String name, T node)
5258                throws SAXException {
5259    
5260        }
5261    
5262        // [NOCPP[
5263    
5264        protected void documentMode(DocumentMode m, String publicIdentifier,
5265                String systemIdentifier, boolean html4SpecificAdditionalErrorChecks)
5266                throws SAXException {
5267    
5268        }
5269    
5270        /**
5271         * @see nu.validator.htmlparser.common.TokenHandler#wantsComments()
5272         */
5273        public boolean wantsComments() {
5274            return wantingComments;
5275        }
5276    
5277        public void setIgnoringComments(boolean ignoreComments) {
5278            wantingComments = !ignoreComments;
5279        }
5280    
5281        /**
5282         * Sets the errorHandler.
5283         * 
5284         * @param errorHandler
5285         *            the errorHandler to set
5286         */
5287        public final void setErrorHandler(ErrorHandler errorHandler) {
5288            this.errorHandler = errorHandler;
5289        }
5290    
5291        /**
5292         * Returns the errorHandler.
5293         * 
5294         * @return the errorHandler
5295         */
5296        public ErrorHandler getErrorHandler() {
5297            return errorHandler;
5298        }
5299    
5300        /**
5301         * The argument MUST be an interned string or <code>null</code>.
5302         * 
5303         * @param context
5304         */
5305        public final void setFragmentContext(@Local String context) {
5306            this.contextName = context;
5307            this.contextNamespace = "http://www.w3.org/1999/xhtml";
5308            this.contextNode = null;
5309            this.fragment = (contextName != null);
5310            this.quirks = false;
5311        }
5312    
5313        // ]NOCPP]
5314    
5315        /**
5316         * @see nu.validator.htmlparser.common.TokenHandler#cdataSectionAllowed()
5317         */
5318        @Inline public boolean cdataSectionAllowed() throws SAXException {
5319            return isInForeign();
5320        }
5321        
5322        private boolean isInForeign() {
5323            return currentPtr >= 0
5324                    && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml";
5325        }
5326    
5327        /**
5328         * The argument MUST be an interned string or <code>null</code>.
5329         * 
5330         * @param context
5331         */
5332        public final void setFragmentContext(@Local String context,
5333                @NsUri String ns, T node, boolean quirks) {
5334            this.contextName = context;
5335            this.contextNamespace = ns;
5336            this.contextNode = node;
5337            this.fragment = (contextName != null);
5338            this.quirks = quirks;
5339        }
5340    
5341        protected final T currentNode() {
5342            return stack[currentPtr].node;
5343        }
5344    
5345        /**
5346         * Returns the scriptingEnabled.
5347         * 
5348         * @return the scriptingEnabled
5349         */
5350        public boolean isScriptingEnabled() {
5351            return scriptingEnabled;
5352        }
5353    
5354        /**
5355         * Sets the scriptingEnabled.
5356         * 
5357         * @param scriptingEnabled
5358         *            the scriptingEnabled to set
5359         */
5360        public void setScriptingEnabled(boolean scriptingEnabled) {
5361            this.scriptingEnabled = scriptingEnabled;
5362        }
5363    
5364        // [NOCPP[
5365    
5366        /**
5367         * Sets the doctypeExpectation.
5368         * 
5369         * @param doctypeExpectation
5370         *            the doctypeExpectation to set
5371         */
5372        public void setDoctypeExpectation(DoctypeExpectation doctypeExpectation) {
5373            this.doctypeExpectation = doctypeExpectation;
5374        }
5375    
5376        public void setNamePolicy(XmlViolationPolicy namePolicy) {
5377            this.namePolicy = namePolicy;
5378        }
5379    
5380        /**
5381         * Sets the documentModeHandler.
5382         * 
5383         * @param documentModeHandler
5384         *            the documentModeHandler to set
5385         */
5386        public void setDocumentModeHandler(DocumentModeHandler documentModeHandler) {
5387            this.documentModeHandler = documentModeHandler;
5388        }
5389    
5390        /**
5391         * Sets the reportingDoctype.
5392         * 
5393         * @param reportingDoctype
5394         *            the reportingDoctype to set
5395         */
5396        public void setReportingDoctype(boolean reportingDoctype) {
5397            this.reportingDoctype = reportingDoctype;
5398        }
5399    
5400        // ]NOCPP]
5401    
5402        /**
5403         * Flushes the pending characters. Public for document.write use cases only.
5404         * @throws SAXException
5405         */
5406        public final void flushCharacters() throws SAXException {
5407            if (charBufferLen > 0) {
5408                if ((mode == IN_TABLE || mode == IN_TABLE_BODY || mode == IN_ROW)
5409                        && charBufferContainsNonWhitespace()) {
5410                    err("Misplaced non-space characters insided a table.");
5411                    reconstructTheActiveFormattingElements();
5412                    if (!stack[currentPtr].isFosterParenting()) {
5413                        // reconstructing gave us a new current node
5414                        appendCharacters(currentNode(), charBuffer, 0,
5415                                charBufferLen);
5416                        charBufferLen = 0;
5417                        return;
5418                    }
5419                    int eltPos = findLastOrRoot(TreeBuilder.TABLE);
5420                    StackNode<T> node = stack[eltPos];
5421                    T elt = node.node;
5422                    if (eltPos == 0) {
5423                        appendCharacters(elt, charBuffer, 0, charBufferLen);
5424                        charBufferLen = 0;
5425                        return;
5426                    }
5427                    insertFosterParentedCharacters(charBuffer, 0, charBufferLen,
5428                            elt, stack[eltPos - 1].node);
5429                    charBufferLen = 0;
5430                    return;
5431                }
5432                appendCharacters(currentNode(), charBuffer, 0, charBufferLen);
5433                charBufferLen = 0;
5434            }
5435        }
5436    
5437        private boolean charBufferContainsNonWhitespace() {
5438            for (int i = 0; i < charBufferLen; i++) {
5439                switch (charBuffer[i]) {
5440                    case ' ':
5441                    case '\t':
5442                    case '\n':
5443                    case '\r':
5444                    case '\u000C':
5445                        continue;
5446                    default:
5447                        return true;
5448                }
5449            }
5450            return false;
5451        }
5452    
5453        /**
5454         * Creates a comparable snapshot of the tree builder state. Snapshot
5455         * creation is only supported immediately after a script end tag has been
5456         * processed. In C++ the caller is responsible for calling
5457         * <code>delete</code> on the returned object.
5458         * 
5459         * @return a snapshot.
5460         * @throws SAXException
5461         */
5462        @SuppressWarnings("unchecked") public TreeBuilderState<T> newSnapshot()
5463                throws SAXException {
5464            StackNode<T>[] listCopy = new StackNode[listPtr + 1];
5465            for (int i = 0; i < listCopy.length; i++) {
5466                StackNode<T> node = listOfActiveFormattingElements[i];
5467                if (node != null) {
5468                    StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
5469                            node.name, node.node, node.popName,
5470                            node.attributes.cloneAttributes(null)
5471                            // [NOCPP[
5472                            , node.getLocator()
5473                    // ]NOCPP]
5474                    );
5475                    listCopy[i] = newNode;
5476                } else {
5477                    listCopy[i] = null;
5478                }
5479            }
5480            StackNode<T>[] stackCopy = new StackNode[currentPtr + 1];
5481            for (int i = 0; i < stackCopy.length; i++) {
5482                StackNode<T> node = stack[i];
5483                int listIndex = findInListOfActiveFormattingElements(node);
5484                if (listIndex == -1) {
5485                    StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
5486                            node.name, node.node, node.popName,
5487                            null
5488                            // [NOCPP[
5489                            , node.getLocator()
5490                    // ]NOCPP]
5491                    );
5492                    stackCopy[i] = newNode;
5493                } else {
5494                    stackCopy[i] = listCopy[listIndex];
5495                    stackCopy[i].retain();
5496                }
5497            }
5498            return new StateSnapshot<T>(stackCopy, listCopy, formPointer, headPointer, deepTreeSurrogateParent, mode, originalMode, framesetOk, needToDropLF, quirks);
5499        }
5500    
5501        public boolean snapshotMatches(TreeBuilderState<T> snapshot) {
5502            StackNode<T>[] stackCopy = snapshot.getStack();
5503            int stackLen = snapshot.getStackLength();
5504            StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
5505            int listLen = snapshot.getListOfActiveFormattingElementsLength();
5506    
5507            if (stackLen != currentPtr + 1
5508                    || listLen != listPtr + 1
5509                    || formPointer != snapshot.getFormPointer()
5510                    || headPointer != snapshot.getHeadPointer()
5511                    || deepTreeSurrogateParent != snapshot.getDeepTreeSurrogateParent()
5512                    || mode != snapshot.getMode()
5513                    || originalMode != snapshot.getOriginalMode()
5514                    || framesetOk != snapshot.isFramesetOk()
5515                    || needToDropLF != snapshot.isNeedToDropLF()
5516                    || quirks != snapshot.isQuirks()) { // maybe just assert quirks
5517                return false;
5518            }
5519            for (int i = listLen - 1; i >= 0; i--) {
5520                if (listCopy[i] == null
5521                        && listOfActiveFormattingElements[i] == null) {
5522                    continue;
5523                } else if (listCopy[i] == null
5524                        || listOfActiveFormattingElements[i] == null) {
5525                    return false;
5526                }
5527                if (listCopy[i].node != listOfActiveFormattingElements[i].node) {
5528                    return false; // it's possible that this condition is overly
5529                                  // strict
5530                }
5531            }
5532            for (int i = stackLen - 1; i >= 0; i--) {
5533                if (stackCopy[i].node != stack[i].node) {
5534                    return false;
5535                }
5536            }
5537            return true;
5538        }
5539    
5540        @SuppressWarnings("unchecked") public void loadState(
5541                TreeBuilderState<T> snapshot, Interner interner)
5542                throws SAXException {
5543            StackNode<T>[] stackCopy = snapshot.getStack();
5544            int stackLen = snapshot.getStackLength();
5545            StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
5546            int listLen = snapshot.getListOfActiveFormattingElementsLength();
5547            
5548            for (int i = 0; i <= listPtr; i++) {
5549                if (listOfActiveFormattingElements[i] != null) {
5550                    listOfActiveFormattingElements[i].release();
5551                }
5552            }
5553            if (listOfActiveFormattingElements.length < listLen) {
5554                listOfActiveFormattingElements = new StackNode[listLen];
5555            }
5556            listPtr = listLen - 1;
5557    
5558            for (int i = 0; i <= currentPtr; i++) {
5559                stack[i].release();
5560            }
5561            if (stack.length < stackLen) {
5562                stack = new StackNode[stackLen];
5563            }
5564            currentPtr = stackLen - 1;
5565    
5566            for (int i = 0; i < listLen; i++) {
5567                StackNode<T> node = listCopy[i];
5568                if (node != null) {
5569                    StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
5570                            Portability.newLocalFromLocal(node.name, interner), node.node,
5571                            Portability.newLocalFromLocal(node.popName, interner),
5572                            node.attributes.cloneAttributes(null)
5573                            // [NOCPP[
5574                            , node.getLocator()
5575                    // ]NOCPP]
5576                    );
5577                    listOfActiveFormattingElements[i] = newNode;
5578                } else {
5579                    listOfActiveFormattingElements[i] = null;
5580                }
5581            }
5582            for (int i = 0; i < stackLen; i++) {
5583                StackNode<T> node = stackCopy[i];
5584                int listIndex = findInArray(node, listCopy);
5585                if (listIndex == -1) {
5586                    StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
5587                            Portability.newLocalFromLocal(node.name, interner), node.node,
5588                            Portability.newLocalFromLocal(node.popName, interner),
5589                            null
5590                            // [NOCPP[
5591                            , node.getLocator()
5592                    // ]NOCPP]       
5593                    );
5594                    stack[i] = newNode;
5595                } else {
5596                    stack[i] = listOfActiveFormattingElements[listIndex];
5597                    stack[i].retain();
5598                }
5599            }
5600            formPointer = snapshot.getFormPointer();
5601            headPointer = snapshot.getHeadPointer();
5602            deepTreeSurrogateParent = snapshot.getDeepTreeSurrogateParent();
5603            mode = snapshot.getMode();
5604            originalMode = snapshot.getOriginalMode();
5605            framesetOk = snapshot.isFramesetOk();
5606            needToDropLF = snapshot.isNeedToDropLF();
5607            quirks = snapshot.isQuirks();
5608        }
5609    
5610        private int findInArray(StackNode<T> node, StackNode<T>[] arr) {
5611            for (int i = listPtr; i >= 0; i--) {
5612                if (node == arr[i]) {
5613                    return i;
5614                }
5615            }
5616            return -1;
5617        }
5618    
5619        /**
5620         * @see nu.validator.htmlparser.impl.TreeBuilderState#getFormPointer()
5621         */
5622        public T getFormPointer() {
5623            return formPointer;
5624        }
5625    
5626        /**
5627         * Returns the headPointer.
5628         * 
5629         * @return the headPointer
5630         */
5631        public T getHeadPointer() {
5632            return headPointer;
5633        }
5634        
5635        /**
5636         * Returns the deepTreeSurrogateParent.
5637         * 
5638         * @return the deepTreeSurrogateParent
5639         */
5640        public T getDeepTreeSurrogateParent() {
5641            return deepTreeSurrogateParent;
5642        }
5643    
5644        /**
5645         * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElements()
5646         */
5647        public StackNode<T>[] getListOfActiveFormattingElements() {
5648            return listOfActiveFormattingElements;
5649        }
5650    
5651        /**
5652         * @see nu.validator.htmlparser.impl.TreeBuilderState#getStack()
5653         */
5654        public StackNode<T>[] getStack() {
5655            return stack;
5656        }
5657    
5658        /**
5659         * Returns the mode.
5660         * 
5661         * @return the mode
5662         */
5663        public int getMode() {
5664            return mode;
5665        }
5666    
5667        /**
5668         * Returns the originalMode.
5669         * 
5670         * @return the originalMode
5671         */
5672        public int getOriginalMode() {
5673            return originalMode;
5674        }
5675    
5676        /**
5677         * Returns the framesetOk.
5678         * 
5679         * @return the framesetOk
5680         */
5681        public boolean isFramesetOk() {
5682            return framesetOk;
5683        }
5684        
5685        /**
5686         * Returns the needToDropLF.
5687         * 
5688         * @return the needToDropLF
5689         */
5690        public boolean isNeedToDropLF() {
5691            return needToDropLF;
5692        }
5693    
5694        /**
5695         * Returns the quirks.
5696         * 
5697         * @return the quirks
5698         */
5699        public boolean isQuirks() {
5700            return quirks;
5701        }
5702    
5703        /**
5704         * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElementsLength()
5705         */
5706        public int getListOfActiveFormattingElementsLength() {
5707            return listPtr + 1;
5708        }
5709    
5710        /**
5711         * @see nu.validator.htmlparser.impl.TreeBuilderState#getStackLength()
5712         */
5713        public int getStackLength() {
5714            return currentPtr + 1;
5715        }
5716    
5717    }