1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.devacfr.maven.skins.reflow.snippet;
17
18 import static java.util.Objects.requireNonNull;
19
20 import com.google.common.base.MoreObjects;
21 import com.google.common.base.Strings;
22 import com.google.common.collect.Maps;
23 import com.google.common.collect.Sets;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.stream.Collectors;
27 import javax.annotation.Nonnull;
28 import javax.annotation.Nullable;
29 import org.jsoup.nodes.Attribute;
30 import org.jsoup.nodes.Attributes;
31 import org.jsoup.nodes.Element;
32 import org.jsoup.nodes.Node;
33 import org.jsoup.nodes.TextNode;
34 import org.jsoup.parser.Tag;
35
36
37
38
39
40
41
42
43
44 public class Component<T extends Component<T>> {
45
46 private final static Set<String> knownTags = Sets.newHashSet("svg");
47
48
49 private final Map<String, String> attributes = Maps.newHashMap();
50
51
52 private Component<?> parent;
53
54
55 private final Components children = new Components();
56
57
58 private final Map<String, Components> childrenMap = Maps.newHashMap();
59
60
61 private final Node node;
62
63
64
65
66
67
68
69
70 public static Component<?> createComponent(@Nonnull final Node node, final Component<?> parent) {
71 return new Component<>(node).withParent(parent).addAttributes(node.attributes());
72 }
73
74
75
76
77
78 protected Component(@Nonnull final Node node) {
79 this.node = requireNonNull(node);
80 }
81
82
83
84
85 public String getName() {
86 return node.nodeName();
87 }
88
89
90
91
92 public boolean isHtmlTag() {
93 return node instanceof TextNode || Tag.isKnownTag(node.nodeName()) || knownTags.contains(node.nodeName());
94 }
95
96
97
98
99 public String getHtml() {
100 if (!isHtmlTag()) {
101 if (children.isEmpty()) {
102 return "";
103 } else {
104 return children.html();
105 }
106 } else {
107 return this.node.outerHtml();
108 }
109 }
110
111
112
113
114 public String getOwnHtml() {
115 if (!isHtmlTag()) {
116 return null;
117 } else {
118 return this.node.outerHtml();
119 }
120 }
121
122
123
124
125 public Component<?> getParent() {
126 return parent;
127 }
128
129 @Nullable protected Element getElement() {
130 if (this.node instanceof Element) {
131 return (Element) this.node;
132 }
133 return null;
134 }
135
136
137
138
139
140
141 public Object get(@Nonnull final String name) {
142 requireNonNull(name);
143 String key = name.toLowerCase();
144
145 if (this.attributes.containsKey(key)) {
146 return this.attributes.get(key);
147 } else {
148
149
150 if (key.endsWith("s") && !this.childrenMap.containsKey(key)) {
151 key = key.substring(0, key.length() - 1);
152 if (this.childrenMap.containsKey(key)) {
153 return this.childrenMap.get(key);
154 }
155 } else if (this.childrenMap.containsKey(key)) {
156 final Components value = this.childrenMap.get(key);
157
158 if (value.size() > 1) {
159 return value;
160 }
161 return value.first();
162 }
163 }
164 return null;
165 }
166
167 public Map<String, String> getAttrs() {
168 return this.attributes;
169 }
170
171 public String getAttribute(String name) {
172 if (!this.attributes.containsKey(name)) {
173 return null;
174 }
175 return this.attributes.get(name);
176 }
177
178 public boolean hasAttribute(String name) {
179 return this.attributes.containsKey(name);
180 }
181
182
183
184
185 public Map<String, String> getAriaAttributes() {
186 return this.attributes.entrySet()
187 .stream()
188 .filter(e -> e.getKey().startsWith("aria-"))
189 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
190 }
191
192
193
194
195 public Map<String, String> getDataAttributes() {
196 return this.attributes.entrySet()
197 .stream()
198 .filter(e -> e.getKey().startsWith("data-"))
199 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
200 }
201
202
203
204
205 public Components getChildren() {
206 return children;
207 }
208
209 public Components getChildren(final String name) {
210 final String key = requireNonNull(name).toLowerCase();
211 if (this.childrenMap.containsKey(key)) {
212 return this.childrenMap.get(key);
213 }
214 return Components.empty();
215 }
216
217 public T addChild(final Component<?> component) {
218 final String key = component.getName();
219 Components value = null;
220 if (!this.childrenMap.containsKey(key)) {
221 value = new Components();
222 this.childrenMap.put(key, value);
223 } else {
224 value = this.childrenMap.get(key);
225 }
226 value.add(component);
227 this.children.add(component);
228 return self();
229 }
230
231 protected T withParent(final Component<?> parent) {
232 this.parent = parent;
233 return self();
234 }
235
236 @Nonnull
237 protected SnippetComponent<?> getRootParent() {
238 Component<?> parent = this.parent;
239 while (!(parent instanceof SnippetComponent<?>)) {
240 parent = parent.parent;
241 }
242 return (SnippetComponent<?>) parent;
243 }
244
245
246
247
248
249
250 protected T addAttributes(@Nonnull final Attributes attrs) {
251 attrs.asList().stream().forEach(this::addAttribute);
252 return self();
253 }
254
255
256
257
258
259
260 protected T addAttribute(@Nonnull final Attribute attr) {
261 this.attributes.put(requireNonNull(attr.getKey()).toLowerCase(),
262 Strings.isNullOrEmpty(attr.getValue()) ? "true" : attr.getValue());
263 return self();
264 }
265
266
267
268
269 @SuppressWarnings("unchecked")
270 protected T self() {
271 return (T) this;
272 }
273
274
275
276
277 @Override
278 public String toString() {
279 return MoreObjects.toStringHelper(this)
280 .add("name", this.getName())
281 .add("isHtmlTag", this.isHtmlTag())
282 .add("attributes", this.attributes)
283 .add("children", this.children.stream().map((cpt) -> cpt.getName()).collect(Collectors.toList()))
284 .toString();
285 }
286 }