1. Project Clover database mar. janv. 20 2026 12:32:22 CET
  2. Package org.devacfr.maven.skins.reflow.snippet

File SnippetContext.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart9.png
22% of files have more coverage

Code metrics

20
79
23
2
290
213
35
0,44
3,43
11,5
1,52

Classes

Class Line # Actions
SnippetContext 63 74 0% 31 6
0.9469026394,7%
SnippetContext.SnippetResource 265 5 0% 4 6
0.3333333433,3%
 

Contributing tests

This file is covered by 32 tests. .

Source view

1    /*
2    * Copyright 2012-2025 Christophe Friederich
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    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10    * Unless required by applicable law or agreed to in writing, software
11    * distributed under the License is distributed on an "AS IS" BASIS,
12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    * See the License for the specific language governing permissions and
14    * limitations under the License.
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    * @author Christophe Friederich
61    * @version 2.4
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    * Constructor.
79    *
80    * @param parser
81    */
 
82  33 toggle public SnippetContext(@Nonnull final SnippetParser parser) {
83  33 this.parser = requireNonNull(parser);
84    }
85   
86    /**
87    * Reset the context.
88    */
 
89  16 toggle public void reset() {
90  16 this.htmlSource = null;
91  16 this.config = null;
92    }
93   
 
94  0 toggle public String generateSnippetIdentifier() {
95  0 return "snippet-placement-" + UUID.randomUUID().toString();
96    }
97   
 
98  51 toggle public SnippetParser getParser() {
99  51 return this.parser;
100    }
101   
 
102  53 toggle public ISkinConfig getConfig() {
103  53 return config;
104    }
105   
 
106  1 toggle public SnippetParser createChildParser() {
107  1 final SnippetParser parser = new SnippetParser();
108  1 return parser;
109    }
110   
 
111  17 toggle public void setConfig(final ISkinConfig config) {
112  17 this.config = config;
113    }
114   
 
115  32 toggle void setHtmlSource(final String htmlSource) {
116  32 this.htmlSource = htmlSource;
117    }
118   
 
119  16 toggle public String html() {
120  16 return htmlSource;
121    }
122   
 
123  1 toggle public Element document() {
124  1 return JsoupUtils.createHtmlDocument(html());
125    }
126   
 
127  49 toggle @Nonnull
128    public SnippetComponent<?> create(@Nonnull final Element element,
129    @Nonnull final ComponentToken startToken,
130    @Nullable final ComponentToken endToken) {
131  49 requireNonNull(element);
132  49 requireNonNull(startToken);
133  49 final SnippetComponent<?> component = SnippetComponent.createSnippet(element, null, startToken.type());
134  49 recurciveCreateComponent(element, component);
135  49 return component;
136    }
137   
 
138  3 toggle @Nonnull
139    public SnippetComponent<?> create(@Nonnull final Element element, final Component<?> commponent) {
140  3 requireNonNull(element);
141  3 final SnippetComponent<?> component = SnippetComponent.createSnippet(element, commponent, Type.webComponent);
142  3 recurciveCreateComponent(element, component);
143  3 return component;
144    }
145   
 
146  249 toggle private void recurciveCreateComponent(@Nonnull final Node element, final Component<?> parent) {
147  249 element.childNodes().forEach(child -> {
148  591 Component<?> component = null;
149    // accept textnode not empty as component.
150  591 if (child instanceof TextNode && ((TextNode) child).text().trim().length() > 0) {
151  120 component = Component.createComponent(child, parent);
152  471 } else if ("p".equals(child.nodeName()) && child.outerHtml().length() == 7) {
153    // skip empty <p> tags
154  471 } else if (child instanceof Element) {
155  199 final Element el = (Element) child;
156  199 if (parser.isSnippet(el)) {
157  2 component = create(el, parent);
158    } else {
159  197 component = Component.createComponent(el, parent);
160  197 recurciveCreateComponent(el, component);
161    }
162    }
163  591 if (component != null) {
164  319 parent.addChild(component);
165    }
166    });
167    }
168   
 
169  49 toggle protected void render(final SnippetComponent<?> component) {
170  49 traverseTee(component, c -> {
171  319 if (c instanceof SnippetComponent) {
172  2 ((SnippetComponent<?>) c).render(this);
173    }
174    });
175  49 component.render(this);
176    }
177   
 
178  368 toggle private void traverseTee(final Component<?> component, final Consumer<Component<?>> consumer) {
179  368 final Consumer<Component<?>> traverse = c -> traverseTee(c, consumer);
180  368 component.getChildren().forEach(consumer.andThen(traverse));
181    }
182   
 
183  52 toggle protected String renderComponent(final SnippetComponent<?> component, final Context context) {
184  52 final StringWriter writer = new StringWriter();
185  52 try {
186  52 mergeTemplate(component, context, writer);
187  52 return writer.toString();
188    } finally {
189  52 IOUtils.closeQuietly(writer);
190    }
191    }
192   
 
193  52 toggle protected void mergeTemplate(final SnippetComponent<?> component, final Context contextParent, final Writer writer) {
194  52 boolean found = false;
195  52 for (final String path : this.parser.getSnippetPaths()) {
196  143 final String filePath = path + '/' + component.getName() + ".vm";
197  143 if (Velocity.resourceExists(filePath)) {
198  52 found = true;
199  52 final Context context = createVelocityContext(contextParent);
200  52 context.put("snippet", component);
201  52 context.put("snippetPath", filePath);
202  52 context.put("config", this.config);
203  52 if (this.config.getContext() != null) {
204  19 context.put("pageContext", this.config.getContext());
205  19 context.put("pageType", this.config.getContext().getType());
206    }
207  52 context.put("velocity", Velocity.class);
208  52 context.put("site", this.config.getSiteModel());
209   
210  52 Velocity.mergeTemplate("META-INF/skin/snippets/_snippet.vm",
211    RuntimeSingleton.getString(RuntimeConstants.INPUT_ENCODING, RuntimeConstants.ENCODING_DEFAULT),
212    context,
213    writer);
214  52 break;
215    } else {
216  91 if (LOGGER.isDebugEnabled()) {
217  91 LOGGER.debug("Template for component '{}' not found in path:{} ", component, filePath);
218    }
219    }
220    }
221  52 if (!found) {
222  0 LOGGER.warn("The snippet '{}' template doesn't exist", component.getName());
223    }
224    }
225   
226    /**
227    * Creates a Velocity Context with all generic tools configured wit the site rendering context.
228    *
229    * @param contextParent
230    * velocity context parent
231    * @return a Velocity tools managed context
232    */
 
233  52 toggle protected Context createVelocityContext(final Context contextParent) {
234  52 final VelocityContext context = new VelocityContext(contextParent);
235  52 return context;
236    }
237   
 
238  16 toggle protected static ToolManager createToolManaged() {
239   
240  16 final EasyFactoryConfiguration config = new EasyFactoryConfiguration(false);
241  16 config.property("safeMode", Boolean.FALSE);
242  16 config.toolbox(Scope.REQUEST)
243    .tool(ContextTool.class)
244    .tool(LinkTool.class)
245    .tool(LoopTool.class)
246    .tool(RenderTool.class);
247  16 config.toolbox(Scope.APPLICATION)
248    .tool(ClassTool.class)
249    .tool(ComparisonDateTool.class)
250    .tool(DisplayTool.class)
251    .tool(EscapeTool.class)
252    .tool(FieldTool.class)
253    .tool(MathTool.class)
254    .tool(NumberTool.class)
255    .tool(ResourceTool.class)
256    .tool(XmlTool.class)
257    .tool(URITool.class)
258    .tool(HtmlTool.class);
259   
260  16 final ToolManager manager = new ToolManager(false, false);
261  16 manager.configure(config);
262  16 return manager;
263    }
264   
 
265    public static class SnippetResource {
266   
267    private final String name;
268   
269    private final String path;
270   
 
271  966 toggle public SnippetResource(final String name, final String path) {
272  966 this.name = name;
273  966 this.path = path;
274    }
275   
 
276  0 toggle public String getName() {
277  0 return name;
278    }
279   
 
280  0 toggle public String getPath() {
281  0 return path;
282    }
283   
 
284  0 toggle @Override
285    public String toString() {
286  0 return "SnippetResource [name=" + name + ", path=" + path + "]";
287    }
288    }
289   
290    }