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