diff options
Diffstat (limited to 'support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java')
-rw-r--r-- | support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java b/support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java new file mode 100644 index 0000000000..d7ffbddc4d --- /dev/null +++ b/support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: BSD-3-Clause +package org.islandoftex.texplate.util; + +import io.vavr.control.Try; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.TemplateInitException; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.runtime.RuntimeSingleton; +import org.apache.velocity.runtime.parser.ParseException; +import org.islandoftex.texplate.exceptions.TemplateMergingException; +import org.islandoftex.texplate.model.Template; +import org.islandoftex.texplate.model.handlers.Handler; +import org.slf4j.helpers.NOPLoggerFactory; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringReader; +import java.io.Writer; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +/** + * Merging utilities. + * + * @version 1.0 + * @since 1.0 + */ +public class MergingUtils { + + /** + * Merges both template and data. + * + * @param template The template object. + * @param map The data map. + * @param output The output path. + * @param cmap The configuration map. + * @return The length of the generated output. + * @throws TemplateMergingException The merging failed. + */ + private static long mergeTemplate(Template template, + Map<String, String> map, Path output, + Map<String, Object> cmap) + throws TemplateMergingException { + + // create the context map + Map<String, Object> context = handle(template, map, cmap); + + // create a file writer for + // the output reference + try (Writer writer = new FileWriter(output.toFile())) { + + // the document is actually read + // into a string reader + StringReader reader = new StringReader(template.getDocument()); + + // load both runtime services and + // the template model from Velocity + RuntimeServices services = RuntimeSingleton.getRuntimeServices(); + services.addProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, + new NOPLoggerFactory().getLogger("")); + org.apache.velocity.Template reference + = new org.apache.velocity.Template(); + + // set both runtime services + // and document data into the + // template document + reference.setRuntimeServices(services); + reference.setData(services.parse(reader, reference)); + reference.initDocument(); + + // create the context based on + // the data map previously set + VelocityContext entries = new VelocityContext(context); + + // merge both template and data + // into the file writer + reference.merge(entries, writer); + + } catch (IOException | MethodInvocationException + | ParseErrorException | ParseException + | ResourceNotFoundException + | TemplateInitException exception) { + + // an exception has happened, so a + // new exception is thrown, attaching + // the original throwable cause + throw new TemplateMergingException("An error occurred while " + + "trying to merge the template reference with the " + + "provided data. Make sure the template is correct " + + "and try again. The raised exception might give us " + + "some hints on what exactly happened. Typically, " + + "make sure the template strictly follows the " + + "Velocity 2.0 language syntax.", exception); + } + + // simply return the length of + // the generated output file + return output.toFile().length(); + } + + /** + * Merges template and data. + * + * @param template The template model. + * @param map The data map. + * @param output The path reference. + * @param cmap The configuration map. + * @return The length of the generated output, enclosed as a Try object. + */ + public static Try<Long> merge(Template template, Map<String, String> map, + Path output, Map<String, Object> cmap) { + return Try.of(() -> mergeTemplate(template, map, output, cmap)); + } + + /** + * Handles the context map. + * + * @param template The template model. + * @param map The context map. + * @return The new context map. + */ + private static Map<String, Object> handle(Template template, + Map<String, String> map, + Map<String, Object> cmap) { + + // no handlers found + if (template.getHandlers() == null) { + + // create a new map from the + // command line map and put the + // values from the configuration + // file, if absent + Map<String, Object> result = new HashMap<>(map); + cmap.forEach(result::putIfAbsent); + return result; + + } else { + + // get default handlers and + // set the resulting map + Map<String, Handler> handlers = HandlerUtils.getHandlers(); + Map<String, Object> result = new HashMap<>(); + + // check each key from the map + map.forEach((String key, String value) -> { + + // there is a handler for + // the current key + if (template.getHandlers().containsKey(key)) { + + // the handler seems valid + if (handlers.containsKey(template.getHandlers().get(key))) { + + // apply the handler and + // store the value in the map + result.put(key, handlers.get(template. + getHandlers().get(key)).apply(value)); + } else { + + // simply store the value + result.put(key, value); + // TODO: should we warn about an invalid handler? + } + } else { + + // simply store the value + result.put(key, value); + } + }); + + // put remaining values from + // the configuration file, if + // absent + cmap.forEach(result::putIfAbsent); + + // return the map + return result; + } + } + +} |