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

File Processor.java

 

Coverage histogram

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

Code metrics

30
75
6
2
237
135
27
0,36
12,5
3
4,5

Classes

Class Line # Actions
Processor 40 63 0% 19 12
0.8620689586,2%
Processor.WebComponentProcessor 199 12 0% 8 3
0.87587,5%
 

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 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.devacfr.maven.skins.reflow.snippet.ComponentToken.Tag;
26    import org.jsoup.nodes.Comment;
27    import org.jsoup.nodes.Document;
28    import org.jsoup.nodes.Element;
29    import org.jsoup.nodes.Node;
30    import org.jsoup.nodes.TextNode;
31    import org.slf4j.Logger;
32    import org.slf4j.LoggerFactory;
33   
34    /**
35    * Specific process for each type of snippet component.
36    *
37    * @author Christophe Friederich
38    * @version 2.4
39    */
 
40    public abstract class Processor {
41   
42    private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class);
43   
44    /** */
45    protected final SnippetParser parser;
46   
47    /**
48    * Default constructor
49    *
50    * @param parser
51    * current parser.
52    */
 
53  33 toggle public Processor(final SnippetParser parser) {
54  33 this.parser = parser;
55    }
56   
57    /**
58    * Specific parsing for each {@link ComponentToken}.
59    *
60    * @param token
61    * the current token.
62    */
 
63  87 toggle public void parse(final ComponentToken token) {
64  87 if (LOGGER.isDebugEnabled()) {
65  87 LOGGER.debug("Parse Token: {}", token);
66    }
67  87 SnippetContext snippetContext = parser.getSnippetContext();
68  87 switch (token.tag()) {
69  9 case empty:
70  9 snippetContext.render(createSnippetComponent(snippetContext, token, null));
71  9 break;
72   
73  38 case start:
74  38 parser.push(token);
75  38 parser.parse();
76  38 break;
77   
78  38 case end:
79  38 final ComponentToken startToken = parser.pop();
80  38 if (!token.isCloseTagOf(startToken)) {
81  0 throw new RuntimeException("start token " + startToken + " should be closed, but next token is " + token);
82    }
83  38 snippetContext.render(createSnippetComponent(snippetContext, startToken, token));
84  38 break;
85   
86  2 case html:
87  2 snippetContext.render(createSnippetComponent(snippetContext, token, null));
88  2 break;
89   
90  0 default:
91  0 throw new SnippetParseException("unknown token tag " + token.tag());
92    }
93    }
94   
95    /**
96    * Append child {@link Node} in html rendering.
97    *
98    * @param node
99    * the node to use.
100    * @param writer
101    * the html writer
102    * @throws IOException
103    * If an I/O error occurs.
104    */
105    protected abstract void appendChildrenToHtml(Node node, Appendable writer) throws IOException;
106   
107    /**
108    * Convert the snippet to html.
109    *
110    * @param startToken
111    * the start token
112    * @param endToken
113    * the end token.
114    * @return Returns a new {@link Element} representing html represention of snippet.
115    */
 
116  47 toggle protected Element convertToHtml(@Nonnull final ComponentToken startToken, @Nullable final ComponentToken endToken) {
117  47 final Element startElement = startToken.getElement();
118  47 final Node previousElement = startElement.previousSibling();
119  47 Element endElement = null;
120  47 if (endToken != null) {
121  38 endElement = endToken.getElement();
122    }
123  47 final Document doc = JsoupUtils.createHtmlDocument("");
124  47 final Element tmp = doc.body();
125  47 final Node parent = startElement.parentNode();
126   
127  47 final StringBuilder html = new StringBuilder(ComponentResolver.convertElementToHtml(startElement));
128  47 final List<Node> nodesToRemove = Lists.newArrayList();
129  47 nodesToRemove.add(startElement);
130  47 if (endElement != null) {
131  38 boolean startCopy = false;
132   
133  38 for (final Node n : parent.childNodes()) {
134  500 if (n.equals(endElement)) {
135  38 nodesToRemove.add(n);
136  38 break;
137    }
138  462 if (startCopy) {
139  128 try {
140  128 appendChildrenToHtml(n, html);
141    } catch (final IOException e) {
142  0 throw new RuntimeException(e.getMessage(), e);
143    }
144  128 nodesToRemove.add(n);
145    }
146  462 if (n.equals(startElement)) {
147  38 startCopy = true;
148    }
149    }
150  38 html.append(ComponentResolver.convertElementToHtml(endElement));
151    }
152  47 tmp.html(html.toString());
153  47 final Element component = tmp.children().first();
154  47 final Element parentElement = startElement.parent();
155   
156  47 if (previousElement != null) {
157  47 previousElement.after(component);
158    } else {
159  0 if (parentElement.children().first() != null) {
160  0 parentElement.children().first().before(component);
161    } else {
162  0 parentElement.children().add(component);
163    }
164    }
165  47 nodesToRemove.forEach(Node::remove);
166  47 return component;
167    }
168   
169    /**
170    * Create a {@link SnippetComponent}.
171    *
172    * @param snippetContext
173    * the snippet context to use.
174    * @param startToken
175    * the start token.
176    * @param endToken
177    * the end token.
178    * @return Returns a new instance of {@link SnippetComponent} representing the information contained between
179    * {@code startToken} and {@code endToken}.
180    */
 
181  49 toggle protected SnippetComponent<?> createSnippetComponent(final SnippetContext snippetContext,
182    final ComponentToken startToken,
183    final ComponentToken endToken) {
184  49 Element componentElement = null;
185  49 if (Tag.html.equals(startToken.tag())) {
186  2 componentElement = startToken.getElement();
187    } else {
188  47 componentElement = convertToHtml(startToken, endToken);
189    }
190  49 return snippetContext.create(componentElement, startToken, endToken);
191    }
192   
193    /**
194    * Specific process for snippet web component.
195    *
196    * @author Christophe Friederich
197    * @version 2.4
198    */
 
199    public static class WebComponentProcessor extends Processor {
200   
201    /**
202    * Default constructor
203    *
204    * @param parser
205    * current parser.
206    */
 
207  33 toggle public WebComponentProcessor(final SnippetParser parser) {
208  33 super(parser);
209    }
210   
211    /**
212    * {@inheritDoc}
213    */
 
214  128 toggle @Override
215    protected void appendChildrenToHtml(final Node node, final Appendable writer) throws IOException {
216  128 if (node instanceof Element) {
217  34 final Element el = (Element) node;
218    // when use code highlighting
219  34 if ("div".equals(el.tagName()) && el.hasClass("source")) {
220  4 writer.append(el.text());
221    } else {
222    // comment can be enclose in <p> element.
223  30 if (Iterables.tryFind(el.childNodes(), n -> n instanceof Comment).isPresent()) {
224  0 writer.append(el.data());
225    } else {
226  30 writer.append(el.outerHtml());
227    }
228    }
229  94 } else if (node instanceof Comment) {
230  11 writer.append(((Comment) node).getData());
231  83 } else if (node instanceof TextNode) {
232  83 writer.append(((TextNode) node).text());
233    }
234    }
235    }
236   
237    }