summaryrefslogtreecommitdiff
path: root/support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java
diff options
context:
space:
mode:
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.java186
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;
+ }
+ }
+
+}