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 java.io.StringWriter;
21 import java.io.Writer;
22 import java.util.UUID;
23 import java.util.function.Consumer;
24 import javax.annotation.Nonnull;
25 import javax.annotation.Nullable;
26 import org.apache.commons.io.IOUtils;
27 import org.apache.velocity.VelocityContext;
28 import org.apache.velocity.app.Velocity;
29 import org.apache.velocity.context.Context;
30 import org.apache.velocity.runtime.RuntimeConstants;
31 import org.apache.velocity.runtime.RuntimeSingleton;
32 import org.apache.velocity.tools.Scope;
33 import org.apache.velocity.tools.ToolManager;
34 import org.apache.velocity.tools.config.EasyFactoryConfiguration;
35 import org.apache.velocity.tools.generic.ClassTool;
36 import org.apache.velocity.tools.generic.ComparisonDateTool;
37 import org.apache.velocity.tools.generic.ContextTool;
38 import org.apache.velocity.tools.generic.DisplayTool;
39 import org.apache.velocity.tools.generic.EscapeTool;
40 import org.apache.velocity.tools.generic.FieldTool;
41 import org.apache.velocity.tools.generic.LinkTool;
42 import org.apache.velocity.tools.generic.LoopTool;
43 import org.apache.velocity.tools.generic.MathTool;
44 import org.apache.velocity.tools.generic.NumberTool;
45 import org.apache.velocity.tools.generic.RenderTool;
46 import org.apache.velocity.tools.generic.ResourceTool;
47 import org.apache.velocity.tools.generic.XmlTool;
48 import org.devacfr.maven.skins.reflow.HtmlTool;
49 import org.devacfr.maven.skins.reflow.ISkinConfig;
50 import org.devacfr.maven.skins.reflow.JsoupUtils;
51 import org.devacfr.maven.skins.reflow.URITool;
52 import org.devacfr.maven.skins.reflow.snippet.SnippetComponent.Type;
53 import org.jsoup.nodes.Element;
54 import org.jsoup.nodes.Node;
55 import org.jsoup.nodes.TextNode;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59
60
61
62
63 public class SnippetContext {
64
65
66 private static final Logger LOGGER = LoggerFactory.getLogger(SnippetContext.class);
67
68
69 private String htmlSource;
70
71
72 private ISkinConfig config;
73
74
75 private final SnippetParser parser;
76
77
78
79
80
81
82 public SnippetContext(@Nonnull final SnippetParser parser) {
83 this.parser = requireNonNull(parser);
84 }
85
86
87
88
89 public void reset() {
90 this.htmlSource = null;
91 this.config = null;
92 }
93
94
95
96
97
98
99 public String generateSnippetIdentifier() {
100 return "snippet-placement-" + UUID.randomUUID().toString();
101 }
102
103
104
105
106
107
108 public SnippetParser getParser() {
109 return this.parser;
110 }
111
112
113
114
115
116
117 public ISkinConfig getConfig() {
118 return config;
119 }
120
121
122
123
124
125
126 public SnippetParser createChildParser() {
127 final SnippetParser parser = new SnippetParser();
128 return parser;
129 }
130
131
132
133
134
135
136
137 public void setConfig(final ISkinConfig config) {
138 this.config = config;
139 }
140
141
142
143
144
145
146
147 void setHtmlSource(final String htmlSource) {
148 this.htmlSource = htmlSource;
149 }
150
151
152
153
154
155
156 public String html() {
157 return htmlSource;
158 }
159
160
161
162
163
164
165 public Element document() {
166 return JsoupUtils.createHtmlDocument(html());
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180 @Nonnull
181 public Component<?> create(@Nonnull final Element element,
182 @Nonnull final ComponentToken startToken,
183 @Nullable final ComponentToken endToken) {
184 requireNonNull(element);
185 requireNonNull(startToken);
186 Component<?> component = null;
187 if (parser.isSnippet(startToken.name())) {
188
189 component = SnippetComponent.createSnippet(element, null, startToken.type());
190 recurciveCreateComponent(element, component);
191 } else {
192
193 component = Component.createComponent(element, null);
194 recurciveCreateComponent(element, component);
195 }
196 return component;
197 }
198
199
200
201
202
203
204
205
206
207
208 @Nonnull
209 public Component<?> create(@Nonnull final Element element, final Component<?> parent) {
210 requireNonNull(element);
211 Component<?> component = null;
212 if (parser.isSnippet(element.tagName())) {
213 component = SnippetComponent.createSnippet(element, parent, Type.webComponent);
214 } else {
215 component = Component.createComponent(element, parent);
216 }
217 recurciveCreateComponent(element, component);
218 return component;
219 }
220
221
222
223
224
225
226
227
228
229 private void recurciveCreateComponent(@Nonnull final Node element, final Component<?> parent) {
230 element.childNodes().forEach(child -> {
231 Component<?> component = null;
232
233 if (child instanceof TextNode && ((TextNode) child).text().trim().length() > 0) {
234 component = Component.createComponent(child, parent);
235 } else if ("p".equals(child.nodeName()) && child.outerHtml().length() == 7) {
236
237 } else if (child instanceof Element) {
238 final Element el = (Element) child;
239 component = create(el, parent);
240 }
241 if (component != null) {
242 parent.addChild(component);
243 }
244 });
245 }
246
247
248
249
250
251
252
253 protected void render(final Component<?> component) {
254 traverseTee(component, c -> {
255 if (c instanceof SnippetComponent) {
256 ((SnippetComponent<?>) c).render(this);
257 }
258 });
259 if (component instanceof SnippetComponent) {
260 ((SnippetComponent<?>) component).render(this);
261 }
262 }
263
264 private void traverseTee(final Component<?> component, final Consumer<Component<?>> consumer) {
265 final Consumer<Component<?>> traverse = c -> traverseTee(c, consumer);
266 component.getChildren().forEach(consumer.andThen(traverse));
267 }
268
269 protected String renderComponent(final SnippetComponent<?> component, final Context context) {
270 final StringWriter writer = new StringWriter();
271 try {
272 mergeTemplate(component, context, writer);
273 return writer.toString();
274 } finally {
275 IOUtils.closeQuietly(writer);
276 }
277 }
278
279
280
281
282
283
284
285
286
287
288
289 protected void mergeTemplate(final SnippetComponent<?> component, final Context contextParent, final Writer writer) {
290 boolean found = false;
291 for (final String path : this.parser.getSnippetPaths()) {
292 final String filePath = path + '/' + component.getName() + ".vm";
293 if (Velocity.resourceExists(filePath)) {
294 found = true;
295 final Context context = createVelocityContext(contextParent);
296 context.put("snippet", component);
297 context.put("snippetPath", filePath);
298 context.put("config", this.config);
299 if (this.config.getContext() != null) {
300 context.put("pageContext", this.config.getContext());
301 context.put("pageType", this.config.getContext().getType());
302 }
303 context.put("velocity", Velocity.class);
304 context.put("site", this.config.getSiteModel());
305
306 Velocity.mergeTemplate("META-INF/skin/snippets/_snippet.vm",
307 RuntimeSingleton.getString(RuntimeConstants.INPUT_ENCODING, RuntimeConstants.ENCODING_DEFAULT),
308 context,
309 writer);
310 break;
311 } else {
312 if (LOGGER.isDebugEnabled()) {
313 LOGGER.debug("Template for component '{}' not found in path:{} ", component, filePath);
314 }
315 }
316 }
317 if (!found) {
318 LOGGER.warn("The snippet '{}' template doesn't exist", component.getName());
319 }
320 }
321
322
323
324
325
326
327
328
329 protected Context createVelocityContext(final Context contextParent) {
330 final VelocityContext context = new VelocityContext(contextParent);
331 return context;
332 }
333
334
335
336
337
338
339 protected static ToolManager createToolManaged() {
340
341 final EasyFactoryConfiguration config = new EasyFactoryConfiguration(false);
342 config.property("safeMode", Boolean.FALSE);
343 config.toolbox(Scope.REQUEST)
344 .tool(ContextTool.class)
345 .tool(LinkTool.class)
346 .tool(LoopTool.class)
347 .tool(RenderTool.class);
348 config.toolbox(Scope.APPLICATION)
349 .tool(ClassTool.class)
350 .tool(ComparisonDateTool.class)
351 .tool(DisplayTool.class)
352 .tool(EscapeTool.class)
353 .tool(FieldTool.class)
354 .tool(MathTool.class)
355 .tool(NumberTool.class)
356 .tool(ResourceTool.class)
357 .tool(XmlTool.class)
358 .tool(URITool.class)
359 .tool(HtmlTool.class);
360
361 final ToolManager manager = new ToolManager(false, false);
362 manager.configure(config);
363 return manager;
364 }
365
366
367
368
369 public static class SnippetResource {
370
371
372 private final String name;
373
374
375 private final String path;
376
377
378
379
380
381
382
383
384
385 public SnippetResource(final String name, final String path) {
386 this.name = name;
387 this.path = path;
388 }
389
390
391 public String getName() {
392 return name;
393 }
394
395
396 public String getPath() {
397 return path;
398 }
399
400
401
402
403 @Override
404 public String toString() {
405 return "SnippetResource [name=" + name + ", path=" + path + "]";
406 }
407 }
408
409 }