Closed damianhofmannwork closed 1 year ago
There are several approaches to this. One would be to implement lowlevel replacer plugin and inspect the provided value for null. Then track this tag and at the end of processing you should know that there was such cases. You can use something like thread local or configuration setup per processing as this plugins are shared during processing.
You can find low level replace and page 31 of user manual. There is also an example here: https://github.com/ngs-doo/TemplaterExamples/blob/4bb7cce55240c4aa50b19504e867b3a17e59500c/Beginner/MailMerge/src/main/java/hr/ngs/templater/example/MailMergeExample.java#L38
Also, its not clear if thats your case, but Templater has some special logic when it detects unknown tags which "should" be processed. You can control that via UnprocessedTags plugin, which is described on page 34. There is also example here: https://github.com/ngs-doo/TemplaterExamples/blob/6709e6c4e6f65ec09b3c2540e0ea22d4bd19aad0/Intermediate/MissingProperty/src/Program.cs#L25
Also, one more "trick" you can do is instead of replacing it with null in low level replacer, you can replace it with the actual tag value. This way it will look as the tag was not replaced at all, but it should be processed. Also, you can register your own empty handler which will behave differently. Keep in mind that unless you disable built-in handlers, default one will still run. Here is an override for built-in bool handler: https://github.com/ngs-doo/TemplaterExamples/blob/master/Intermediate/BoolOverride/src/main/java/hr/ngs/templater/example/BoolExample.java#L14
Do let me know if you manage to resolve it or get stuck.
Thanks @zapov
I think the first approach is "less invasive". Disabling the built-in handlers (as suggested in your third post) might have side-effects, which I don't fully understand.
Here's what I ended up with, according to your first suggestion:
public class NullValueTracker implements DocumentFactoryBuilder.LowLevelReplacer {
private final Set<String> tagsWithNullValues = new LinkedHashSet<>();
@Override
public Object replace(Object value, String tag, String[] metadata) {
if (value == null) {
tagsWithNullValues.add(tag);
}
return value;
}
public Set<String> getTagsWithNullValues() {
return tagsWithNullValues;
}
}
Then I use it like this:
File templateFile = new File("path/to/my/template.docx");
File outputFile = new File("path/to/my/output.docx");
NullValueTracker nullValueTracker = new NullValueTracker();
DocumentFactory documentFactory = Configuration.builder().include(nullValueTracker).build();
try (InputStream templateStream = Files.newInputStream(templateFile.toPath());
OutputStream outputStream = Files.newOutputStream(outputFile.toPath());
TemplateDocument template = documentFactory.open(templateStream, "docx", outputStream)) {
template.process(businessObject);
// collect unprocessed and empty tags
final var unprocessedTags = new LinkedHashSet<String>();
unprocessedTags.addAll(nullValueTracker.getTagsWithNullValues());
unprocessedTags.addAll(asList(template.templater().tags()));
if (!unprocessedTags.isEmpty()) {
throw new RuntimeException("Incomplete report!");
}
} catch (Exception e) {
throw new RuntimeException("Failed to generate report!", e);
}
This seems to work quite well, but I'm still testing.
I am processing reports, which must be "complete". Complete means, that all tags must be replaced with a valid value. I achieve this by reading the remaining tags raising an exception if the template still contains tags after processing.
During testing, I have noticed that Templater replaces (collapses) tags with empty String, if the property in the input object exists, but has value
null
.I failed to find examples or documentation on how to change this default behavior. I want to configure Templater such that, if a property has value
null
(which means "missing value" in my context), it does not process the tag. I still want to use[[thisTagMayBeNull]:empty(Value Missing)
] in places, where I expectnull
values. But I want the default to leave the tag alone, if there's no value.Can you provide (or point me to) a Java example or the relevant documentation on how to achieve this? Thank you very much.