1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.svg;
16
17 import java.util.HashMap;
18 import java.util.LinkedHashMap;
19 import java.util.Map;
20 import java.util.concurrent.ConcurrentHashMap;
21
22 import org.htmlunit.SgmlPage;
23 import org.htmlunit.html.DomAttr;
24 import org.htmlunit.html.DomElement;
25 import org.htmlunit.html.ElementFactory;
26 import org.htmlunit.util.StringUtils;
27 import org.xml.sax.Attributes;
28
29
30
31
32
33
34
35
36
37 public class SvgElementFactory implements ElementFactory {
38
39 private static final Class<?>[] CLASSES_ = {SvgAltGlyph.class, SvgAltGlyphDef.class, SvgAltGlyphItem.class,
40 SvgAnchor.class, SvgAnimate.class, SvgAnimateColor.class, SvgAnimateMotion.class, SvgAnimateTransform.class,
41 SvgCircle.class, SvgClipPath.class, SvgColorProfile.class, SvgCursor.class, SvgDefs.class, SvgDesc.class,
42 SvgEllipse.class, SvgFeBlend.class, SvgFeColorMatrix.class, SvgFeComponentTransfer.class,
43 SvgFeComposite.class, SvgFeConvolveMatrix.class, SvgFeDiffuseLighting.class, SvgFeDisplacementMap.class,
44 SvgFeDistantLight.class, SvgFeFlood.class, SvgFeFuncA.class, SvgFeFuncB.class, SvgFeFuncG.class,
45 SvgFeFuncR.class, SvgFeGaussianBlur.class, SvgFeImage.class, SvgFeMerge.class, SvgFeMergeNode.class,
46 SvgFeMorphology.class, SvgFeOffset.class, SvgFePointLight.class, SvgFeSpecularLighting.class,
47 SvgFeSpotLight.class, SvgFeTile.class, SvgFeTurbulence.class, SvgFilter.class, SvgFont.class,
48 SvgFontFace.class, SvgFontFaceFormat.class, SvgFontFaceName.class, SvgFontFaceSrc.class,
49 SvgFontFaceURI.class, SvgForeignObject.class, SvgGlyph.class, SvgGlyphRef.class, SvgGroup.class,
50 SvgHKern.class, SvgImage.class, SvgLine.class, SvgLinearGradient.class, SvgMarker.class, SvgMask.class,
51 SvgMetadata.class, SvgMissingGlyph.class, SvgMPath.class, SvgPath.class, SvgPattern.class, SvgPolygon.class,
52 SvgPolyline.class, SvgRadialGradient.class, SvgRect.class, SvgScript.class, SvgSet.class, SvgStop.class,
53 SvgStyle.class, SvgSwitch.class, SvgSymbol.class, SvgText.class, SvgTextPath.class,
54 SvgTitle.class, SvgTRef.class, SvgTSpan.class, SvgUse.class, SvgView.class, SvgVKern.class
55 };
56
57 private static final Map<String, Class<?>> ELEMENTS_ = new ConcurrentHashMap<>();
58
59
60
61
62
63
64
65 private static final Map<String, String> CASE_SENSITIVE_TAG_NAMES_ = new HashMap<>();
66
67 static {
68 for (final String name : new String[] {
69 "altGlyph", "altGlyphDef", "altGlyphItem", "animateColor", "animateMotion", "animateTransform",
70 "clipPath", "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix",
71 "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA",
72 "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology",
73 "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence",
74 "foreignObject", "glyphRef", "linearGradient", "radialGradient", "textPath"}) {
75 CASE_SENSITIVE_TAG_NAMES_.put(StringUtils.toRootLowerCase(name), name);
76 }
77 }
78
79 static {
80 try {
81 for (final Class<?> klass : CLASSES_) {
82 final String key = klass.getField("TAG_NAME").get(null).toString();
83 ELEMENTS_.put(StringUtils.toRootLowerCase(key), klass);
84 }
85 }
86 catch (final Exception e) {
87 throw new IllegalStateException(e);
88 }
89 }
90
91
92
93
94 @Override
95 public DomElement createElement(final SgmlPage page, final String tagName, final Attributes attributes) {
96 throw new IllegalStateException("SVG.createElement not yet implemented!");
97 }
98
99
100
101
102 @Override
103 public DomElement createElementNS(final SgmlPage page, final String namespaceURI, final String qualifiedName,
104 final Attributes attributes) {
105
106 final Map<String, DomAttr> attributeMap = toMap(page, attributes);
107
108
109 String tagNameLC = StringUtils.toRootLowerCase(qualifiedName);
110 if (tagNameLC.indexOf(':') != -1) {
111 tagNameLC = tagNameLC.substring(tagNameLC.indexOf(':') + 1);
112 }
113 DomElement element = null;
114
115 final Class<?> klass = ELEMENTS_.get(tagNameLC);
116 if (klass != null) {
117 try {
118 element = (DomElement) klass.getDeclaredConstructors()[0]
119 .newInstance(namespaceURI, qualifiedName, page, attributeMap);
120 }
121 catch (final Exception e) {
122 throw new IllegalStateException(e);
123 }
124 }
125 if (element == null) {
126 element = new SvgElement(namespaceURI, qualifiedName, page, attributeMap);
127 }
128 return element;
129 }
130
131 private static Map<String, DomAttr> toMap(final SgmlPage page, final Attributes attributes) {
132 Map<String, DomAttr> attributeMap = null;
133 if (attributes != null) {
134 attributeMap = new LinkedHashMap<>(attributes.getLength());
135 for (int i = 0; i < attributes.getLength(); i++) {
136 final String qName = attributes.getQName(i);
137
138 if (!attributeMap.containsKey(qName)) {
139 String namespaceURI = attributes.getURI(i);
140 if (namespaceURI != null && namespaceURI.isEmpty()) {
141 namespaceURI = null;
142 }
143 final DomAttr newAttr = new DomAttr(page, namespaceURI, qName, attributes.getValue(i), true);
144 attributeMap.put(qName, newAttr);
145 }
146 }
147 }
148 return attributeMap;
149 }
150
151
152
153
154
155
156
157
158
159
160
161 public static String adjustTagNameForHtmlParser(final String tagName) {
162 final String tagNameLC = StringUtils.toRootLowerCase(tagName);
163 return CASE_SENSITIVE_TAG_NAMES_.getOrDefault(tagNameLC, tagNameLC);
164 }
165
166
167
168
169
170
171 public boolean isSupported(final String tagNameLowerCase) {
172 return ELEMENTS_.containsKey(tagNameLowerCase);
173 }
174 }