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