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 com.google.common.collect.Iterables;
19 import com.google.common.collect.Lists;
20 import java.io.IOException;
21 import java.util.List;
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24 import org.devacfr.maven.skins.reflow.JsoupUtils;
25 import org.jsoup.nodes.Comment;
26 import org.jsoup.nodes.Document;
27 import org.jsoup.nodes.Element;
28 import org.jsoup.nodes.Node;
29 import org.jsoup.nodes.TextNode;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33
34
35
36
37
38
39 public abstract class Processor {
40
41 private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class);
42
43
44 protected final SnippetParser parser;
45
46
47
48
49
50
51
52 public Processor(final SnippetParser parser) {
53 this.parser = parser;
54 }
55
56
57
58
59
60
61
62 public void parse(final ComponentToken token) {
63 if (LOGGER.isDebugEnabled()) {
64 LOGGER.debug("Parse Token: {}", token);
65 }
66 switch (token.tag()) {
67 case empty:
68 this.handleEmptyTag(token);
69 break;
70
71 case start:
72 this.handleStartTag(token);
73 parser.push(token);
74 parser.parse();
75 break;
76
77 case end:
78 final ComponentToken startToken = parser.pop();
79 final ComponentToken endToken = token;
80 this.handleCloseTag(startToken, endToken);
81 break;
82 case html:
83 this.handleHtmlTag(token);
84 break;
85 default:
86 throw new SnippetParseException("unknown token tag " + token.tag());
87 }
88 }
89
90
91
92
93
94
95
96
97
98
99
100 protected abstract void appendChildrenToHtml(Node node, Appendable writer) throws IOException;
101
102
103
104
105
106
107
108 protected void handleStartTag(final ComponentToken token) {
109
110 }
111
112
113
114
115
116
117
118
119
120 protected void handleCloseTag(final ComponentToken startToken, final ComponentToken endToken) {
121
122 }
123
124
125
126
127
128
129
130 protected void handleEmptyTag(final ComponentToken token) {
131
132 }
133
134
135
136
137
138
139
140 protected void handleHtmlTag(final ComponentToken token) {
141
142 }
143
144
145
146
147
148
149
150
151
152
153 protected Element convertToHtml(@Nonnull final ComponentToken startToken, @Nullable final ComponentToken endToken) {
154 final Element startElement = startToken.getElement();
155 final Node previousElement = startElement.previousSibling();
156 Element endElement = null;
157 if (endToken != null) {
158 endElement = endToken.getElement();
159 }
160 final Document doc = JsoupUtils.createHtmlDocument("");
161 final Element tmp = doc.body();
162 final Node parent = startElement.parentNode();
163
164 final StringBuilder html = new StringBuilder(ComponentResolver.convertElementTextToHtml(startElement));
165 final List<Node> nodesToRemove = Lists.newArrayList();
166 nodesToRemove.add(startElement);
167 if (endElement != null) {
168 boolean startCopy = false;
169
170 for (final Node n : parent.childNodes()) {
171 if (n.equals(endElement)) {
172 nodesToRemove.add(n);
173 break;
174 }
175 if (startCopy) {
176 try {
177 appendChildrenToHtml(n, html);
178 } catch (final IOException e) {
179 throw new RuntimeException(e.getMessage(), e);
180 }
181 nodesToRemove.add(n);
182 }
183 if (n.equals(startElement)) {
184 startCopy = true;
185 }
186 }
187 html.append(ComponentResolver.convertElementTextToHtml(endElement));
188 }
189 tmp.html(html.toString());
190 final Element component = tmp.children().first();
191 final Element parentElement = startElement.parent();
192
193 if (previousElement != null) {
194 previousElement.after(component);
195 } else {
196 if (parentElement.children().first() != null) {
197 parentElement.children().first().before(component);
198 } else {
199 parentElement.children().add(component);
200 }
201 }
202 nodesToRemove.forEach(Node::remove);
203 return component;
204 }
205
206
207
208
209
210
211
212 public static class WebComponentProcessor extends Processor {
213
214
215
216
217
218
219
220 public WebComponentProcessor(final SnippetParser parser) {
221 super(parser);
222 }
223
224
225
226
227 @Override
228 protected void appendChildrenToHtml(final Node node, final Appendable writer) throws IOException {
229 if (node instanceof Element) {
230 final Element el = (Element) node;
231
232 if ("div".equals(el.tagName()) && el.hasClass("source")) {
233 writer.append(el.text());
234 } else {
235
236 if (Iterables.tryFind(el.childNodes(), n -> n instanceof Comment).isPresent()) {
237 writer.append(el.data());
238 } else {
239 writer.append(el.outerHtml());
240 }
241 }
242 } else if (node instanceof Comment) {
243 writer.append(((Comment) node).getData());
244 } else if (node instanceof TextNode) {
245 writer.append(((TextNode) node).text());
246 }
247 }
248
249 @Override
250 protected void handleStartTag(ComponentToken token) {
251
252 }
253
254
255
256
257 @Override
258 protected void handleEmptyTag(final ComponentToken token) {
259 SnippetContext snippetContext = parser.getSnippetContext();
260 Element componentElement = convertToHtml(token, null);
261 Component<?> component = snippetContext.create(componentElement, token, null);
262 snippetContext.render(component);
263 }
264
265
266
267
268 @Override
269 protected void handleCloseTag(final ComponentToken startToken, final ComponentToken endToken) {
270 SnippetContext snippetContext = parser.getSnippetContext();
271
272
273
274 if (!endToken.isCloseTagOf(startToken)) {
275 throw new RuntimeException("start token " + startToken + " should be closed by " + startToken.getCloseTag()
276 + ", but found " + endToken);
277 }
278
279 Element componentElement = convertToHtml(startToken, endToken);
280 Component<?> component = snippetContext.create(componentElement, startToken, endToken);
281 snippetContext.render(component);
282 }
283
284
285
286
287 @Override
288 protected void handleHtmlTag(final ComponentToken token) {
289 SnippetContext snippetContext = parser.getSnippetContext();
290 Element componentElement = token.getElement();
291 Component<?> component = snippetContext.create(componentElement, token, null);
292 snippetContext.render(component);
293 }
294
295 }
296
297 }