Skip to content

Commit 6361430

Browse files
committed
Added tool FreeMarkerTemplateSyntaxVerifier
1 parent 164b8f5 commit 6361430

File tree

13 files changed

+566
-2
lines changed

13 files changed

+566
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- [fj-doc-freemarker] tool FreeMarkerTemplateSyntaxVerifier (check for FreeMarker templates syntax)
13+
1014
## [8.6.5] - 2024-08-23
1115

1216
### Fixed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package org.fugerit.java.doc.freemarker.tool;
2+
3+
import freemarker.core.ParseException;
4+
import freemarker.template.Configuration;
5+
import freemarker.template.Template;
6+
import freemarker.template.Version;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.fugerit.java.core.function.SafeFunction;
9+
import org.fugerit.java.core.util.result.Result;
10+
import org.fugerit.java.doc.freemarker.config.FreeMarkerConfigStep;
11+
import org.fugerit.java.doc.freemarker.tool.verify.VerifyTemplateInfo;
12+
import org.fugerit.java.doc.freemarker.tool.verify.VerifyTemplateOutput;
13+
14+
import java.io.File;
15+
import java.io.IOException;
16+
import java.util.Properties;
17+
18+
/**
19+
* Tool to check FreeMarker template syntax for all templates in a FreeMarker Configuration.
20+
*
21+
* Only syntax is checked, by calling Configuration.getTemplate() and catching freemarker.core.ParseException.
22+
*
23+
* In brief this tool can find "build" time syntax error in FreeMarker templates.
24+
*
25+
* Quickstart :
26+
*
27+
* <code>
28+
* File baseFolder = nw File( "..." ); // path to FreeMarker base folder template
29+
* FreeMarkerTemplateSyntaxVerifier.doCreateConfigurationAndVerify( baseFolder );
30+
* </code>
31+
*
32+
* NOTE: For customized uses check : createConfigurationAndVerify() and verify() methods.
33+
*/
34+
@Slf4j
35+
public class FreeMarkerTemplateSyntaxVerifier {
36+
37+
/**
38+
* If a FreeMarker Configuration should be created, it will use this FreeMarker version
39+
*/
40+
public static final String PARAM_VERSION = FreeMarkerConfigStep.ATT_FREEMARKER_CONFIG_KEY_VERSION;
41+
42+
/**
43+
* It will check only templates with this pattern, (regex check on fileName, i.e. ".{0,}[.]ftl")
44+
*/
45+
public static final String PARAM_TEMPLATE_FILE_PATTERN = "templateFilePattern";
46+
47+
public static VerifyTemplateOutput doCreateConfigurationAndVerify(File baseFolder) {
48+
return new FreeMarkerTemplateSyntaxVerifier().createConfigurationAndVerify( baseFolder, new Properties() );
49+
}
50+
51+
protected void verifyCurrentTemplate( VerifyTemplateOutput output, File baseFolder, Configuration cfg, String templateId ) throws IOException {
52+
try {
53+
Template template = cfg.getTemplate( templateId );
54+
log.debug( "check template OK, id: {}, name:{}, base folder : {}", templateId, template.getName(), baseFolder );
55+
output.getInfos().add( new VerifyTemplateInfo(Result.RESULT_CODE_OK, templateId) );
56+
} catch ( ParseException e ) {
57+
log.warn( "check template KO:{}, id: {}, base folder : {}", e.getMessage(), templateId, baseFolder );
58+
output.getInfos().add( new VerifyTemplateInfo(Result.RESULT_CODE_KO, templateId, e) );
59+
}
60+
}
61+
62+
protected void iterateVerify( VerifyTemplateOutput output, File baseFolder, Configuration cfg, File currentFolder ) throws IOException {
63+
for ( File currentFile : currentFolder.listFiles( output.getFileFilter() ) ) {
64+
if ( currentFile.isDirectory() ) {
65+
this.iterateVerify( output, baseFolder, cfg, currentFile );
66+
} else {
67+
String templateId = currentFile.getCanonicalPath().substring( baseFolder.getCanonicalPath().length()+1 );
68+
this.verifyCurrentTemplate( output, baseFolder, cfg, templateId );
69+
}
70+
}
71+
}
72+
73+
/**
74+
* It will verify all templates for a FreeMarker configuration :
75+
* All templates in baseFolder params will be verified
76+
*
77+
* NOTE: Given FreemarkerConfiguration cfg should be able to resolve such templates.
78+
*
79+
* @param baseFolder all templates in this folder will be checked
80+
* @param cfg FreeMarker Configuration used to load templates
81+
* @param params additional params
82+
* @return
83+
*/
84+
public VerifyTemplateOutput verify(File baseFolder, Configuration cfg, Properties params) {
85+
return SafeFunction.get( () -> {
86+
VerifyTemplateOutput output = new VerifyTemplateOutput();
87+
// check additional options
88+
if ( params.containsKey( PARAM_TEMPLATE_FILE_PATTERN ) ) {
89+
String templateFilePattern = params.getProperty( PARAM_TEMPLATE_FILE_PATTERN );
90+
log.info( "setting file filter with templateFilePattern: {}", templateFilePattern );
91+
output.setFileFilter( f -> f.isDirectory() || f.getName().matches( templateFilePattern ) );
92+
}
93+
// setup iteration with baseFolder as currentFolder
94+
this.iterateVerify( output, baseFolder, cfg, baseFolder );
95+
return output;
96+
} );
97+
}
98+
99+
/**
100+
* It will create a new FreeMarker configuration, loading templates from given base folder.
101+
*
102+
* If available, parameters "version" will be used to specific FreeMarker configuration version.
103+
*
104+
* Then it will verify all templates for a FreeMarker configuration :
105+
* All templates in baseFolder params will be verified
106+
*
107+
* @param baseFolder it will be used for Configuration.setDirectoryForTemplateLoading() and to find templates path.
108+
* @param params additional configuration parameters
109+
* @return the output of the verify operation
110+
*/
111+
public VerifyTemplateOutput createConfigurationAndVerify(File baseFolder, Properties params) {
112+
return SafeFunction.get( () -> {
113+
String version = params.getProperty( PARAM_VERSION, FreeMarkerConfigStep.ATT_FREEMARKER_CONFIG_KEY_VERSION_LATEST );
114+
log.info( "creating FreeMarker configuration, version : {}", version );
115+
Configuration cfg = new Configuration( new Version( version ) );
116+
cfg.setDirectoryForTemplateLoading( baseFolder );
117+
return this.verify( baseFolder, cfg, params );
118+
} );
119+
}
120+
121+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.fugerit.java.doc.freemarker.tool.verify;
2+
3+
import lombok.*;
4+
5+
@ToString
6+
@AllArgsConstructor
7+
@RequiredArgsConstructor
8+
public class VerifyTemplateInfo {
9+
10+
@Getter
11+
@NonNull
12+
private Integer resultCode;
13+
14+
@Getter
15+
@NonNull
16+
private String templateId;
17+
18+
@Getter
19+
private Exception exception;
20+
21+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.fugerit.java.doc.freemarker.tool.verify;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
import org.fugerit.java.core.util.result.Result;
6+
7+
import java.io.FileFilter;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
public class VerifyTemplateOutput {
12+
13+
@Getter @Setter
14+
private FileFilter fileFilter = f -> Boolean.TRUE;
15+
16+
@Getter
17+
private List<VerifyTemplateInfo> infos;
18+
19+
public VerifyTemplateOutput() {
20+
this.infos = new ArrayList<>();
21+
}
22+
23+
/**
24+
* General result code for this output
25+
*
26+
* @return 0 (OK) if all the template check is OK, ad different value if at least one template check failed
27+
*/
28+
public int getResultCode() {
29+
for ( VerifyTemplateInfo info : this.infos ) {
30+
if ( info.getResultCode() != Result.RESULT_CODE_OK ) {
31+
return Result.RESULT_CODE_KO;
32+
}
33+
}
34+
return Result.RESULT_CODE_OK;
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package test.org.fugerit.java.doc.freemarker.tool;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.fugerit.java.core.function.SafeFunction;
5+
import org.fugerit.java.core.util.result.Result;
6+
import org.fugerit.java.doc.freemarker.tool.FreeMarkerTemplateSyntaxVerifier;
7+
import org.fugerit.java.doc.freemarker.tool.verify.VerifyTemplateInfo;
8+
import org.fugerit.java.doc.freemarker.tool.verify.VerifyTemplateOutput;
9+
import org.junit.Assert;
10+
import org.junit.Test;
11+
import test.org.fugerit.java.BasicTest;
12+
13+
import java.io.File;
14+
import java.util.Arrays;
15+
import java.util.Properties;
16+
import java.util.function.Function;
17+
18+
@Slf4j
19+
public class TestFreeMarkerTemplateSyntaxyVerifier extends BasicTest {
20+
21+
private VerifyTemplateOutput verifyWorker(String basePath) {
22+
return this.verifyWorker( basePath, f -> FreeMarkerTemplateSyntaxVerifier.doCreateConfigurationAndVerify( f ) );
23+
}
24+
25+
private VerifyTemplateOutput verifyWorker(String basePath, Function<File, VerifyTemplateOutput> verifyFun ) {
26+
return SafeFunction.get( () -> {
27+
File baseFolder = new File( basePath );
28+
log.info( "basic verify for template path : {}", baseFolder.getCanonicalPath() );
29+
VerifyTemplateOutput output = verifyFun.apply( baseFolder );
30+
for ( VerifyTemplateInfo info : output.getInfos() ) {
31+
log.info( "check info : {}", info );
32+
}
33+
log.info( "total number of checked template : {}, resultCode : {}", output.getInfos().size(), output.getResultCode() );
34+
return output;
35+
} );
36+
}
37+
38+
@Test
39+
public void verifyTestOk() {
40+
Arrays.asList( "src/test/resources/fj_doc_test/template",
41+
"src/test/resources/fj_doc_test/template-macro" ).forEach(
42+
basePath -> {
43+
VerifyTemplateOutput output = verifyWorker( basePath );
44+
Assert.assertEquals(Result.RESULT_CODE_OK, output.getResultCode());
45+
}
46+
);
47+
}
48+
49+
@Test
50+
public void verifyTestKo() {
51+
Arrays.asList( "src/test/resources/fj_doc_test/template-fail" ).forEach(
52+
basePath -> {
53+
VerifyTemplateOutput output = verifyWorker( basePath );
54+
Assert.assertEquals(Result.RESULT_CODE_KO, output.getResultCode());
55+
}
56+
);
57+
}
58+
59+
@Test
60+
public void verifyTemplateFilePattern() {
61+
Properties params = new Properties();
62+
params.setProperty( FreeMarkerTemplateSyntaxVerifier.PARAM_TEMPLATE_FILE_PATTERN, ".{0,}[.]ftl" );
63+
FreeMarkerTemplateSyntaxVerifier verifier = new FreeMarkerTemplateSyntaxVerifier();
64+
Arrays.asList( "src/test/resources/fj_doc_test/template-fail",
65+
"src/test/resources/fj_doc_test/template-macro" ).forEach(
66+
basePath -> {
67+
VerifyTemplateOutput output = this.verifyWorker( basePath,
68+
f -> verifier.createConfigurationAndVerify( f, params ) );
69+
Assert.assertEquals(Result.RESULT_CODE_OK, output.getResultCode());
70+
}
71+
);
72+
}
73+
74+
@Test
75+
public void checkPojo() {
76+
Assert.assertThrows( NullPointerException.class, () -> new VerifyTemplateInfo( Result.RESULT_CODE_OK, null ) );
77+
Assert.assertThrows( NullPointerException.class, () -> new VerifyTemplateInfo( Result.RESULT_CODE_OK, null, null ) );
78+
Assert.assertThrows( NullPointerException.class, () -> new VerifyTemplateInfo( null, "1" ) );
79+
Assert.assertThrows( NullPointerException.class, () -> new VerifyTemplateInfo( null, "2", null ) );
80+
Assert.assertThrows( NullPointerException.class, () -> new VerifyTemplateInfo( null, null ) );
81+
Assert.assertThrows( NullPointerException.class, () -> new VerifyTemplateInfo( null, null, null ) );
82+
}
83+
84+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<doc
3+
xmlns="http://javacoredoc.fugerit.org"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://javacoredoc.fugerit.org https://www.fugerit.org/data/java/doc/xsd/doc-2-0.xsd" >
6+
7+
<!--
8+
Sample Apache FreeMarker template for Fugerit Doc.
9+
Note : this example has no intention of being a guid to FreeMarker
10+
(In case check FreeMarker documentation https://freemarker.apache.org/docs/index.html)
11+
-->
12+
13+
<meta>
14+
15+
<!-- Margin for document : left;right;top;bottom -->
16+
<info name="margins">10;10;10;30</info>
17+
18+
<!-- id table to be used for xlsx output -->
19+
<info name="excel-table-id">excel-table=print</info>
20+
<info name="excel-width-multiplier">450</info>
21+
<!-- id table to be used for xsv output -->
22+
<info name="csv-table-id">excel-table</info>
23+
24+
<!-- you need to escape free marker expression for currentPage -->
25+
<footer-ext>
26+
<para align="right">${r"${currentPage}"} / ${r"${pageCount}"}</para>
27+
</footer-ext>
28+
29+
</meta>
30+
31+
<body>
32+
33+
<h head-level="1">Heading test level 1 default font</h>
34+
35+
<h head-level="1">Heading test level 1 TitilliumWeb</h>
36+
37+
<h head-level="2">Heading test level 2</h>
38+
39+
<h head-level="3" align="right">Heading test level 3</h>
40+
41+
<para align="right">Test right</para>
42+
43+
<para align="right">${messageFormat('test format -> {0} {1}', 'param1', 'param2')}</para>
44+
45+
<para align="right">${textWrap('test text wrap')}</para>
46+
47+
48+
<br/>
49+
<br/>
50+
<br/>
51+
52+
<phrase>Test template page params free marker</phrase>
53+
54+
<image url="cl://test/img_test_green.png" scaling="100"/>
55+
56+
<image url="cl://test/img_test_red.png" scaling="50"/>
57+
58+
<image url="png" base64="${imageBase64CLFun('test/img_test_red.png')}" scaling="25"/>
59+
60+
<image url="png" base64="${imageBase64CLFun('test/not_exists.png')}" scaling="25"/>
61+
62+
<para style="bold">italic ${sumLong(10, 20)}</para>
63+
64+
<image url="png" base64="${imageBase64CLFun()}" scaling="25"/>
65+
66+
<para style="bold">italic ${messageFormat()}</para>
67+
68+
<para style="italic">bold</para>
69+
70+
<para style="bolditalic">bold italic</para>
71+
72+
<table columns="3" colwidths="30;30;40" width="100" id="excel-table" padding="2">
73+
<row>
74+
<cell align="center" border-color="#ee0000" border-width="1"><para style="bold">Name</para></cell>
75+
<cell align="center"><para style="bold">Surname</para></cell>
76+
<cell align="center"><para style="bold">Title</para></cell>
77+
</row>
78+
</table>
79+
</body>
80+
81+
</doc>

0 commit comments

Comments
 (0)