I've recently done some extensive testing on my team's hundreds of bchingselelectrule FTL statements.
To mimic the behavior of Documaker in a java unit test requires some knowledge that isn't documented.
The behavior is sensible but completely undocumented.
Basic Info :
12.7.2 uses Freemarker 2.3.16.
The entire set of bching selectrule where active =1 is treated as a single freemarker template.
The freemarker template resolution is a three-pass template resolution process.
-
The base freemarker statement.
-
'${TRNS.KEY2!}'=='CPD'
-
In the first pass it generates new Freemarker templates based off the bchingselectrules and the input data.
-
'RES'=='CPD'
-
In the second pass, it wraps the Freemarker templates into boolean statements.
-
<#if ('RES'=='CPD')>'TRUE'<#/if><#/if>
- This is probably not exactly what happens under the hood, but it is extremely close.
- I have no idea why freemarker requires two closing ifs in this statement, but it does.
-
In the third pass, docfactory evaluates the freemarker expression from Step 3a.
- If more than 1 bchingselelectrule returns TRUE for a RCPS entry, throw an error.
- If less than 1 bchingselelectrule returns TRUE for a RCPS entry, place the recipient in the Default batch.
Some demo code to unit test from an exported bchingselectrule column from a BCHINGS table using FTL 2.3.33 jar from maven.
To setup, export only the BCHINGSELECTRULE column where Active=1 to “bchingselect.ftl
” as a UTF-8 without BOM.
private static void processFTLTemplate(Configuration cfg, Map<String, Object> input){
StringWriter sw = new StringWriter();
BufferedWriter buff = new BufferedWriter(sw);
cfg.setTemplateLoader(new FileTemplateLoader());
Template ftlTemp = cfg.getTemplate("bchingselect.ftl");
ftlTemp.process(input, buff);
StringTemplateLoader stl=intermediatePassStringBuff(sw.toString)
int trueCount=secondPass(input, cfg,stl);
if (trueCount!=1){
throw new RuntimeException("Failed on this!");
}
}
private static int secondPass(Map<String, Object> input, Configuration cfg, StringTemplateLoader stl) throws TemplateException, IOException {
StringWriter sw = new StringWriter();
cfg.setTemplateLoader(stl);
Template temp = cfg.getTemplate("test");
temp.process(input, sw);
String s = sw.toString();
int trueCount=0;
trueCount=StringUtils.countMatches(s,"TRUE");
if (trueCount!=1) {
System.out.println("Found an extra entry");
Files.write(Paths.get("error.txt"), s.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
}
return trueCount;
}
private static StringTemplateLoader intermediatePassStringBuff(String string) throws IOException {
String[] lines = string.split("\\R");
ArrayList<String> outLines = new ArrayList<>();
for (String s:lines){
StringBuilder sb=new StringBuilder();
sb.append("<#if (").append((s));
sb.append(")>'TRUE'<#/if></#if>");
outLines.add(sb.toString());
}
String p = String.join("", outLines);
StringTemplateLoader stl= new StringTemplateLoader();
stl.putTemplate("test",p);
return stl;
}
public static void main(String[] args) throws IOException {
//Documaker 12_7_2 technically uses 2_3_16, but our only options are either 2_3_0 or 2_3_19.
Configuration cfg = new Configuration(Configuration.VERSION_2_3_19);
Map<String, Object> input = new HashMap<String, Object>();
//simple POJO with 1 attribute for demo purposes
RCPS rcps = new RCPS();
//simple POJO with 2 attributes for test purposes
TRNS trns = new TRNS();
trns.setKEY1("Test");
trns.setKEY2("CPD");
rcps.setRCPCUSINT001(0)
input.put("RCPS", rcps);
input.put("TRNS", trns);
processFTLTemplate(cfg,input)
}