diff --git a/src/main/java/ch/digitalfondue/jfiveparse/Common.java b/src/main/java/ch/digitalfondue/jfiveparse/Common.java index 8cf93df..5622bfd 100644 --- a/src/main/java/ch/digitalfondue/jfiveparse/Common.java +++ b/src/main/java/ch/digitalfondue/jfiveparse/Common.java @@ -305,131 +305,6 @@ static boolean isEndTagNamed(int tokenType, int namedID, int tagNameID) { return tokenType == TreeConstructor.TT_END_TAG && namedID == tagNameID; } - static int tagNameToID(String tagName) { - if (tagName == null) { - return 0; - } - return switch (tagName) { - case "address" -> ELEMENT_ADDRESS_ID; - case "applet" -> ELEMENT_APPLET_ID; - case "area" -> ELEMENT_AREA_ID; - case "article" -> ELEMENT_ARTICLE_ID; - case "aside" -> ELEMENT_ASIDE_ID; - case "base" -> ELEMENT_BASE_ID; - case "basefont" -> ELEMENT_BASEFONT_ID; - case "bgsound" -> ELEMENT_BGSOUND_ID; - case "blockquote" -> ELEMENT_BLOCKQUOTE_ID; - case "body" -> ELEMENT_BODY_ID; - case "br" -> ELEMENT_BR_ID; - case "button" -> ELEMENT_BUTTON_ID; - case "caption" -> ELEMENT_CAPTION_ID; - case "center" -> ELEMENT_CENTER_ID; - case "col" -> ELEMENT_COL_ID; - case "colgroup" -> ELEMENT_COLGROUP_ID; - case "dd" -> ELEMENT_DD_ID; - case "details" -> ELEMENT_DETAILS_ID; - case "dir" -> ELEMENT_DIR_ID; - case "div" -> ELEMENT_DIV_ID; - case "dl" -> ELEMENT_DL_ID; - case "dt" -> ELEMENT_DT_ID; - case "embed" -> ELEMENT_EMBED_ID; - case "fieldset" -> ELEMENT_FIELDSET_ID; - case "figcaption" -> ELEMENT_FIGCAPTION_ID; - case "figure" -> ELEMENT_FIGURE_ID; - case "footer" -> ELEMENT_FOOTER_ID; - case "form" -> ELEMENT_FORM_ID; - case "frame" -> ELEMENT_FRAME_ID; - case "frameset" -> ELEMENT_FRAMESET_ID; - case "h1" -> ELEMENT_H1_ID; - case "h2" -> ELEMENT_H2_ID; - case "h3" -> ELEMENT_H3_ID; - case "h4" -> ELEMENT_H4_ID; - case "h5" -> ELEMENT_H5_ID; - case "h6" -> ELEMENT_H6_ID; - case "head" -> ELEMENT_HEAD_ID; - case "header" -> ELEMENT_HEADER_ID; - case "hgroup" -> ELEMENT_HGROUP_ID; - case "hr" -> ELEMENT_HR_ID; - case "html" -> ELEMENT_HTML_ID; - case "iframe" -> ELEMENT_IFRAME_ID; - case "img" -> ELEMENT_IMG_ID; - case "input" -> ELEMENT_INPUT_ID; - case "li" -> ELEMENT_LI_ID; - case "link" -> ELEMENT_LINK_ID; - case "listing" -> ELEMENT_LISTING_ID; - case "main" -> ELEMENT_MAIN_ID; - case "marquee" -> ELEMENT_MARQUEE_ID; - case "menu" -> ELEMENT_MENU_ID; - case "meta" -> ELEMENT_META_ID; - case "nav" -> ELEMENT_NAV_ID; - case "noembed" -> ELEMENT_NOEMBED_ID; - case "noframes" -> ELEMENT_NOFRAMES_ID; - case "noscript" -> ELEMENT_NOSCRIPT_ID; - case "object" -> ELEMENT_OBJECT_ID; - case "ol" -> ELEMENT_OL_ID; - case "p" -> ELEMENT_P_ID; - case "param" -> ELEMENT_PARAM_ID; - case "plaintext" -> ELEMENT_PLAINTEXT_ID; - case "pre" -> ELEMENT_PRE_ID; - case "script" -> ELEMENT_SCRIPT_ID; - case "section" -> ELEMENT_SECTION_ID; - case "select" -> ELEMENT_SELECT_ID; - case "source" -> ELEMENT_SOURCE_ID; - case "style" -> ELEMENT_STYLE_ID; - case "summary" -> ELEMENT_SUMMARY_ID; - case "table" -> ELEMENT_TABLE_ID; - case "tbody" -> ELEMENT_TBODY_ID; - case "td" -> ELEMENT_TD_ID; - case "template" -> ELEMENT_TEMPLATE_ID; - case "textarea" -> ELEMENT_TEXTAREA_ID; - case "tfoot" -> ELEMENT_TFOOT_ID; - case "th" -> ELEMENT_TH_ID; - case "thead" -> ELEMENT_THEAD_ID; - case "title" -> ELEMENT_TITLE_ID; - case "tr" -> ELEMENT_TR_ID; - case "track" -> ELEMENT_TRACK_ID; - case "ul" -> ELEMENT_UL_ID; - case "wbr" -> ELEMENT_WBR_ID; - case "xmp" -> ELEMENT_XMP_ID; - // - case "optgroup" -> ELEMENT_OPTGROUP_ID; - case "option" -> ELEMENT_OPTION_ID; - case "rb" -> ELEMENT_RB_ID; - case "rp" -> ELEMENT_RP_ID; - case "rt" -> ELEMENT_RT_ID; - case "rtc" -> ELEMENT_RTC_ID; - // - case "a" -> ELEMENT_A_ID; - // - - case "dialog" -> ELEMENT_DIALOG_ID; - case "search" -> ELEMENT_SEARCH_ID; - case "b" -> ELEMENT_B_ID; - case "big" -> ELEMENT_BIG_ID; - case "code" -> ELEMENT_CODE_ID; - case "em" -> ELEMENT_EM_ID; - case "font" -> ELEMENT_FONT_ID; - case "i" -> ELEMENT_I_ID; - case "s" -> ELEMENT_S_ID; - case "small" -> ELEMENT_SMALL_ID; - case "strike" -> ELEMENT_STRIKE_ID; - case "strong" -> ELEMENT_STRONG_ID; - case "tt" -> ELEMENT_TT_ID; - case "u" -> ELEMENT_U_ID; - case "nobr" -> ELEMENT_NO_BR_ID; - case "keygen" -> ELEMENT_KEYGEN_ID; - case "image" -> ELEMENT_IMAGE_ID; - case "math" -> ELEMENT_MATH_ID; - case "svg" -> ELEMENT_SVG_ID; - case "ruby" -> ELEMENT_RUBY_ID; - case "span" -> ELEMENT_SPAN_ID; - case "sub" -> ELEMENT_SUB_ID; - case "sup" -> ELEMENT_SUP_ID; - case "var" -> ELEMENT_VAR_ID; - default -> 0; - }; - } - // this order is the SPECIAL_ELEMENTS_HTML container from 1 to 81 static final int ELEMENT_ADDRESS_ID = 1; static final int ELEMENT_APPLET_ID = 2; @@ -548,6 +423,411 @@ static int tagNameToID(String tagName) { static final int ELEMENT_SUP_ID = 111; static final int ELEMENT_VAR_ID = 112; + // must have the same exact index + static final String[] CANONICAL_TAG_NAMES = new String[]{ + null, + "address", // 1 + "applet", + "area", + "article", + "aside", + "base", + "basefont", + "bgsound", + "blockquote", + "body", + "br", + "button", + "caption", + "center", + "col", + "colgroup", + "dd", + "details", + "dir", + "div", + "dl", + "dt", + "embed", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "frame", + "frameset", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "head", + "header", + "hgroup", + "hr", + "html", + "iframe", + "img", + "input", + "li", + "link", + "listing", + "main", + "marquee", + "menu", + "meta", + "nav", + "noembed", + "noframes", + "noscript", + "object", + "ol", + "p", + "param", + "plaintext", + "pre", + "script", + "section", + "select", + "source", + "style", + "summary", + "table", + "tbody", + "td", + "template", + "textarea", + "tfoot", + "th", + "thead", + "title", + "tr", + "track", + "ul", + "wbr", + "xmp", + "optgroup", + "option", + "rb", + "rp", + "rt", + "rtc", + "a", + "dialog", + "search", + "b", + "big", + "code", + "em", + "font", + "i", + "s", + "small", + "strike", + "strong", + "tt", + "u", + "nobr", + "keygen", + "image", + "math", + "svg", + "ruby", + "span", + "sub", + "sup", + "var" + }; + + private static boolean equalsIgnoreCase(char[] buff, int pos, String expected) { + if (pos != expected.length()) { + return false; + } + for (int i = 0; i < pos; i++) { + if ((char) toLowerCase(buff[i]) != expected.charAt(i)) { + return false; + } + } + return true; + } + + static int lookupTagNameID(ResizableCharBuilder builder) { + int pos = builder.pos(); + if (pos < 1 || pos > 10) { + return 0; + } + char[] buff = builder.getBuff(); + char c0 = (char) toLowerCase(buff[0]); + switch (pos) { + case 1: + switch (c0) { + case 'a': return ELEMENT_A_ID; + case 'b': return ELEMENT_B_ID; + case 'i': return ELEMENT_I_ID; + case 'p': return ELEMENT_P_ID; + case 's': return ELEMENT_S_ID; + case 'u': return ELEMENT_U_ID; + default: return 0; + } + case 2: + switch (c0) { + case 'b': return equalsIgnoreCase(buff, pos, "br") ? ELEMENT_BR_ID : 0; + case 'd': { + char c1 = (char) toLowerCase(buff[1]); + switch (c1) { + case 'd': return ELEMENT_DD_ID; + case 'l': return ELEMENT_DL_ID; + case 't': return ELEMENT_DT_ID; + default: return 0; + } + } + case 'e': return equalsIgnoreCase(buff, pos, "em") ? ELEMENT_EM_ID : 0; + case 'h': { + char c1 = (char) toLowerCase(buff[1]); + switch (c1) { + case '1': return ELEMENT_H1_ID; + case '2': return ELEMENT_H2_ID; + case '3': return ELEMENT_H3_ID; + case '4': return ELEMENT_H4_ID; + case '5': return ELEMENT_H5_ID; + case '6': return ELEMENT_H6_ID; + case 'r': return ELEMENT_HR_ID; + default: return 0; + } + } + case 'l': return equalsIgnoreCase(buff, pos, "li") ? ELEMENT_LI_ID : 0; + case 'r': { + char c1 = (char) toLowerCase(buff[1]); + switch (c1) { + case 'b': return ELEMENT_RB_ID; + case 'p': return ELEMENT_RP_ID; + case 't': return ELEMENT_RT_ID; + default: return 0; + } + } + case 't': { + char c1 = (char) toLowerCase(buff[1]); + switch (c1) { + case 'd': return ELEMENT_TD_ID; + case 'h': return ELEMENT_TH_ID; + case 'r': return ELEMENT_TR_ID; + case 't': return ELEMENT_TT_ID; + default: return 0; + } + } + case 'o': return equalsIgnoreCase(buff, pos, "ol") ? ELEMENT_OL_ID : 0; + case 'u': return equalsIgnoreCase(buff, pos, "ul") ? ELEMENT_UL_ID : 0; + default: return 0; + } + case 3: + switch (c0) { + case 'b': return equalsIgnoreCase(buff, pos, "big") ? ELEMENT_BIG_ID : 0; + case 'c': return equalsIgnoreCase(buff, pos, "col") ? ELEMENT_COL_ID : 0; + case 'd': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'i') { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'r') return ELEMENT_DIR_ID; + if (c2 == 'v') return ELEMENT_DIV_ID; + } + return 0; + } + case 'i': return equalsIgnoreCase(buff, pos, "img") ? ELEMENT_IMG_ID : 0; + case 'n': return equalsIgnoreCase(buff, pos, "nav") ? ELEMENT_NAV_ID : 0; + case 'p': return equalsIgnoreCase(buff, pos, "pre") ? ELEMENT_PRE_ID : 0; + case 'r': return equalsIgnoreCase(buff, pos, "rtc") ? ELEMENT_RTC_ID : 0; + case 's': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'u') { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'b') return ELEMENT_SUB_ID; + if (c2 == 'p') return ELEMENT_SUP_ID; + } else if (c1 == 'v' && (char) toLowerCase(buff[2]) == 'g') { + return ELEMENT_SVG_ID; + } + return 0; + } + case 'v': return equalsIgnoreCase(buff, pos, "var") ? ELEMENT_VAR_ID : 0; + case 'w': return equalsIgnoreCase(buff, pos, "wbr") ? ELEMENT_WBR_ID : 0; + case 'x': return equalsIgnoreCase(buff, pos, "xmp") ? ELEMENT_XMP_ID : 0; + default: return 0; + } + case 4: + switch (c0) { + case 'a': return equalsIgnoreCase(buff, pos, "area") ? ELEMENT_AREA_ID : 0; + case 'b': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'a') return equalsIgnoreCase(buff, pos, "base") ? ELEMENT_BASE_ID : 0; + if (c1 == 'o') return equalsIgnoreCase(buff, pos, "body") ? ELEMENT_BODY_ID : 0; + return 0; + } + case 'c': return equalsIgnoreCase(buff, pos, "code") ? ELEMENT_CODE_ID : 0; + case 'f': { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'r') return equalsIgnoreCase(buff, pos, "form") ? ELEMENT_FORM_ID : 0; + if (c2 == 'n') return equalsIgnoreCase(buff, pos, "font") ? ELEMENT_FONT_ID : 0; + return 0; + } + case 'h': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'e') return equalsIgnoreCase(buff, pos, "head") ? ELEMENT_HEAD_ID : 0; + if (c1 == 't') return equalsIgnoreCase(buff, pos, "html") ? ELEMENT_HTML_ID : 0; + return 0; + } + case 'l': return equalsIgnoreCase(buff, pos, "link") ? ELEMENT_LINK_ID : 0; + case 'm': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'a') { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'i') return equalsIgnoreCase(buff, pos, "main") ? ELEMENT_MAIN_ID : 0; + if (c2 == 't') return equalsIgnoreCase(buff, pos, "math") ? ELEMENT_MATH_ID : 0; + } else if (c1 == 'e') { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'n') return equalsIgnoreCase(buff, pos, "menu") ? ELEMENT_MENU_ID : 0; + if (c2 == 't') return equalsIgnoreCase(buff, pos, "meta") ? ELEMENT_META_ID : 0; + } + return 0; + } + case 'n': return equalsIgnoreCase(buff, pos, "nobr") ? ELEMENT_NO_BR_ID : 0; + case 'r': return equalsIgnoreCase(buff, pos, "ruby") ? ELEMENT_RUBY_ID : 0; + case 's': return equalsIgnoreCase(buff, pos, "span") ? ELEMENT_SPAN_ID : 0; + default: return 0; + } + case 5: + switch (c0) { + case 'a': return equalsIgnoreCase(buff, pos, "aside") ? ELEMENT_ASIDE_ID : 0; + case 'e': return equalsIgnoreCase(buff, pos, "embed") ? ELEMENT_EMBED_ID : 0; + case 'f': return equalsIgnoreCase(buff, pos, "frame") ? ELEMENT_FRAME_ID : 0; + case 'i': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'n') return equalsIgnoreCase(buff, pos, "input") ? ELEMENT_INPUT_ID : 0; + if (c1 == 'm') return equalsIgnoreCase(buff, pos, "image") ? ELEMENT_IMAGE_ID : 0; + return 0; + } + case 'p': return equalsIgnoreCase(buff, pos, "param") ? ELEMENT_PARAM_ID : 0; + case 's': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'm') return equalsIgnoreCase(buff, pos, "small") ? ELEMENT_SMALL_ID : 0; + if (c1 == 't') return equalsIgnoreCase(buff, pos, "style") ? ELEMENT_STYLE_ID : 0; + return 0; + } + case 't': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'a') return equalsIgnoreCase(buff, pos, "table") ? ELEMENT_TABLE_ID : 0; + if (c1 == 'b') return equalsIgnoreCase(buff, pos, "tbody") ? ELEMENT_TBODY_ID : 0; + if (c1 == 'f') return equalsIgnoreCase(buff, pos, "tfoot") ? ELEMENT_TFOOT_ID : 0; + if (c1 == 'h') return equalsIgnoreCase(buff, pos, "thead") ? ELEMENT_THEAD_ID : 0; + if (c1 == 'i') return equalsIgnoreCase(buff, pos, "title") ? ELEMENT_TITLE_ID : 0; + if (c1 == 'r') return equalsIgnoreCase(buff, pos, "track") ? ELEMENT_TRACK_ID : 0; + return 0; + } + default: return 0; + } + case 6: + switch (c0) { + case 'a': return equalsIgnoreCase(buff, pos, "applet") ? ELEMENT_APPLET_ID : 0; + case 'b': return equalsIgnoreCase(buff, pos, "button") ? ELEMENT_BUTTON_ID : 0; + case 'c': return equalsIgnoreCase(buff, pos, "center") ? ELEMENT_CENTER_ID : 0; + case 'd': return equalsIgnoreCase(buff, pos, "dialog") ? ELEMENT_DIALOG_ID : 0; + case 'f': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'i') return equalsIgnoreCase(buff, pos, "figure") ? ELEMENT_FIGURE_ID : 0; + if (c1 == 'o') return equalsIgnoreCase(buff, pos, "footer") ? ELEMENT_FOOTER_ID : 0; + return 0; + } + case 'h': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'e') return equalsIgnoreCase(buff, pos, "header") ? ELEMENT_HEADER_ID : 0; + if (c1 == 'g') return equalsIgnoreCase(buff, pos, "hgroup") ? ELEMENT_HGROUP_ID : 0; + return 0; + } + case 'i': return equalsIgnoreCase(buff, pos, "iframe") ? ELEMENT_IFRAME_ID : 0; + case 'k': return equalsIgnoreCase(buff, pos, "keygen") ? ELEMENT_KEYGEN_ID : 0; + case 'o': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'b') return equalsIgnoreCase(buff, pos, "object") ? ELEMENT_OBJECT_ID : 0; + if (c1 == 'p') return equalsIgnoreCase(buff, pos, "option") ? ELEMENT_OPTION_ID : 0; + return 0; + } + case 's': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'c') return equalsIgnoreCase(buff, pos, "script") ? ELEMENT_SCRIPT_ID : 0; + if (c1 == 'e') { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'a') return equalsIgnoreCase(buff, pos, "search") ? ELEMENT_SEARCH_ID : 0; + if (c2 == 'l') return equalsIgnoreCase(buff, pos, "select") ? ELEMENT_SELECT_ID : 0; + } + if (c1 == 'o') return equalsIgnoreCase(buff, pos, "source") ? ELEMENT_SOURCE_ID : 0; + if (c1 == 't') { + char c3 = (char) toLowerCase(buff[3]); + if (c3 == 'i') return equalsIgnoreCase(buff, pos, "strike") ? ELEMENT_STRIKE_ID : 0; + if (c3 == 'o') return equalsIgnoreCase(buff, pos, "strong") ? ELEMENT_STRONG_ID : 0; + } + return 0; + } + default: return 0; + } + case 7: + switch (c0) { + case 'a': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'd') return equalsIgnoreCase(buff, pos, "address") ? ELEMENT_ADDRESS_ID : 0; + if (c1 == 'r') return equalsIgnoreCase(buff, pos, "article") ? ELEMENT_ARTICLE_ID : 0; + return 0; + } + case 'b': return equalsIgnoreCase(buff, pos, "bgsound") ? ELEMENT_BGSOUND_ID : 0; + case 'c': return equalsIgnoreCase(buff, pos, "caption") ? ELEMENT_CAPTION_ID : 0; + case 'd': return equalsIgnoreCase(buff, pos, "details") ? ELEMENT_DETAILS_ID : 0; + case 'l': return equalsIgnoreCase(buff, pos, "listing") ? ELEMENT_LISTING_ID : 0; + case 'm': return equalsIgnoreCase(buff, pos, "marquee") ? ELEMENT_MARQUEE_ID : 0; + case 'n': return equalsIgnoreCase(buff, pos, "noembed") ? ELEMENT_NOEMBED_ID : 0; + case 's': { + char c1 = (char) toLowerCase(buff[1]); + if (c1 == 'e') return equalsIgnoreCase(buff, pos, "section") ? ELEMENT_SECTION_ID : 0; + if (c1 == 'u') return equalsIgnoreCase(buff, pos, "summary") ? ELEMENT_SUMMARY_ID : 0; + return 0; + } + default: return 0; + } + case 8: + switch (c0) { + case 'b': return equalsIgnoreCase(buff, pos, "basefont") ? ELEMENT_BASEFONT_ID : 0; + case 'c': return equalsIgnoreCase(buff, pos, "colgroup") ? ELEMENT_COLGROUP_ID : 0; + case 'f': { + int c1 = toLowerCase(buff[1]); + if (c1 == 'i') return equalsIgnoreCase(buff, pos, "fieldset") ? ELEMENT_FIELDSET_ID : 0; + if (c1 == 'r') return equalsIgnoreCase(buff, pos, "frameset") ? ELEMENT_FRAMESET_ID : 0; + return 0; + } + case 'n': { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'f') return equalsIgnoreCase(buff, pos, "noframes") ? ELEMENT_NOFRAMES_ID : 0; + if (c2 == 's') return equalsIgnoreCase(buff, pos, "noscript") ? ELEMENT_NOSCRIPT_ID : 0; + return 0; + } + case 'o': return equalsIgnoreCase(buff, pos, "optgroup") ? ELEMENT_OPTGROUP_ID : 0; + case 't': { + char c2 = (char) toLowerCase(buff[2]); + if (c2 == 'm') return equalsIgnoreCase(buff, pos, "template") ? ELEMENT_TEMPLATE_ID : 0; + if (c2 == 'x') return equalsIgnoreCase(buff, pos, "textarea") ? ELEMENT_TEXTAREA_ID : 0; + return 0; + } + default: return 0; + } + case 9: + return equalsIgnoreCase(buff, pos, "plaintext") ? ELEMENT_PLAINTEXT_ID : 0; + case 10: + switch (c0) { + case 'b': return equalsIgnoreCase(buff, pos, "blockquote") ? ELEMENT_BLOCKQUOTE_ID : 0; + case 'f': return equalsIgnoreCase(buff, pos, "figcaption") ? ELEMENT_FIGCAPTION_ID : 0; + default: return 0; + } + default: + return 0; + } + } + static boolean isSpecialCategory(Element element) { String nodeName = element.nodeName; int nodeNameID = element.nodeNameID; diff --git a/src/main/java/ch/digitalfondue/jfiveparse/Element.java b/src/main/java/ch/digitalfondue/jfiveparse/Element.java index ae14dbb..f584e74 100644 --- a/src/main/java/ch/digitalfondue/jfiveparse/Element.java +++ b/src/main/java/ch/digitalfondue/jfiveparse/Element.java @@ -69,7 +69,7 @@ public Element(String name, String nameSpace, Attributes attributes) { // #document // |
// due to the fact that we are losing the case of foreignObject: to evaluate when we can lowercase safely - this(name, Common.tagNameToID(name), name, nameSpace, Node.toNamespaceId(nameSpace), attributes); + this(name, Common.lookupTagNameID(new ResizableCharBuilder(name)), name, nameSpace, Node.toNamespaceId(nameSpace), attributes); } Element(String name, diff --git a/src/main/java/ch/digitalfondue/jfiveparse/ResizableCharBuilder.java b/src/main/java/ch/digitalfondue/jfiveparse/ResizableCharBuilder.java index c7da74a..a802a7a 100644 --- a/src/main/java/ch/digitalfondue/jfiveparse/ResizableCharBuilder.java +++ b/src/main/java/ch/digitalfondue/jfiveparse/ResizableCharBuilder.java @@ -32,11 +32,25 @@ final class ResizableCharBuilder { buff = new char[16]; } + public ResizableCharBuilder(String value) { + set(value); + } + + char[] getBuff() { + return buff; + } + // reset and set the given string void set(String s) { buff = s.toCharArray(); pos = buff.length; containsUpperCase = false; + for (int i = 0; i < pos; i++) { + if (Common.isUpperCaseASCIILetter(buff[i])) { + containsUpperCase = true; + break; + } + } } void reset() { @@ -56,18 +70,17 @@ void append(char c) { if (pos == buff.length) { buff = Arrays.copyOf(buff, buff.length * 2 + 2); } + if (Common.isUpperCaseASCIILetter(c)) { + containsUpperCase = true; + } buff[pos++] = c; } String toLowerCase() { - containsUpperCase = false; - for (int i = 0; i < pos; i++) { - if (Common.isUpperCaseASCIILetter(buff[i])) { - containsUpperCase = true; - return lowerCaseInternal(); - } + if (!containsUpperCase) { + return toString(); } - return toString(); + return lowerCaseInternal(); } private String lowerCaseInternal() { @@ -105,11 +118,18 @@ void append(char[] c, int offset, int length) { if (pos + length >= buff.length) { buff = Arrays.copyOf(buff, Math.max(pos + length, buff.length * 2 + 2)); } + for (int i = offset; i < offset + length; i++) { + if (Common.isUpperCaseASCIILetter(c[i])) { + containsUpperCase = true; + break; + } + } System.arraycopy(c, offset, buff, pos, length); pos += length; } void append(ResizableCharBuilder other) { + containsUpperCase |= other.containsUpperCase; append(other.buff, 0, other.pos); } } diff --git a/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructor.java b/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructor.java index f2bd72e..5cb1c0f 100644 --- a/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructor.java +++ b/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructor.java @@ -123,11 +123,16 @@ void setTokenizer(Tokenizer tokenizer) { this.disableInTableTextForsterParenting = disableInTableTextForsterParenting; } - void setTagName(String lowerCasedTagName) { - this.tagName = lowerCasedTagName; - this.tagNameID = Common.tagNameToID(lowerCasedTagName); + void setTagName(ResizableCharBuilder name) { + int id = Common.lookupTagNameID(name); + if (id != 0) { + this.tagNameID = id; + this.tagName = Common.CANONICAL_TAG_NAMES[id]; + } else { + this.tagNameID = 0; + this.tagName = name.toLowerCase(); + } } - // Element getAdjustedCurrentNode() { @@ -848,14 +853,14 @@ void emitEOF() { } void emitEndTagToken(ResizableCharBuilder name) { - setTagName(name.toLowerCase()); + setTagName(name); tokenType = TT_END_TAG; dispatch(); } void emitStartTagToken(ResizableCharBuilder name, Attributes attrs, boolean selfClosing) { // - setTagName(name.toLowerCase()); + setTagName(name); this.originalTagName = name.containsUpperCase ? name.toString() : this.tagName; // this.attrs = attrs; diff --git a/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructorHandlers.java b/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructorHandlers.java index 9243373..6abfb1a 100644 --- a/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructorHandlers.java +++ b/src/main/java/ch/digitalfondue/jfiveparse/TreeConstructorHandlers.java @@ -1490,9 +1490,11 @@ private static void startTextarea(TreeConstructor treeConstructor) { treeConstructor.ignoreCharacterTokenLF(); } + private static final ResizableCharBuilder IMG = new ResizableCharBuilder("img"); + private static void startImage(TreeConstructor treeConstructor) { treeConstructor.emitParseError(); - treeConstructor.setTagName("img"); + treeConstructor.setTagName(IMG); treeConstructor.dispatch(); } @@ -2207,7 +2209,7 @@ private static void anyOtherStartTag(String tagName, TreeConstructor treeConstru adjustForeignAttributes(treeConstructor.getAttributes()); - treeConstructor.insertElementToken(tagName, Common.tagNameToID(tagName), currentNode.getNamespaceURI(), currentNode.namespaceID, treeConstructor.getAttributes()); + treeConstructor.insertElementToken(tagName, Common.lookupTagNameID(new ResizableCharBuilder(tagName)), currentNode.getNamespaceURI(), currentNode.namespaceID, treeConstructor.getAttributes()); if (treeConstructor.isSelfClosing()) { // we don't execute scripts