View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.html;
16  
17  import static org.htmlunit.BrowserVersionFeatures.HTML_LAYER_TAG;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.Map;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.htmlunit.SgmlPage;
27  import org.htmlunit.cyberneko.xerces.util.XMLAttributesImpl;
28  import org.htmlunit.cyberneko.xerces.xni.QName;
29  import org.htmlunit.util.OrderedFastHashMap;
30  import org.xml.sax.Attributes;
31  
32  /**
33   * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
34   *
35   * Element factory which creates elements by calling the constructor on a
36   * given {@link HtmlElement} subclass.
37   * The constructor is expected to take 2 arguments of type
38   * {@link HtmlPage} and {@link java.util.Map}
39   * where the first one is the owning page of the element, the second one is a map
40   * holding the initial attributes for the element.
41   *
42   * @author Christian Sell
43   * @author Marc Guillemot
44   * @author Ahmed Ashour
45   * @author David K. Taylor
46   * @author Dmitri Zoubkov
47   * @author Ronald Brill
48   * @author Frank Danek
49   */
50  public class DefaultElementFactory implements ElementFactory {
51  
52      /** Logging support. */
53      private static final Log LOG = LogFactory.getLog(DefaultElementFactory.class);
54  
55      /**
56       * You can generate your own test cases by looking into
57       * org.htmlunit.source.ElementTestSource#generateTestForHtmlElements(String, String).
58       */
59      public static final List<String> SUPPORTED_TAGS_ = List.of(
60              HtmlAbbreviated.TAG_NAME, HtmlAcronym.TAG_NAME,
61              HtmlAnchor.TAG_NAME, HtmlAddress.TAG_NAME, HtmlArea.TAG_NAME,
62              HtmlArticle.TAG_NAME, HtmlAside.TAG_NAME, HtmlAudio.TAG_NAME,
63              HtmlBase.TAG_NAME, HtmlBaseFont.TAG_NAME,
64              HtmlBidirectionalIsolation.TAG_NAME, HtmlBidirectionalOverride.TAG_NAME, HtmlBig.TAG_NAME,
65              HtmlBlockQuote.TAG_NAME, HtmlBody.TAG_NAME, HtmlBold.TAG_NAME,
66              HtmlBreak.TAG_NAME, HtmlButton.TAG_NAME, HtmlCanvas.TAG_NAME, HtmlCaption.TAG_NAME,
67              HtmlCenter.TAG_NAME, HtmlCitation.TAG_NAME, HtmlCode.TAG_NAME,
68              HtmlData.TAG_NAME, HtmlDataList.TAG_NAME,
69              HtmlDefinition.TAG_NAME, HtmlDefinitionDescription.TAG_NAME,
70              HtmlDeletedText.TAG_NAME, HtmlDetails.TAG_NAME, HtmlDialog.TAG_NAME, HtmlDirectory.TAG_NAME,
71              HtmlDivision.TAG_NAME, HtmlDefinitionList.TAG_NAME,
72              HtmlDefinitionTerm.TAG_NAME, HtmlEmbed.TAG_NAME,
73              HtmlEmphasis.TAG_NAME,
74              HtmlFieldSet.TAG_NAME, HtmlFigureCaption.TAG_NAME, HtmlFigure.TAG_NAME,
75              HtmlFont.TAG_NAME, HtmlForm.TAG_NAME, HtmlFooter.TAG_NAME,
76              HtmlFrame.TAG_NAME, HtmlFrameSet.TAG_NAME,
77              HtmlHead.TAG_NAME, HtmlHeader.TAG_NAME,
78              HtmlHeading1.TAG_NAME, HtmlHeading2.TAG_NAME, HtmlHeading3.TAG_NAME,
79              HtmlHeading4.TAG_NAME, HtmlHeading5.TAG_NAME, HtmlHeading6.TAG_NAME,
80              HtmlHeadingGroup.TAG_NAME, HtmlHorizontalRule.TAG_NAME,
81              HtmlHtml.TAG_NAME,
82              HtmlInlineFrame.TAG_NAME, HtmlInlineQuotation.TAG_NAME,
83              HtmlImage.TAG_NAME, HtmlImage.TAG_NAME2,
84              HtmlInput.TAG_NAME,
85              HtmlInsertedText.TAG_NAME,
86              HtmlItalic.TAG_NAME,
87              HtmlKeyboard.TAG_NAME, HtmlLabel.TAG_NAME, HtmlLayer.TAG_NAME,
88              HtmlLegend.TAG_NAME, HtmlListing.TAG_NAME, HtmlListItem.TAG_NAME,
89              HtmlLink.TAG_NAME, HtmlMain.TAG_NAME, HtmlMap.TAG_NAME, HtmlMark.TAG_NAME, HtmlMarquee.TAG_NAME,
90              HtmlMenu.TAG_NAME, HtmlMeta.TAG_NAME, HtmlMeter.TAG_NAME,
91              HtmlNav.TAG_NAME,
92              HtmlNoBreak.TAG_NAME, HtmlNoEmbed.TAG_NAME, HtmlNoFrames.TAG_NAME,
93              HtmlNoLayer.TAG_NAME,
94              HtmlNoScript.TAG_NAME, HtmlObject.TAG_NAME, HtmlOrderedList.TAG_NAME,
95              HtmlOptionGroup.TAG_NAME, HtmlOption.TAG_NAME, HtmlOutput.TAG_NAME,
96              HtmlParagraph.TAG_NAME,
97              HtmlParameter.TAG_NAME, HtmlPicture.TAG_NAME, HtmlPlainText.TAG_NAME, HtmlPreformattedText.TAG_NAME,
98              HtmlProgress.TAG_NAME,
99              HtmlRb.TAG_NAME, HtmlRp.TAG_NAME, HtmlRt.TAG_NAME, HtmlRtc.TAG_NAME, HtmlRuby.TAG_NAME,
100             HtmlS.TAG_NAME, HtmlSample.TAG_NAME,
101             HtmlScript.TAG_NAME, HtmlSection.TAG_NAME, HtmlSelect.TAG_NAME, HtmlSlot.TAG_NAME, HtmlSmall.TAG_NAME,
102             HtmlSource.TAG_NAME, HtmlSpan.TAG_NAME,
103             HtmlStrike.TAG_NAME, HtmlStrong.TAG_NAME, HtmlStyle.TAG_NAME,
104             HtmlSubscript.TAG_NAME, HtmlSummary.TAG_NAME, HtmlSuperscript.TAG_NAME,
105             HtmlSvg.TAG_NAME,
106             HtmlTable.TAG_NAME, HtmlTableColumn.TAG_NAME, HtmlTableColumnGroup.TAG_NAME,
107             HtmlTableBody.TAG_NAME, HtmlTableDataCell.TAG_NAME, HtmlTableHeaderCell.TAG_NAME,
108             HtmlTableRow.TAG_NAME, HtmlTextArea.TAG_NAME, HtmlTableFooter.TAG_NAME,
109             HtmlTableHeader.TAG_NAME, HtmlTeletype.TAG_NAME, HtmlTemplate.TAG_NAME, HtmlTime.TAG_NAME,
110             HtmlTitle.TAG_NAME, HtmlTrack.TAG_NAME, HtmlUnderlined.TAG_NAME, HtmlUnorderedList.TAG_NAME,
111             HtmlVariable.TAG_NAME, HtmlVideo.TAG_NAME, HtmlWordBreak.TAG_NAME, HtmlExample.TAG_NAME);
112 
113     // for performance optimization
114     static final class OrderedFastHashMapWithLowercaseKeys<K, V> extends OrderedFastHashMap<K, V> {
115         OrderedFastHashMapWithLowercaseKeys(final int size) {
116             super(size);
117         }
118     }
119 
120     /**
121      * {@inheritDoc}
122      */
123     @Override
124     public HtmlElement createElement(final SgmlPage page, final String tagName, final Attributes attributes) {
125         return createElementNS(page, null, tagName, attributes);
126     }
127 
128     /**
129      * {@inheritDoc}
130      */
131     @Override
132     public HtmlElement createElementNS(final SgmlPage page, final String namespaceURI,
133             final String qualifiedName, final Attributes attributes) {
134         final Map<String, DomAttr> attributeMap = toMap(page, attributes);
135 
136         final HtmlElement element;
137         final String tagName;
138         final int colonIndex = qualifiedName.indexOf(':');
139         if (colonIndex == -1) {
140             tagName = qualifiedName.toLowerCase(Locale.ROOT);
141         }
142         else {
143             tagName = qualifiedName.substring(colonIndex + 1).toLowerCase(Locale.ROOT);
144         }
145 
146         switch (tagName) {
147             case HtmlAbbreviated.TAG_NAME:
148                 element = new HtmlAbbreviated(qualifiedName, page, attributeMap);
149                 break;
150 
151             case HtmlAcronym.TAG_NAME:
152                 element = new HtmlAcronym(qualifiedName, page, attributeMap);
153                 break;
154 
155             case HtmlAddress.TAG_NAME:
156                 element = new HtmlAddress(qualifiedName, page, attributeMap);
157                 break;
158 
159             case HtmlAnchor.TAG_NAME:
160                 element = new HtmlAnchor(qualifiedName, page, attributeMap);
161                 break;
162 
163             case HtmlArea.TAG_NAME:
164                 element = new HtmlArea(qualifiedName, page, attributeMap);
165                 break;
166 
167             case HtmlArticle.TAG_NAME:
168                 element = new HtmlArticle(qualifiedName, page, attributeMap);
169                 break;
170 
171             case HtmlAside.TAG_NAME:
172                 element = new HtmlAside(qualifiedName, page, attributeMap);
173                 break;
174 
175             case HtmlAudio.TAG_NAME:
176                 element = new HtmlAudio(qualifiedName, page, attributeMap);
177                 break;
178 
179             case HtmlBase.TAG_NAME:
180                 element = new HtmlBase(qualifiedName, page, attributeMap);
181                 break;
182 
183             case HtmlBaseFont.TAG_NAME:
184                 element = new HtmlBaseFont(qualifiedName, page, attributeMap);
185                 break;
186 
187             case HtmlBidirectionalIsolation.TAG_NAME:
188                 element = new HtmlBidirectionalIsolation(qualifiedName, page, attributeMap);
189                 break;
190 
191             case HtmlBidirectionalOverride.TAG_NAME:
192                 element = new HtmlBidirectionalOverride(qualifiedName, page, attributeMap);
193                 break;
194 
195             case HtmlBig.TAG_NAME:
196                 element = new HtmlBig(qualifiedName, page, attributeMap);
197                 break;
198 
199             case HtmlBlockQuote.TAG_NAME:
200                 element = new HtmlBlockQuote(qualifiedName, page, attributeMap);
201                 break;
202 
203             case HtmlBody.TAG_NAME:
204                 element = new HtmlBody(qualifiedName, page, attributeMap, false);
205                 // Force script object creation now to forward onXXX handlers to window.
206                 if (page instanceof HtmlPage) {
207                     if (page.getWebClient().isJavaScriptEngineEnabled()) {
208                         element.getScriptableObject();
209                     }
210                 }
211                 break;
212 
213             case HtmlBold.TAG_NAME:
214                 element = new HtmlBold(qualifiedName, page, attributeMap);
215                 break;
216 
217             case HtmlBreak.TAG_NAME:
218                 element = new HtmlBreak(qualifiedName, page, attributeMap);
219                 break;
220 
221             case HtmlButton.TAG_NAME:
222                 element = new HtmlButton(qualifiedName, page, attributeMap);
223                 break;
224 
225             case HtmlCanvas.TAG_NAME:
226                 element = new HtmlCanvas(qualifiedName, page, attributeMap);
227                 break;
228 
229             case HtmlCaption.TAG_NAME:
230                 element = new HtmlCaption(qualifiedName, page, attributeMap);
231                 break;
232 
233             case HtmlCenter.TAG_NAME:
234                 element = new HtmlCenter(qualifiedName, page, attributeMap);
235                 break;
236 
237             case HtmlCitation.TAG_NAME:
238                 element = new HtmlCitation(qualifiedName, page, attributeMap);
239                 break;
240 
241             case HtmlCode.TAG_NAME:
242                 element = new HtmlCode(qualifiedName, page, attributeMap);
243                 break;
244 
245             case HtmlData.TAG_NAME:
246                 element = new HtmlData(qualifiedName, page, attributeMap);
247                 break;
248 
249             case HtmlDataList.TAG_NAME:
250                 element = new HtmlDataList(qualifiedName, page, attributeMap);
251                 break;
252 
253             case HtmlDefinition.TAG_NAME:
254                 element = new HtmlDefinition(qualifiedName, page, attributeMap);
255                 break;
256 
257             case HtmlDefinitionDescription.TAG_NAME:
258                 element = new HtmlDefinitionDescription(qualifiedName, page, attributeMap);
259                 break;
260 
261             case HtmlDefinitionList.TAG_NAME:
262                 element = new HtmlDefinitionList(qualifiedName, page, attributeMap);
263                 break;
264 
265             case HtmlDefinitionTerm.TAG_NAME:
266                 element = new HtmlDefinitionTerm(qualifiedName, page, attributeMap);
267                 break;
268 
269             case HtmlDeletedText.TAG_NAME:
270                 element = new HtmlDeletedText(qualifiedName, page, attributeMap);
271                 break;
272 
273             case HtmlDetails.TAG_NAME:
274                 element = new HtmlDetails(qualifiedName, page, attributeMap);
275                 break;
276 
277             case HtmlDialog.TAG_NAME:
278                 element = new HtmlDialog(qualifiedName, page, attributeMap);
279                 break;
280 
281             case HtmlDirectory.TAG_NAME:
282                 element = new HtmlDirectory(qualifiedName, page, attributeMap);
283                 break;
284 
285             case HtmlDivision.TAG_NAME:
286                 element = new HtmlDivision(qualifiedName, page, attributeMap);
287                 break;
288 
289             case HtmlEmbed.TAG_NAME:
290                 element = new HtmlEmbed(qualifiedName, page, attributeMap);
291                 break;
292 
293             case HtmlEmphasis.TAG_NAME:
294                 element = new HtmlEmphasis(qualifiedName, page, attributeMap);
295                 break;
296 
297             case HtmlExample.TAG_NAME:
298                 element = new HtmlExample(qualifiedName, page, attributeMap);
299                 break;
300 
301             case HtmlFieldSet.TAG_NAME:
302                 element = new HtmlFieldSet(qualifiedName, page, attributeMap);
303                 break;
304 
305             case HtmlFigure.TAG_NAME:
306                 element = new HtmlFigure(qualifiedName, page, attributeMap);
307                 break;
308 
309             case HtmlFigureCaption.TAG_NAME:
310                 element = new HtmlFigureCaption(qualifiedName, page, attributeMap);
311                 break;
312 
313             case HtmlFont.TAG_NAME:
314                 element = new HtmlFont(qualifiedName, page, attributeMap);
315                 break;
316 
317             case HtmlForm.TAG_NAME:
318                 element = new HtmlForm(qualifiedName, page, attributeMap);
319                 break;
320 
321             case HtmlFooter.TAG_NAME:
322                 element = new HtmlFooter(qualifiedName, page, attributeMap);
323                 break;
324 
325             case HtmlFrame.TAG_NAME:
326                 if (attributeMap != null) {
327                     final DomAttr srcAttribute = attributeMap.get(DomElement.SRC_ATTRIBUTE);
328                     if (srcAttribute != null) {
329                         srcAttribute.setValue(srcAttribute.getValue().trim());
330                     }
331                 }
332                 element = new HtmlFrame(qualifiedName, page, attributeMap);
333                 break;
334 
335             case HtmlFrameSet.TAG_NAME:
336                 element = new HtmlFrameSet(qualifiedName, page, attributeMap);
337                 break;
338 
339             case HtmlHead.TAG_NAME:
340                 element = new HtmlHead(qualifiedName, page, attributeMap);
341                 break;
342 
343             case HtmlHeader.TAG_NAME:
344                 element = new HtmlHeader(qualifiedName, page, attributeMap);
345                 break;
346 
347             case HtmlHeading1.TAG_NAME:
348                 element = new HtmlHeading1(qualifiedName, page, attributeMap);
349                 break;
350 
351             case HtmlHeading2.TAG_NAME:
352                 element = new HtmlHeading2(qualifiedName, page, attributeMap);
353                 break;
354 
355             case HtmlHeading3.TAG_NAME:
356                 element = new HtmlHeading3(qualifiedName, page, attributeMap);
357                 break;
358 
359             case HtmlHeading4.TAG_NAME:
360                 element = new HtmlHeading4(qualifiedName, page, attributeMap);
361                 break;
362 
363             case HtmlHeading5.TAG_NAME:
364                 element = new HtmlHeading5(qualifiedName, page, attributeMap);
365                 break;
366 
367             case HtmlHeading6.TAG_NAME:
368                 element = new HtmlHeading6(qualifiedName, page, attributeMap);
369                 break;
370 
371             case HtmlHeadingGroup.TAG_NAME:
372                 element = new HtmlHeadingGroup(qualifiedName, page, attributeMap);
373                 break;
374 
375             case HtmlHorizontalRule.TAG_NAME:
376                 element = new HtmlHorizontalRule(qualifiedName, page, attributeMap);
377                 break;
378 
379             case HtmlHtml.TAG_NAME:
380                 element = new HtmlHtml(qualifiedName, page, attributeMap);
381                 break;
382 
383             case HtmlImage.TAG_NAME:
384             case HtmlImage.TAG_NAME2:
385                 element = new HtmlImage(qualifiedName, page, attributeMap);
386                 break;
387 
388             case HtmlInlineFrame.TAG_NAME:
389                 if (attributeMap != null) {
390                     final DomAttr srcAttribute = attributeMap.get(DomElement.SRC_ATTRIBUTE);
391                     if (srcAttribute != null) {
392                         srcAttribute.setValue(srcAttribute.getValue().trim());
393                     }
394                 }
395                 element = new HtmlInlineFrame(qualifiedName, page, attributeMap);
396                 break;
397 
398             case HtmlInlineQuotation.TAG_NAME:
399                 element = new HtmlInlineQuotation(qualifiedName, page, attributeMap);
400                 break;
401 
402             case HtmlInput.TAG_NAME:
403                 element = createInputElement(qualifiedName, page, attributeMap);
404                 break;
405 
406             case HtmlInsertedText.TAG_NAME:
407                 element = new HtmlInsertedText(qualifiedName, page, attributeMap);
408                 break;
409 
410             case HtmlItalic.TAG_NAME:
411                 element = new HtmlItalic(qualifiedName, page, attributeMap);
412                 break;
413 
414             case HtmlKeyboard.TAG_NAME:
415                 element = new HtmlKeyboard(qualifiedName, page, attributeMap);
416                 break;
417 
418             case HtmlLabel.TAG_NAME:
419                 element = new HtmlLabel(qualifiedName, page, attributeMap);
420                 break;
421 
422             case HtmlLayer.TAG_NAME:
423                 if (page.getWebClient().getBrowserVersion().hasFeature(HTML_LAYER_TAG)) {
424                     element = new HtmlLayer(qualifiedName, page, attributeMap);
425                 }
426                 else {
427                     element = UnknownElementFactory.INSTANCE
428                             .createElementNS(page, namespaceURI, qualifiedName, attributes);
429                 }
430                 break;
431 
432             case HtmlLegend.TAG_NAME:
433                 element = new HtmlLegend(qualifiedName, page, attributeMap);
434                 break;
435 
436             case HtmlLink.TAG_NAME:
437                 element = new HtmlLink(qualifiedName, page, attributeMap);
438                 break;
439 
440             case HtmlListing.TAG_NAME:
441                 element = new HtmlListing(qualifiedName, page, attributeMap);
442                 break;
443 
444             case HtmlListItem.TAG_NAME:
445                 element = new HtmlListItem(qualifiedName, page, attributeMap);
446                 break;
447 
448             case HtmlMain.TAG_NAME:
449                 element = new HtmlMain(qualifiedName, page, attributeMap);
450                 break;
451 
452             case HtmlMap.TAG_NAME:
453                 element = new HtmlMap(qualifiedName, page, attributeMap);
454                 break;
455 
456             case HtmlMark.TAG_NAME:
457                 element = new HtmlMark(qualifiedName, page, attributeMap);
458                 break;
459 
460             case HtmlMarquee.TAG_NAME:
461                 element = new HtmlMarquee(qualifiedName, page, attributeMap);
462                 break;
463 
464             case HtmlMenu.TAG_NAME:
465                 element = new HtmlMenu(qualifiedName, page, attributeMap);
466                 break;
467 
468             case HtmlMeta.TAG_NAME:
469                 element = new HtmlMeta(qualifiedName, page, attributeMap);
470                 break;
471 
472             case HtmlMeter.TAG_NAME:
473                 element = new HtmlMeter(qualifiedName, page, attributeMap);
474                 break;
475 
476             case HtmlNav.TAG_NAME:
477                 element = new HtmlNav(qualifiedName, page, attributeMap);
478                 break;
479 
480             case HtmlNoBreak.TAG_NAME:
481                 element = new HtmlNoBreak(qualifiedName, page, attributeMap);
482                 break;
483 
484             case HtmlNoEmbed.TAG_NAME:
485                 element = new HtmlNoEmbed(qualifiedName, page, attributeMap);
486                 break;
487 
488             case HtmlNoFrames.TAG_NAME:
489                 element = new HtmlNoFrames(qualifiedName, page, attributeMap);
490                 break;
491 
492             case HtmlNoLayer.TAG_NAME:
493                 if (page.getWebClient().getBrowserVersion().hasFeature(HTML_LAYER_TAG)) {
494                     element = new HtmlNoLayer(qualifiedName, page, attributeMap);
495                 }
496                 else {
497                     element = UnknownElementFactory.INSTANCE
498                             .createElementNS(page, namespaceURI, qualifiedName, attributes);
499                 }
500                 break;
501 
502             case HtmlNoScript.TAG_NAME:
503                 element = new HtmlNoScript(qualifiedName, page, attributeMap);
504                 break;
505 
506             case HtmlObject.TAG_NAME:
507                 element = new HtmlObject(qualifiedName, page, attributeMap);
508                 break;
509 
510             case HtmlOption.TAG_NAME:
511                 element = new HtmlOption(qualifiedName, page, attributeMap);
512                 break;
513 
514             case HtmlOptionGroup.TAG_NAME:
515                 element = new HtmlOptionGroup(qualifiedName, page, attributeMap);
516                 break;
517 
518             case HtmlOrderedList.TAG_NAME:
519                 element = new HtmlOrderedList(qualifiedName, page, attributeMap);
520                 break;
521 
522             case HtmlOutput.TAG_NAME:
523                 element = new HtmlOutput(qualifiedName, page, attributeMap);
524                 break;
525 
526             case HtmlParagraph.TAG_NAME:
527                 element = new HtmlParagraph(qualifiedName, page, attributeMap);
528                 break;
529 
530             case HtmlParameter.TAG_NAME:
531                 element = new HtmlParameter(qualifiedName, page, attributeMap);
532                 break;
533 
534             case HtmlPicture.TAG_NAME:
535                 element = new HtmlPicture(qualifiedName, page, attributeMap);
536                 break;
537 
538             case HtmlPlainText.TAG_NAME:
539                 element = new HtmlPlainText(qualifiedName, page, attributeMap);
540                 break;
541 
542             case HtmlPreformattedText.TAG_NAME:
543                 element = new HtmlPreformattedText(qualifiedName, page, attributeMap);
544                 break;
545 
546             case HtmlProgress.TAG_NAME:
547                 element = new HtmlProgress(qualifiedName, page, attributeMap);
548                 break;
549 
550             case HtmlRb.TAG_NAME:
551                 element = new HtmlRb(qualifiedName, page, attributeMap);
552                 break;
553 
554             case HtmlRp.TAG_NAME:
555                 element = new HtmlRp(qualifiedName, page, attributeMap);
556                 break;
557 
558             case HtmlRt.TAG_NAME:
559                 element = new HtmlRt(qualifiedName, page, attributeMap);
560                 break;
561 
562             case HtmlRtc.TAG_NAME:
563                 element = new HtmlRtc(qualifiedName, page, attributeMap);
564                 break;
565 
566             case HtmlRuby.TAG_NAME:
567                 element = new HtmlRuby(qualifiedName, page, attributeMap);
568                 break;
569 
570             case HtmlS.TAG_NAME:
571                 element = new HtmlS(qualifiedName, page, attributeMap);
572                 break;
573 
574             case HtmlSample.TAG_NAME:
575                 element = new HtmlSample(qualifiedName, page, attributeMap);
576                 break;
577 
578             case HtmlScript.TAG_NAME:
579                 element = new HtmlScript(qualifiedName, page, attributeMap);
580                 break;
581 
582             case HtmlSection.TAG_NAME:
583                 element = new HtmlSection(qualifiedName, page, attributeMap);
584                 break;
585 
586             case HtmlSelect.TAG_NAME:
587                 element = new HtmlSelect(qualifiedName, page, attributeMap);
588                 break;
589 
590             case HtmlSmall.TAG_NAME:
591                 element = new HtmlSmall(qualifiedName, page, attributeMap);
592                 break;
593 
594             case HtmlSlot.TAG_NAME:
595                 element = new HtmlSlot(qualifiedName, page, attributeMap);
596                 break;
597 
598             case HtmlSource.TAG_NAME:
599                 element = new HtmlSource(qualifiedName, page, attributeMap);
600                 break;
601 
602             case HtmlSpan.TAG_NAME:
603                 element = new HtmlSpan(qualifiedName, page, attributeMap);
604                 break;
605 
606             case HtmlStrike.TAG_NAME:
607                 element = new HtmlStrike(qualifiedName, page, attributeMap);
608                 break;
609 
610             case HtmlStrong.TAG_NAME:
611                 element = new HtmlStrong(qualifiedName, page, attributeMap);
612                 break;
613 
614             case HtmlStyle.TAG_NAME:
615                 element = new HtmlStyle(qualifiedName, page, attributeMap);
616                 break;
617 
618             case HtmlSubscript.TAG_NAME:
619                 element = new HtmlSubscript(qualifiedName, page, attributeMap);
620                 break;
621 
622             case HtmlSummary.TAG_NAME:
623                 element = new HtmlSummary(qualifiedName, page, attributeMap);
624                 break;
625 
626             case HtmlSuperscript.TAG_NAME:
627                 element = new HtmlSuperscript(qualifiedName, page, attributeMap);
628                 break;
629 
630             case HtmlSvg.TAG_NAME:
631                 element = new HtmlSvg(qualifiedName, page, attributeMap);
632                 break;
633 
634             case HtmlTable.TAG_NAME:
635                 element = new HtmlTable(qualifiedName, page, attributeMap);
636                 break;
637 
638             case HtmlTableBody.TAG_NAME:
639                 element = new HtmlTableBody(qualifiedName, page, attributeMap);
640                 break;
641 
642             case HtmlTableColumn.TAG_NAME:
643                 element = new HtmlTableColumn(qualifiedName, page, attributeMap);
644                 break;
645 
646             case HtmlTableColumnGroup.TAG_NAME:
647                 element = new HtmlTableColumnGroup(qualifiedName, page, attributeMap);
648                 break;
649 
650             case HtmlTableDataCell.TAG_NAME:
651                 element = new HtmlTableDataCell(qualifiedName, page, attributeMap);
652                 break;
653 
654             case HtmlTableFooter.TAG_NAME:
655                 element = new HtmlTableFooter(qualifiedName, page, attributeMap);
656                 break;
657 
658             case HtmlTableHeader.TAG_NAME:
659                 element = new HtmlTableHeader(qualifiedName, page, attributeMap);
660                 break;
661 
662             case HtmlTableHeaderCell.TAG_NAME:
663                 element = new HtmlTableHeaderCell(qualifiedName, page, attributeMap);
664                 break;
665 
666             case HtmlTableRow.TAG_NAME:
667                 element = new HtmlTableRow(qualifiedName, page, attributeMap);
668                 break;
669 
670             case HtmlTeletype.TAG_NAME:
671                 element = new HtmlTeletype(qualifiedName, page, attributeMap);
672                 break;
673 
674             case HtmlTemplate.TAG_NAME:
675                 element = new HtmlTemplate(qualifiedName, page, attributeMap);
676                 break;
677 
678             case HtmlTextArea.TAG_NAME:
679                 element = new HtmlTextArea(qualifiedName, page, attributeMap);
680                 break;
681 
682             case HtmlTime.TAG_NAME:
683                 element = new HtmlTime(qualifiedName, page, attributeMap);
684                 break;
685 
686             case HtmlTitle.TAG_NAME:
687                 element = new HtmlTitle(qualifiedName, page, attributeMap);
688                 break;
689 
690             case HtmlTrack.TAG_NAME:
691                 element = new HtmlTrack(qualifiedName, page, attributeMap);
692                 break;
693 
694             case HtmlUnderlined.TAG_NAME:
695                 element = new HtmlUnderlined(qualifiedName, page, attributeMap);
696                 break;
697 
698             case HtmlUnorderedList.TAG_NAME:
699                 element = new HtmlUnorderedList(qualifiedName, page, attributeMap);
700                 break;
701 
702             case HtmlVariable.TAG_NAME:
703                 element = new HtmlVariable(qualifiedName, page, attributeMap);
704                 break;
705 
706             case HtmlVideo.TAG_NAME:
707                 element = new HtmlVideo(qualifiedName, page, attributeMap);
708                 break;
709 
710             case HtmlWordBreak.TAG_NAME:
711                 element = new HtmlWordBreak(qualifiedName, page, attributeMap);
712                 break;
713 
714             default:
715                 throw new IllegalStateException("Cannot find HtmlElement for " + qualifiedName);
716         }
717 
718         return element;
719     }
720 
721     /**
722      * Converts {@link Attributes} into the map needed by {@link HtmlElement}s.
723      *
724      * @param page the page which contains the specified attributes
725      * @param attributes the SAX attributes
726      * @return the map of attribute values for {@link HtmlElement}s
727      */
728     static Map<String, DomAttr> toMap(final SgmlPage page, final Attributes attributes) {
729         // it is ok to return null here, the element ctor's are able to deal with that
730         if (attributes == null) {
731             return null;
732         }
733 
734         final int length = attributes.getLength();
735         if (length == 0) {
736             return null;
737         }
738 
739         final Map<String, DomAttr> attributeMap = new OrderedFastHashMapWithLowercaseKeys<>(length);
740 
741         // small performance optimization if we know the attributes we can avoid some index lookups
742         if (attributes instanceof XMLAttributesImpl impl) {
743             final ArrayList<XMLAttributesImpl.Attribute> attribs = impl.getAttributes();
744             for (final XMLAttributesImpl.Attribute attribute : attribs) {
745                 final QName qName = attribute.getQName();
746                 final String name = qName.getRawname();
747 
748                 String namespaceURI = qName.getUri();
749                 if (namespaceURI != null && namespaceURI.isEmpty()) {
750                     namespaceURI = null;
751                 }
752 
753                 DomAttr attr = new DomAttr(page, namespaceURI, name, attribute.getValue(), true);
754                 attr = attributeMap.put(name, attr);
755 
756                 // browsers consider only first attribute (ex: <div id='foo' id='something'>...</div>)
757                 // for performance reasons we do not check for the existence of the key first
758                 // because this is the unusual case
759                 if (attr != null) {
760                     attributeMap.put(name, attr);
761                 }
762             }
763 
764             return attributeMap;
765         }
766 
767         for (int i = 0; i < length; i++) {
768             final String qName = attributes.getQName(i);
769 
770             String namespaceURI = attributes.getURI(i);
771 
772             if (namespaceURI != null && namespaceURI.isEmpty()) {
773                 namespaceURI = null;
774             }
775 
776             DomAttr attr = new DomAttr(page, namespaceURI, qName, attributes.getValue(i), true);
777             attr = attributeMap.put(qName, attr);
778 
779             // browsers consider only first attribute (ex: <div id='foo' id='something'>...</div>)
780             // for performance reasons we do not check for the existence of the key first
781             // because this is the unusual case
782             if (attr != null) {
783                 attributeMap.put(qName, attr);
784             }
785         }
786 
787         return attributeMap;
788     }
789 
790     private static HtmlElement createInputElement(final String qualifiedName, final SgmlPage page,
791                                                 final Map<String, DomAttr> attributeMap) {
792         String type = "";
793         if (attributeMap != null) {
794             for (final Map.Entry<String, DomAttr> entry : attributeMap.entrySet()) {
795                 if (DomElement.TYPE_ATTRIBUTE.equalsIgnoreCase(entry.getKey())) {
796                     type = entry.getValue().getValue();
797                 }
798             }
799         }
800 
801         final HtmlInput result = switch (type.toLowerCase(Locale.ROOT)) {
802             // This not an illegal value, as it defaults to "text"
803             // cf http://www.w3.org/TR/REC-html40/interact/forms.html#adef-type-INPUT
804             // and the common browsers seem to treat it as a "text" input so we will as well.
805             case "", "text" -> new HtmlTextInput(qualifiedName, page, attributeMap);
806             case "submit" -> new HtmlSubmitInput(qualifiedName, page, attributeMap);
807             case "checkbox" -> new HtmlCheckBoxInput(qualifiedName, page, attributeMap);
808             case "radio" -> new HtmlRadioButtonInput(qualifiedName, page, attributeMap);
809             case "hidden" -> new HtmlHiddenInput(qualifiedName, page, attributeMap);
810             case "password" -> new HtmlPasswordInput(qualifiedName, page, attributeMap);
811             case "image" -> new HtmlImageInput(qualifiedName, page, attributeMap);
812             case "reset" -> new HtmlResetInput(qualifiedName, page, attributeMap);
813             case "button" -> new HtmlButtonInput(qualifiedName, page, attributeMap);
814             case "file" -> new HtmlFileInput(qualifiedName, page, attributeMap);
815             case "color" -> new HtmlColorInput(qualifiedName, page, attributeMap);
816             case "date" -> new HtmlDateInput(qualifiedName, page, attributeMap);
817             case "datetime-local" -> new HtmlDateTimeLocalInput(qualifiedName, page, attributeMap);
818             case "email" -> new HtmlEmailInput(qualifiedName, page, attributeMap);
819             case "month" -> new HtmlMonthInput(qualifiedName, page, attributeMap);
820             case "number" -> new HtmlNumberInput(qualifiedName, page, attributeMap);
821             case "range" -> new HtmlRangeInput(qualifiedName, page, attributeMap);
822             case "search" -> new HtmlSearchInput(qualifiedName, page, attributeMap);
823             case "tel" -> new HtmlTelInput(qualifiedName, page, attributeMap);
824             case "time" -> new HtmlTimeInput(qualifiedName, page, attributeMap);
825             case "url" -> new HtmlUrlInput(qualifiedName, page, attributeMap);
826             case "week" -> new HtmlWeekInput(qualifiedName, page, attributeMap);
827             default -> {
828                 if (LOG.isInfoEnabled()) {
829                     LOG.info("Bad input type: \"" + type + "\", creating a text input");
830                 }
831                 yield new HtmlTextInput(qualifiedName, page, attributeMap);
832             }
833         };
834         return result;
835     }
836 }