summaryrefslogtreecommitdiff
path: root/support/texplate/source
diff options
context:
space:
mode:
Diffstat (limited to 'support/texplate/source')
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/Main.java36
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/model/Configuration.java150
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/model/Template.java252
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java391
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/BooleanHandler.java26
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/CSVListHandler.java30
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/util/HandlerUtils.java31
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java186
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/util/MessageUtils.java84
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/util/PathUtils.java129
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/util/ValidatorUtils.java75
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/Main.kt23
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/TemplateProcessing.kt228
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/InvalidKeySetException.kt (renamed from support/texplate/source/main/java/org/islandoftex/texplate/exceptions/InvalidKeySetException.java)17
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/InvalidTemplateException.kt (renamed from support/texplate/source/main/java/org/islandoftex/texplate/exceptions/InvalidTemplateException.java)17
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/TemplateMergingException.kt (renamed from support/texplate/source/main/java/org/islandoftex/texplate/exceptions/TemplateMergingException.java)17
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Configuration.kt67
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Template.kt90
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/BooleanHandler.kt20
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/CSVListHandler.kt22
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/Handler.kt (renamed from support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/Handler.java)8
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/util/HandlerUtils.kt25
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MergingUtils.kt126
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MessageUtils.kt72
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/util/PathUtils.kt69
-rw-r--r--support/texplate/source/main/kotlin/org/islandoftex/texplate/util/ValidatorUtils.kt54
-rw-r--r--support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-article.toml54
-rw-r--r--support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-standalone.toml45
28 files changed, 913 insertions, 1431 deletions
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/Main.java b/support/texplate/source/main/java/org/islandoftex/texplate/Main.java
deleted file mode 100644
index 5c27aab23c..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/Main.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate;
-
-import org.islandoftex.texplate.model.TemplateProcessing;
-import org.islandoftex.texplate.util.MessageUtils;
-import picocli.CommandLine;
-
-/**
- * The main class. The application logic is enclosed in the template processing
- * class.
- *
- * @version 1.0
- * @since 1.0
- */
-public class Main {
-
- /**
- * Main method. Note that it simply passes the control to the template
- * processing class.
- *
- * @param args The command line arguments.
- */
- public static void main(String[] args) {
-
- // draw the application logo in the
- // terminal (please have fixed fonts
- // in your terminal for a nice display)
- MessageUtils.drawLogo();
-
- // calls the command line processing method
- // and performs the actual application logic
- int exitCode = new CommandLine(new TemplateProcessing()).execute(args);
- System.exit(exitCode);
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/model/Configuration.java b/support/texplate/source/main/java/org/islandoftex/texplate/model/Configuration.java
deleted file mode 100644
index a0f8e83702..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/model/Configuration.java
+++ /dev/null
@@ -1,150 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.model;
-
-import com.moandjiezana.toml.Toml;
-import io.vavr.control.Try;
-import org.islandoftex.texplate.exceptions.InvalidTemplateException;
-
-import java.nio.file.Path;
-import java.util.Map;
-
-/**
- * The configuration model.
- *
- * @version 1.0
- * @since 1.0
- */
-public class Configuration {
-
- // template of the template
- private String template;
-
- // map of variables for the configuration
- private Map<String, Object> map;
-
- /**
- * Constructor.
- */
- public Configuration() {
- }
-
- /**
- * Constructor.
- *
- * @param template The template.
- * @param map The map.
- */
- public Configuration(String template, Map<String, Object> map) {
- this.template = template;
- this.map = map;
- }
-
- /**
- * Gets the template.
- *
- * @return The template.
- */
- public String getTemplate() {
- return template;
- }
-
- /**
- * Sets the template.
- *
- * @param template The template.
- */
- public void setTemplate(String template) {
- this.template = template;
- }
-
- /**
- * Gets the map.
- *
- * @return The map.
- */
- public Map<String, Object> getMap() {
- return map;
- }
-
- /**
- * Sets the map.
- *
- * @param map The map.
- */
- public void setMap(Map<String, Object> map) {
- this.map = map;
- }
-
- /**
- * Checks whether the configuration is valid.
- *
- * @return A boolean value indicating whether the configuration is valid.
- */
- private boolean isValid() {
- return !(template == null || map == null);
- }
-
- /**
- * Reads the configuration from path.
- *
- * @param path The path.
- * @return Configuration.
- * @throws InvalidTemplateException The configuration is invalid.
- */
- private static Configuration readFromPath(Path path)
- throws InvalidTemplateException {
-
- // the actual configuration
- Configuration configuration;
-
- // the default message
- String message = "The provided configuration file looks invalid. "
- + "Please make sure the configuration has a valid syntax and "
- + "try again. ";
-
- try {
-
- // gets the configuration
- configuration = new Toml().read(path.toFile()).
- to(Configuration.class);
-
- } catch (Exception exception) {
-
- // the configuration
- // seems invalid
- throw new InvalidTemplateException(message + "In this particular "
- + "scenario, there is a possibility that the configuration "
- + "file does not follow the TOML specification. Please "
- + "refer to the user manual for further details and a "
- + "possible fix. Also, the raised exception message can "
- + "give us some hints on what happened.", exception);
- }
-
- // checks whether the
- // configuration is valid
- if (configuration.isValid()) {
-
- // returns the configuration
- return configuration;
- } else {
-
- // the configuration
- // is invalid
- throw new InvalidTemplateException(message + "Specifically, some "
- + "mandatory fields are either absent or empty in the "
- + "configuration file. It is quite important to strictly "
- + "follow the configuration specification, as detailed in "
- + "the user manual, or the tool will not work at all.");
- }
- }
-
- /**
- * Gets the configuration from a path.
- *
- * @param path The path.
- * @return The configuration.
- */
- public static Try<Configuration> of(Path path) {
- return Try.of(() -> readFromPath(path));
- }
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/model/Template.java b/support/texplate/source/main/java/org/islandoftex/texplate/model/Template.java
deleted file mode 100644
index ca92e04658..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/model/Template.java
+++ /dev/null
@@ -1,252 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.model;
-
-import com.moandjiezana.toml.Toml;
-import io.vavr.control.Try;
-import org.islandoftex.texplate.exceptions.InvalidTemplateException;
-
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The template model.
- *
- * @version 1.0
- * @since 1.0
- */
-public class Template {
-
- // name of the template
- private String name;
-
- // description of the template
- private String description;
-
- // list of authors who wrote the template
- private List<String> authors;
-
- // list of requirements for the template
- private List<String> requirements;
-
- // the document to be configured
- private String document;
-
- // the map handlers
- private Map<String, String> handlers;
-
- /**
- * Constructor.
- */
- public Template() {
- }
-
- /**
- * Constructor.
- *
- * @param name Name of the template.
- * @param description Description of the template.
- * @param authors List of authors of the template.
- * @param requirements List of requirements for the template.
- * @param document The document to be configured.
- * @param handlers The optional map handlers.
- */
- public Template(String name, String description, List<String> authors,
- List<String> requirements, String document,
- Map<String, String> handlers) {
- this.name = name;
- this.description = description;
- this.authors = authors;
- this.requirements = requirements;
- this.document = document;
- this.handlers = handlers;
- }
-
- /**
- * Gets the template name.
- *
- * @return The template name.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Sets the template name.
- *
- * @param name The template name.
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * Gets the template description.
- *
- * @return The template description.
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * Sets the template description.
- *
- * @param description The template description.
- */
- public void setDescription(String description) {
- this.description = description;
- }
-
- /**
- * Gets the template authors.
- *
- * @return The template authors.
- */
- public List<String> getAuthors() {
- return authors;
- }
-
- /**
- * Sets the template authors.
- *
- * @param authors The template authors.
- */
- public void setAuthors(List<String> authors) {
- this.authors = authors;
- }
-
- /**
- * Gets the template requirements.
- *
- * @return The template requirements.
- */
- public List<String> getRequirements() {
- return requirements;
- }
-
- /**
- * Sets the template requirements.
- *
- * @param requirements The template requirements.
- */
- public void setRequirements(List<String> requirements) {
- this.requirements = requirements;
- }
-
- /**
- * Gets the template document.
- *
- * @return The template document.
- */
- public String getDocument() {
- return document;
- }
-
- /**
- * Sets the template document.
- *
- * @param document The template document.
- */
- public void setDocument(String document) {
- this.document = document;
- }
-
- /**
- * Gets the map handlers.
- *
- * @return The map handlers.
- */
- public Map<String, String> getHandlers() {
- return handlers;
- }
-
- /**
- * Sets the map handlers.
- *
- * @param handlers The map handlers.
- */
- public void setHandlers(Map<String, String> handlers) {
- this.handlers = handlers;
- }
-
- /**
- * Checks whether the template is valid.
- *
- * @return A boolean value indicating whether the template is valid.
- */
- private boolean isValid() {
- return !((name == null) || (description == null)
- || (authors == null) || (requirements == null)
- || (document == null) || name.trim().isEmpty()
- || description.trim().isEmpty() || authors.isEmpty()
- || document.trim().isEmpty());
- }
-
- /**
- * Reads the template from the provided path.
- *
- * @param path The path to the template file.
- * @return The template object from the provided path.
- * @throws InvalidTemplateException The template is invalid.
- */
- private static Template readFromPath(Path path)
- throws InvalidTemplateException {
-
- // the actual template
- Template template;
-
- // the exception message, in case the
- // conversion fails or if there are
- // missing elements from the template
- String message = "The provided template file looks invalid. Please "
- + "make sure the template has a valid syntax and try again. ";
-
- try {
-
- // reads the file and converts
- // the TOML format into the object
- template = new Toml().read(path.toFile()).to(Template.class);
-
- } catch (Exception exception) {
-
- // throws the new exception and
- // attaches the original cause
- throw new InvalidTemplateException(message + "In this particular "
- + "scenario, there is a possibility that the template "
- + "file does not follow the TOML specification. Please "
- + "refer to the user manual for further details and a "
- + "possible fix. Also, the raised exception message can "
- + "give us some hints on what happened.", exception);
- }
-
- // the conversion hasn't failed, but we need
- // to check whether the template is valid
- if (template.isValid()) {
-
- // everything went fine, so
- // simply return the template
- return template;
- } else {
-
- // the template is invalid, so we
- // need to throw an exception
- throw new InvalidTemplateException(message + "Specifically, some "
- + "mandatory fields are either absent or empty in the "
- + "template file. It is quite important to strictly "
- + "follow the template specification, as detailed in the "
- + "user manual, or the tool will not work at all.");
- }
- }
-
- /**
- * Returns the template from the provided path.
- *
- * @param path The path in which the template is retrieved.
- * @return The corresponding template, enclosed in a Try object.
- */
- public static Try<Template> of(Path path) {
- return Try.of(() -> readFromPath(path));
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java b/support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java
deleted file mode 100644
index eccaf6c732..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java
+++ /dev/null
@@ -1,391 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.model;
-
-import io.vavr.control.Either;
-import org.islandoftex.texplate.util.MergingUtils;
-import org.islandoftex.texplate.util.MessageUtils;
-import org.islandoftex.texplate.util.PathUtils;
-import org.islandoftex.texplate.util.ValidatorUtils;
-import picocli.CommandLine;
-import picocli.CommandLine.Option;
-
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-
-/**
- * The template processing class.
- *
- * @version 1.0
- * @since 1.0
- */
-@CommandLine.Command(
- usageHelpWidth = 70,
- name = "texplate"
-)
-public class TemplateProcessing implements Callable<Integer> {
-
- // the file output, which will hold the
- // result of the merging of both template
- // and context data map from command line
- @Option(
- names = {"-o", "--output"},
- description = "The output file in which the chosen "
- + "template will be effectively written. Make sure "
- + "the directory has the correct permissions for "
- + "writing the output file.",
- required = true,
- type = Path.class
- )
- private Path output;
-
- // the template name
- @Option(
- names = {"-t", "--template"},
- description = "The template and. The tool will "
- + "search both user and system locations and set the "
- + "template model accordingly, based on your specs."
- )
- private String template;
-
- // the context data map that holds
- // a set of key/value pairs to be
- // merged with the template
- @Option(
- names = {"-m", "--map"},
- description = "The contextual map that provides the "
- + "data to be merged in the template. This parameter "
- + "can be used multiple times. You can specify a map "
- + "entry with the key=value syntax (mind the entry "
- + "separator).",
- arity = "1..*"
- )
- private Map<String, String> map;
-
- @Option(
- names = {"-c", "--config"},
- description = "The configuration file in which the tool "
- + "can read template data, for automation purposes. Make "
- + "sure to follow the correct specification when writing "
- + "a configuration file.",
- type = Path.class
- )
- private Path configuration;
-
- /**
- * The application logic, enclosed as a call method.
- *
- * @return An integer value denoting the exit status.
- * @throws Exception An exception was raised in the application logic.
- */
- @Override
- public Integer call() throws Exception {
-
- // the exit status, originally
- // set as a valid value
- int exit = 0;
-
- // configuration halt flag, indicating
- // whether the tool has to end earlier
- boolean halt = false;
-
- Map<String, Object> cmap = new HashMap<>();
-
- // ensure the context data map
- // is at least instantiated
- ensureMap();
-
- // there is a configuration file
- // found in the command line
- if (has(configuration)) {
-
- // print the configuration
- // check line for status
- MessageUtils.line("Checking configuration");
-
- // try to read the configuration
- // file into a proper object
- Either<Throwable, Configuration> configChecking = Configuration.
- of(configuration).toEither();
-
- // the configuration file
- // seems to be valid, proceed
- if (configChecking.isRight()) {
-
- // print status line
- MessageUtils.status(true);
-
- // get the configuration file
- Configuration config = configChecking.get();
-
- // check if the configuration
- // has a proper template
- if (has(config.getTemplate())) {
-
- // if so, build the template if, and
- // only if, there's no one already
- // set through command line
- template = ensure(template, config.getTemplate());
- }
-
- // check if the configuration
- // has a proper string/string map
- if (has(config.getMap())) {
-
- // set the main configuration
- // map to be dealt later on
- cmap = config.getMap();
- }
-
- // print header about tidying
- // up configuration variables
- MessageUtils.line("Adjusting variables from file");
- MessageUtils.status(true);
- System.out.println();
-
- } else {
-
- // an error occurred, print it
- // set exit code and halt
- MessageUtils.status(false);
- MessageUtils.error(configChecking.getLeft());
- exit = -1;
- halt = true;
- }
- } else {
-
- // print header regarding
- // no config file found
- MessageUtils.line("Configuration file mode disabled");
- MessageUtils.status(true);
-
- // print a header regarding
- // full command line mode
- MessageUtils.line("Entering full command line mode");
-
- // there's no configuration file, so we
- // need to check whether there is not a
- // pattern set in the command line
- if (!has(template)) {
-
- // print status
- MessageUtils.status(false);
-
- // print message
- MessageUtils.error(new Exception("The template was not set "
- + "in the command line through the -t/--template "
- + "option. If not explicitly specified in a "
- + "configuration file, this option becomes mandatory, "
- + "so make sure to define it either in the command "
- + "line or in a proper configuration file."));
-
- exit = -1;
- halt = true;
-
- } else {
-
- // print status
- MessageUtils.status(true);
- System.out.println();
- }
-
- }
-
- // check whether we should
- // halt prematurely
- if (!halt) {
-
- // initial message, preparing our
- // hearts to the actual merging :)
- System.out.println("Please, wait...");
- System.out.println();
-
- // now we need to obtain the actual
- // template from a file stored either
- // in the user home or in the system
- MessageUtils.line("Obtaining reference");
-
- // let us try to get the corresponding
- // file from the template pattern
- Either<Throwable, Path> fileChecking = PathUtils.
- getTemplatePath(template).toEither();
-
- // the actual template file was
- // found, so we can proceed to
- // the next phase
- if (fileChecking.isRight()) {
-
- // updates the current
- // status accordingly
- MessageUtils.status(true);
-
- // now it's time to compose the template
- // object from its corresponding file
- MessageUtils.line("Composing template");
-
- // attempts to retrieve the template
- // attributes from the referenced file
- // to the actual template object
- Either<Throwable, Template> templateComposition
- = Template.of(fileChecking.get()).toEither();
-
- // the template composition was successful,
- // so we can move on to the next phase
- if (templateComposition.isRight()) {
-
- // updates the current
- // status accordingly
- MessageUtils.status(true);
-
- // once the template object is populated,
- // we need to verify if both template and
- // data map are not somehow conflicting
- MessageUtils.line("Validating data");
- Either<Throwable, Map<String, String>> dataValidation
- = ValidatorUtils.validate(templateComposition.get(),
- map).toEither();
-
- // the data validation was consistent,
- // so now the merging can be applied
- if (dataValidation.isRight()) {
-
- // updates the current
- // status accordingly
- MessageUtils.status(true);
-
- // now it's the final phase, in which
- // both template and data are merged
- MessageUtils.line("Merging template and data");
-
- // merge both template and context data
- // map
- Either<Throwable, Long> merging = MergingUtils.
- merge(templateComposition.get(),
- dataValidation.get(),
- output, cmap).toEither();
-
- // the merging was successful,
- // so now there's nothing else
- // to do, yay!
- if (merging.isRight()) {
-
- // updates the current
- // status accordingly
- MessageUtils.status(true);
-
- // print the final messge and
- // tell the user everything
- // went smooth!
- System.out.println();
- System.out.println("Done! Enjoy your template!");
- System.out.println("Written: "
- + getSize(merging.get()));
- } else {
-
- // updates the current
- // status accordingly
- MessageUtils.status(false);
-
- // the merging failed, so the
- // exception is displayed and
- // the exit status is updated
- MessageUtils.error(merging.getLeft());
- exit = -1;
- }
- } else {
-
- // updates the current
- // status accordingly
- MessageUtils.status(false);
-
- // the data validation failed, so
- // the exception is displayed and
- // the exit status is updated
- MessageUtils.error(dataValidation.getLeft());
- exit = -1;
- }
- } else {
-
- // updates the current
- // status accordingly
- MessageUtils.status(false);
-
- // the template composition failed,
- // so the exception is displayed and
- // the exit status is updated
- MessageUtils.error(templateComposition.getLeft());
- exit = -1;
- }
- } else {
-
- // updates the current
- // status accordingly
- MessageUtils.status(false);
-
- // the file checking failed, so
- // the exception is displayed and
- // the exit status is updated
- MessageUtils.error(fileChecking.getLeft());
- exit = -1;
- }
- }
-
- // the exit status is returned,
- // denoting whether the application
- // was able to merge both template
- // and data accordingly
- return exit;
- }
-
- /**
- * Ensures the data map is never pointed to a null reference.
- */
- private void ensureMap() {
-
- // if the map is null, simply
- // create a new instance
- if (!has(map)) {
- map = new HashMap<>();
- }
- }
-
- /**
- * Gets the file size in a human readable format.
- *
- * @param bytes The file size, in bytes.
- * @return The file size in a human readable format.
- */
- private String getSize(long bytes) {
- if (bytes < 1024) {
- return bytes + " B";
- } else {
- int exponent = (int) (Math.log(bytes) / Math.log(1024));
- return String.format("%.1f %cB", bytes / Math.pow(1024, exponent),
- "KMGTPE".charAt(exponent - 1));
- }
- }
-
- /**
- * Ensures the first parameter is not null, or sets it to the second one.
- *
- * @param <T> The type.
- * @param first First parameter.
- * @param second Second parameter.
- * @return Either the first or the second one.
- */
- private <T> T ensure(T first, T second) {
- return !has(first) ? second : first;
- }
-
- /**
- * Checks whether the object exists.
- *
- * @param object The objects.
- * @return Boolean value indicating whether the object exists.
- */
- private boolean has(Object object) {
- return object != null;
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/BooleanHandler.java b/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/BooleanHandler.java
deleted file mode 100644
index 461b9fd170..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/BooleanHandler.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.model.handlers;
-
-import java.util.Arrays;
-
-/**
- * Implements a boolean handler.
- *
- * @version 1.0
- * @since 1.0
- */
-public class BooleanHandler implements Handler {
-
- /**
- * Applies the conversion to the string.
- *
- * @param string The string.
- * @return A list.
- */
- @Override
- public Object apply(String string) {
- return Arrays.asList("true", "1", "yes")
- .contains(string.toLowerCase());
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/CSVListHandler.java b/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/CSVListHandler.java
deleted file mode 100644
index 4b3d0c2e69..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/CSVListHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.model.handlers;
-
-import java.util.Arrays;
-import java.util.stream.Collectors;
-
-/**
- * Implements a CSV list handler.
- *
- * @version 1.0
- * @since 1.0
- */
-public class CSVListHandler implements Handler {
-
- /**
- * Applies the conversion to the string.
- *
- * @param string The string.
- * @return A list.
- */
- @Override
- public Object apply(String string) {
- return Arrays.stream(string.split(",(?=(?:[^\"]*\""
- + "[^\"]*\")*[^\"]*$)"))
- .map(String::trim)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.toList());
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/util/HandlerUtils.java b/support/texplate/source/main/java/org/islandoftex/texplate/util/HandlerUtils.java
deleted file mode 100644
index 7ab01f7328..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/util/HandlerUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.util;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.islandoftex.texplate.model.handlers.BooleanHandler;
-import org.islandoftex.texplate.model.handlers.CSVListHandler;
-import org.islandoftex.texplate.model.handlers.Handler;
-
-/**
- * Provides the map of handlers.
- *
- * @version 1.0
- * @since 1.0
- */
-public class HandlerUtils {
-
- /**
- * Gets the map of handlers.
- *
- * @return Map of handlers.
- */
- public static Map<String, Handler> getHandlers() {
- Map<String, Handler> handlers = new HashMap<>();
- handlers.put("to-csv-list", new CSVListHandler());
- handlers.put("to-boolean", new BooleanHandler());
- return handlers;
- }
-
-}
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
deleted file mode 100644
index d7ffbddc4d..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/util/MergingUtils.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// 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;
- }
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/util/MessageUtils.java b/support/texplate/source/main/java/org/islandoftex/texplate/util/MessageUtils.java
deleted file mode 100644
index 7649a60e82..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/util/MessageUtils.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.util;
-
-import org.apache.commons.text.TextStringBuilder;
-import org.apache.commons.text.WordUtils;
-
-import java.time.LocalDate;
-
-/**
- * Message helper methods.
- *
- * @version 1.0
- * @since 1.0
- */
-public class MessageUtils {
-
- // the message width
- private static final int WIDTH = 60;
-
- // the application version
- private static final String VERSION = "1.0.1";
-
- /**
- * Prints a line in the terminal, without a line break.
- *
- * @param message The message to be printed.
- */
- public static void line(String message) {
- System.out.print(new TextStringBuilder()
- .appendFixedWidthPadRight(message.concat(" "), WIDTH - 9, '.')
- .append(" ")
- .toString()
- );
- }
-
- /**
- * Prints the status in the terminal.
- *
- * @param result The boolean value.
- */
- public static void status(boolean result) {
- System.out.println(result ? "[ DONE ]" : "[FAILED]");
- }
-
- /**
- * Prints the error in the terminal.
- *
- * @param throwable The throwable reference.
- */
- public static void error(Throwable throwable) {
- System.out.println(
- new TextStringBuilder("\n")
- .appendFixedWidthPadRight("HOUSTON, WE'VE GOT"
- + " A PROBLEM ", WIDTH, '-')
- .append("\n")
- .appendln(WordUtils.wrap(throwable.getMessage(), WIDTH))
- .appendFixedWidthPadLeft("", WIDTH, '-')
- .append("\n")
- .toString());
- }
-
- /**
- * Prints the application logo in the terminal.
- */
- public static void drawLogo() {
- System.out.println(
- " ______ __ __ ___ __ \n" +
- "/\\__ _\\ /\\ \\ /\\ \\ /\\_ \\ /\\ \\__ \n" +
- "\\/_/\\ \\/ __ \\ `\\`\\/'/' _____\\//\\ \\ __ \\ \\ ,_\\ __ \n" +
- " \\ \\ \\ /'__`\\`\\/ > < /\\ '__`\\\\ \\ \\ /'__`\\ \\ \\ \\/ /'__`\\ \n" +
- " \\ \\ \\/\\ __/ \\/'/\\`\\\\ \\ \\L\\ \\\\_\\ \\_/\\ \\L\\.\\_\\ \\ \\_/\\ __/ \n" +
- " \\ \\_\\ \\____\\ /\\_\\\\ \\_\\ \\ ,__//\\____\\ \\__/.\\_\\\\ \\__\\ \\____\\\n" +
- " \\/_/\\/____/ \\/_/ \\/_/\\ \\ \\/ \\/____/\\/__/\\/_/ \\/__/\\/____/\n" +
- " \\ \\_\\ \n" +
- " \\/_/ \n"
- );
- System.out.println(
- "TeXplate " + VERSION + ", a document structure creation tool\n" +
- "Copyright (c) " + LocalDate.now().getYear() + ", Island of TeX\n" +
- "All rights reserved.\n"
- );
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/util/PathUtils.java b/support/texplate/source/main/java/org/islandoftex/texplate/util/PathUtils.java
deleted file mode 100644
index fa242c4980..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/util/PathUtils.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.util;
-
-import io.vavr.control.Try;
-import org.islandoftex.texplate.Main;
-
-import java.io.FileNotFoundException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-/**
- * Helper methods for path handling.
- *
- * @version 1.0
- * @since 1.0
- */
-public class PathUtils {
-
- // the templates folder
- private static final String TEMPLATES_FOLDER = "templates";
-
- // the user application folder
- private static final String USER_APPLICATION_FOLDER = ".texplate";
-
- /**
- * Gets the application path.
- *
- * @return The application path.
- */
- private static Path getApplicationPath() {
- return Try.of(() -> Paths.get(Main.class.getProtectionDomain().
- getCodeSource().getLocation().toURI().getPath()).resolve("..").
- normalize()).getOrElse(Paths.get("."));
- }
-
- /**
- * Gets the default template path.
- *
- * @return The default template path.
- */
- private static Path getDefaultTemplatePath() {
- return getApplicationPath().resolve(TEMPLATES_FOLDER);
- }
-
- /**
- * Gets the user template path.
- *
- * @return The user template path.
- */
- private static Path getUserTemplatePath() {
- return Try.of(() -> Paths.get(System.getProperty("user.home"),
- USER_APPLICATION_FOLDER, TEMPLATES_FOLDER)).
- getOrElse(Paths.get("."));
- }
-
- /**
- * Searchs all paths looking for the provided template.
- *
- * @param name The name to be associated to a template file.
- * @return The corresponding template file.
- * @throws FileNotFoundException The template file could not be found.
- */
- private static Path searchTemplatePath(String name)
- throws FileNotFoundException {
- // the file has to be a TOML format,
- // so we add the extension
- name = name.concat(".toml");
-
- // the first reference is based on the
- // user template path resolved with the
- // file name
- Path reference = getUserTemplatePath().resolve(name);
-
- // if the file actually exists,
- // the search is done!
- if (Files.exists(reference)) {
- // return the template
- // file reference
- return reference;
-
- } else {
- // the reference was not found in the
- // user location, so let us try the
- // system counterpart
- reference = getDefaultTemplatePath().resolve(name);
-
- // if the file actually exists,
- // the search is done!
- if (Files.exists(reference)) {
- // return the template
- // file reference
- return reference;
-
- } else {
- // in the system path, look for an `texplate` prefixed
- // version as well
- reference = getDefaultTemplatePath().resolve("texplate-" + name);
-
- if (Files.exists(reference)) {
- // return the template
- // file reference
- return reference;
- } else {
- // the file reference could not be
- // found, so an exception is thrown
- throw new FileNotFoundException("I am sorry, but the template "
- + "file '" + name + "' could not be found in the "
- + "default template locations (system and user). Make "
- + "sure the reference is correct and try again. For "
- + "reference, these are the paths I searched: '"
- + getUserTemplatePath() + "' and '"
- + getDefaultTemplatePath() + "' (in this order).");
- }
- }
- }
- }
-
- /**
- * Gets the template path based on the provided template name.
- *
- * @param name The template name.
- * @return The template path, enclosed in a Try object.
- */
- public static Try<Path> getTemplatePath(String name) {
- return Try.of(() -> searchTemplatePath(name));
- }
-
-}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/util/ValidatorUtils.java b/support/texplate/source/main/java/org/islandoftex/texplate/util/ValidatorUtils.java
deleted file mode 100644
index b400731242..0000000000
--- a/support/texplate/source/main/java/org/islandoftex/texplate/util/ValidatorUtils.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.util;
-
-import io.vavr.control.Try;
-import org.islandoftex.texplate.exceptions.InvalidKeySetException;
-import org.islandoftex.texplate.model.Template;
-
-import java.util.Map;
-
-/**
- * Helper methods for validation.
- *
- * @version 1.0
- * @since 1.0
- */
-public class ValidatorUtils {
-
- /**
- * Validates the data map based on the template requirements.
- *
- * @param template The template.
- * @param map The data map.
- * @return A boolean value indicating whether the data map is valid.
- */
- private static boolean validateRequirements(Template template,
- Map<String, String> map) {
- return template.getRequirements().isEmpty() ||
- template.getRequirements().containsAll(map.keySet());
- }
-
- /**
- * Validates the template pattern and the data map and throws an exception
- * in case of failure.
- *
- * @param template The template.
- * @param map The data map.
- * @return The data map.
- * @throws InvalidKeySetException There are invalid keys in the map.
- */
- private static Map<String, String> checkValidation(Template template,
- Map<String, String> map)
- throws InvalidKeySetException {
-
- // for starters, we try to validate
- // the template requirements
- if (validateRequirements(template, map)) {
-
- // everything is validated, so
- // we simply return the map
- return map;
-
- } else {
-
- // the requirements were missing,
- // so an exception is thrown
- throw new InvalidKeySetException("The provided map does not "
- + "contain all the keys required by the chosen "
- + "template. Make sure to define such keys and try "
- + "again. Check the user manual for further details.");
- }
- }
-
- /**
- * Validates the template pattern and the data map.
- *
- * @param template The template.
- * @param map The data map.
- * @return The data map, enclosed in a Try object.
- */
- public static Try<Map<String, String>> validate(Template template,
- Map<String, String> map) {
- return Try.of(() -> checkValidation(template, map));
- }
-
-}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/Main.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/Main.kt
new file mode 100644
index 0000000000..73de15f8c3
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/Main.kt
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate
+
+import kotlin.system.exitProcess
+import org.islandoftex.texplate.util.MessageUtils
+import picocli.CommandLine
+
+/**
+ * Main method. Note that it simply passes the control to the template
+ * processing class.
+ *
+ * @param args The command line arguments.
+ */
+fun main(args: Array<String>) {
+ // draw the application logo in the terminal (please have fixed fonts
+ // in your terminal for a nice display)
+ MessageUtils.drawLogo()
+ // calls the command line processing method and performs the actual
+ // application logic
+ @Suppress("SpreadOperator")
+ val exitCode = CommandLine(TemplateProcessing()).execute(*args)
+ exitProcess(exitCode)
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/TemplateProcessing.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/TemplateProcessing.kt
new file mode 100644
index 0000000000..659a0df631
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/TemplateProcessing.kt
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate
+
+import java.nio.file.Path
+import java.util.concurrent.Callable
+import kotlin.math.ln
+import kotlin.math.pow
+import org.islandoftex.texplate.exceptions.InvalidTemplateException
+import org.islandoftex.texplate.model.Configuration
+import org.islandoftex.texplate.model.Template
+import org.islandoftex.texplate.util.MergingUtils.mergeTemplate
+import org.islandoftex.texplate.util.MessageUtils.error
+import org.islandoftex.texplate.util.MessageUtils.line
+import org.islandoftex.texplate.util.MessageUtils.status
+import org.islandoftex.texplate.util.PathUtils.getTemplatePath
+import org.islandoftex.texplate.util.ValidatorUtils.validate
+import picocli.CommandLine
+
+/**
+ * The template processing class.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+@CommandLine.Command(usageHelpWidth = 70, name = "texplate")
+class TemplateProcessing : Callable<Int> {
+ // the file output, which will hold the result of the merging of both template
+ // and context data map from command line
+ @CommandLine.Option(
+ names = ["-o", "--output"],
+ description = ["The output file in which the chosen " +
+ "template will be effectively written. Make sure " +
+ "the directory has the correct permissions for " +
+ "writing the output file."],
+ required = true,
+ type = [Path::class]
+ )
+ private val output: Path? = null
+
+ // the template name
+ @CommandLine.Option(
+ names = ["-t", "--template"],
+ description = ["The name of the template. The tool will " +
+ "search both user and system locations and set the " +
+ "template model accordingly, based on your specs."]
+ )
+ private var template: String? = null
+
+ // the context data map that holds a set of key/value pairs to be merged
+ // with the template
+ @CommandLine.Option(
+ names = ["-m", "--map"],
+ description = ["The contextual map that provides the " +
+ "data to be merged in the template. This parameter " +
+ "can be used multiple times. You can specify a map " +
+ "entry with the key=value syntax (mind the entry " +
+ "separator)."],
+ arity = "1..*"
+ )
+ private var map: Map<String, String>? = null
+
+ @CommandLine.Option(
+ names = ["-c", "--config"],
+ description = ["The configuration file in which the tool " +
+ "can read template data, for automation purposes. Make " +
+ "sure to follow the correct specification when writing " +
+ "a configuration file."],
+ type = [Path::class]
+ )
+ private val configuration: Path? = null
+
+ /**
+ * The application logic, enclosed as a call method.
+ *
+ * @return An integer value denoting the exit status.
+ * @throws Exception An exception was raised in the application logic.
+ */
+ @Throws(Exception::class)
+ override fun call(): Int {
+ // the exit status, originally set as a valid value
+ var exit = 0
+ // configuration halt flag, indicating whether the tool has to end earlier
+ var halt = false
+ var cmap: Map<String, Any> = mutableMapOf()
+ // ensure the context data map is at least instantiated
+ ensureMap()
+ // there is a configuration file found in the command line
+ if (has(configuration)) {
+ line("Checking configuration")
+ try {
+ val config = Configuration.fromPath(configuration!!)
+ // the configuration file seems to be valid, proceed
+ status(true)
+ // check if the configuration has a proper template
+ if (has(config.template)) {
+ // if so, build the template if, and only if, there's no one already
+ // set through command line
+ template = ensure(template, config.template)
+ }
+ // check if the configuration has a proper string/string map
+ if (has(config.map)) {
+ // set the main configuration map to be dealt later on
+ cmap = config.map
+ }
+ line("Adjusting variables from file")
+ status(true)
+ println()
+ } catch (e: InvalidTemplateException) {
+ // an error occurred, print it, set exit code and halt
+ status(false)
+ error(e)
+ exit = -1
+ halt = true
+ }
+ } else {
+ line("Configuration file mode disabled")
+ status(true)
+ line("Entering full command line mode")
+ // there's no configuration file, so we need to check whether there is
+ // not a pattern set in the command line
+ if (!has(template)) {
+ status(false)
+ error(Exception("The template was not set " +
+ "in the command line through the -t/--template " +
+ "option. If not explicitly specified in a " +
+ "configuration file, this option becomes mandatory, " +
+ "so make sure to define it either in the command " +
+ "line or in a proper configuration file."))
+ exit = -1
+ halt = true
+ } else {
+ status(true)
+ println()
+ }
+ }
+ // check whether we should halt prematurely
+ if (!halt) {
+ println("Please, wait...")
+ println()
+ // now we need to obtain the actual template from a file stored either
+ // in the user home or in the system
+ line("Obtaining reference")
+ try {
+ // let us try to get the corresponding file from the template pattern
+ val file = getTemplatePath(template!!)
+ // the actual template file was found, so we can proceed to
+ // the next phase
+ status(true)
+ line("Composing template")
+ // attempts to retrieve the template attributes from the referenced file
+ // to the actual template object
+ val template = Template.fromPath(file)
+ // the template composition was successful, so we can move on to the
+ // next phase
+ status(true)
+ // once the template object is populated, we need to verify if both
+ // template and data map are not somehow conflicting
+ line("Validating data")
+ val validatedData = validate(template, map!!)
+ // the data validation was consistent, so now the merging can be
+ // applied
+ status(true)
+ line("Merging template and data")
+ val merged = mergeTemplate(template, validatedData, output!!,
+ cmap)
+ status(true)
+ println()
+ println("Done! Enjoy your template!")
+ println("Written: " + getSize(merged))
+ } catch (e: InvalidTemplateException) {
+ status(false)
+ error(e)
+ exit = -1
+ }
+ }
+ // the exit status is returned, denoting whether the application was able
+ // to merge both template and data accordingly
+ return exit
+ }
+
+ /**
+ * Ensures the data map is never pointed to a null reference.
+ */
+ private fun ensureMap() {
+ if (!has(map)) {
+ map = mutableMapOf()
+ }
+ }
+
+ /**
+ * Gets the file size in a human readable format.
+ *
+ * @param bytes The file size, in bytes.
+ * @return The file size in a human readable format.
+ */
+ @Suppress("MagicNumber")
+ private fun getSize(bytes: Long): String {
+ return if (bytes < 1024) {
+ "$bytes B"
+ } else {
+ val exponent = (ln(bytes.toDouble()) / ln(1024.0)).toInt()
+ "%.1f %cB".format(bytes / 1024.0.pow(exponent.toDouble()),
+ "KMGTPE"[exponent - 1])
+ }
+ }
+
+ /**
+ * Ensures the first parameter is not null, or sets it to the second one.
+ *
+ * @param <T> The type.
+ * @param first First parameter.
+ * @param second Second parameter.
+ * @return Either the first or the second one.
+ </T> */
+ private fun <T> ensure(first: T, second: T): T {
+ return if (!has(first)) second else first
+ }
+
+ /**
+ * Checks whether the object exists.
+ *
+ * @param obj The object.
+ * @return Boolean value indicating whether the object exists.
+ */
+ private fun has(obj: Any?): Boolean {
+ return obj != null
+ }
+}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/exceptions/InvalidKeySetException.java b/support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/InvalidKeySetException.kt
index 3d113a2599..72ff3bcbd4 100644
--- a/support/texplate/source/main/java/org/islandoftex/texplate/exceptions/InvalidKeySetException.java
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/InvalidKeySetException.kt
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.exceptions;
+package org.islandoftex.texplate.exceptions
/**
* Handles exceptions when the context map contains invalid keys.
@@ -7,22 +7,18 @@ package org.islandoftex.texplate.exceptions;
* @version 1.0
* @since 1.0
*/
-public class InvalidKeySetException extends Exception {
-
+class InvalidKeySetException : Exception {
/**
* Constructor.
*/
- public InvalidKeySetException() {
- }
+ constructor()
/**
* Constructor.
*
* @param message Message to be attached to the exception.
*/
- public InvalidKeySetException(String message) {
- super(message);
- }
+ constructor(message: String?) : super(message)
/**
* Constructor.
@@ -30,8 +26,5 @@ public class InvalidKeySetException extends Exception {
* @param message Message to be attached to the exception.
* @param cause The throwable cause to be forwarded.
*/
- public InvalidKeySetException(String message, Throwable cause) {
- super(message, cause);
- }
-
+ constructor(message: String?, cause: Throwable?) : super(message, cause)
}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/exceptions/InvalidTemplateException.java b/support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/InvalidTemplateException.kt
index 73c8276998..678c390055 100644
--- a/support/texplate/source/main/java/org/islandoftex/texplate/exceptions/InvalidTemplateException.java
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/InvalidTemplateException.kt
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.exceptions;
+package org.islandoftex.texplate.exceptions
/**
* Handles exceptions when the template is somehow invalid.
@@ -7,19 +7,15 @@ package org.islandoftex.texplate.exceptions;
* @version 1.0
* @since 1.0
*/
-public class InvalidTemplateException extends Exception {
-
- public InvalidTemplateException() {
- }
+class InvalidTemplateException : Exception {
+ constructor()
/**
* Constructor.
*
* @param message Message to be attached to the exception.
*/
- public InvalidTemplateException(String message) {
- super(message);
- }
+ constructor(message: String?) : super(message)
/**
* Constructor.
@@ -27,8 +23,5 @@ public class InvalidTemplateException extends Exception {
* @param message Message to be attached to the exception.
* @param cause The throwable cause to be forwarded.
*/
- public InvalidTemplateException(String message, Throwable cause) {
- super(message, cause);
- }
-
+ constructor(message: String?, cause: Throwable?) : super(message, cause)
}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/exceptions/TemplateMergingException.java b/support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/TemplateMergingException.kt
index 86dd99af00..e1760f8732 100644
--- a/support/texplate/source/main/java/org/islandoftex/texplate/exceptions/TemplateMergingException.java
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/exceptions/TemplateMergingException.kt
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.exceptions;
+package org.islandoftex.texplate.exceptions
/**
* Handles exceptions when the template merging failed.
@@ -7,22 +7,18 @@ package org.islandoftex.texplate.exceptions;
* @version 1.0
* @since 1.0
*/
-public class TemplateMergingException extends Exception {
-
+class TemplateMergingException : Exception {
/**
* Constructor.
*/
- public TemplateMergingException() {
- }
+ constructor()
/**
* Constructor.
*
* @param message Message to be attached to the exception.
*/
- public TemplateMergingException(String message) {
- super(message);
- }
+ constructor(message: String?) : super(message)
/**
* Constructor.
@@ -30,8 +26,5 @@ public class TemplateMergingException extends Exception {
* @param message Message to be attached to the exception.
* @param cause The throwable cause to be forwarded.
*/
- public TemplateMergingException(String message, Throwable cause) {
- super(message, cause);
- }
-
+ constructor(message: String?, cause: Throwable?) : super(message, cause)
}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Configuration.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Configuration.kt
new file mode 100644
index 0000000000..8d37cbb39a
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Configuration.kt
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.model
+
+import com.moandjiezana.toml.Toml
+import java.nio.file.Path
+import org.islandoftex.texplate.exceptions.InvalidTemplateException
+
+/**
+ * The configuration model.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+data class Configuration(
+ /**
+ * The template of the template.
+ */
+ val template: String? = null,
+ /**
+ * Map of variables for the configuration.
+ */
+ val map: Map<String, Any> = mapOf()
+) {
+ /**
+ * Whether the configuration is valid.
+ */
+ private val isValid: Boolean
+ get() = template != null
+
+ companion object {
+ /**
+ * Reads the configuration from path.
+ *
+ * @param path The path.
+ * @return Configuration.
+ * @throws InvalidTemplateException The configuration is invalid.
+ */
+ @Throws(InvalidTemplateException::class)
+ fun fromPath(path: Path): Configuration {
+ val configuration: Configuration
+ val message = ("The provided configuration file looks invalid. " +
+ "Please make sure the configuration has a valid syntax and " +
+ "try again. ")
+ configuration = try {
+ // gets the configuration
+ Toml().read(path.toFile()).to(Configuration::class.java)
+ } catch (exception: IllegalStateException) {
+ // the configuration seems invalid
+ throw InvalidTemplateException(message + "In this particular " +
+ "scenario, there is a possibility that the configuration " +
+ "file does not follow the TOML specification. Please " +
+ "refer to the user manual for further details and a " +
+ "possible fix. Also, the raised exception message can " +
+ "give us some hints on what happened.", exception)
+ }
+ return if (configuration.isValid) {
+ configuration
+ } else {
+ throw InvalidTemplateException(message + "Specifically, some " +
+ "mandatory fields are either absent or empty in the " +
+ "configuration file. It is quite important to strictly " +
+ "follow the configuration specification, as detailed in " +
+ "the user manual, or the tool will not work at all.")
+ }
+ }
+ }
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Template.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Template.kt
new file mode 100644
index 0000000000..82616242de
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/Template.kt
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.model
+
+import com.moandjiezana.toml.Toml
+import java.nio.file.Path
+import org.islandoftex.texplate.exceptions.InvalidTemplateException
+
+/**
+ * The template model.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+data class Template(
+ /**
+ * Name of the template
+ */
+ val name: String? = null,
+ /**
+ * Description of the template
+ */
+ val description: String? = null,
+ /**
+ * List of authors who wrote the template
+ */
+ val authors: List<String> = listOf(),
+ /**
+ * List of requirements for the template
+ */
+ val requirements: List<String> = listOf(),
+ /**
+ * The document to be configured
+ */
+ val document: String? = null,
+ /**
+ * The map handlers
+ */
+ val handlers: Map<String, String> = mapOf()
+) {
+ /**
+ * Checks whether the template is valid.
+ *
+ * @return A boolean value indicating whether the template is valid.
+ */
+ private val isValid: Boolean
+ get() = !(name == null || description == null ||
+ document == null || name.isBlank() ||
+ description.isBlank() || authors.isEmpty() ||
+ document.isBlank())
+
+ companion object {
+ /**
+ * Reads the template from the provided path.
+ *
+ * @param path The path to the template file.
+ * @return The template object from the provided path.
+ * @throws InvalidTemplateException The template is invalid.
+ */
+ @JvmStatic
+ @Throws(InvalidTemplateException::class)
+ fun fromPath(path: Path): Template {
+ val template: Template
+ // the exception message, in case the conversion fails or if there are
+ // missing elements from the template
+ val message = ("The provided template file looks invalid. Please " +
+ "make sure the template has a valid syntax and try again. ")
+ template = try {
+ Toml().read(path.toFile()).to(Template::class.java)
+ } catch (exception: IllegalStateException) {
+ throw InvalidTemplateException(message + "In this particular " +
+ "scenario, there is a possibility that the template " +
+ "file does not follow the TOML specification. Please " +
+ "refer to the user manual for further details and a " +
+ "possible fix. Also, the raised exception message can " +
+ "give us some hints on what happened.", exception)
+ }
+ // the conversion hasn't failed, but we need to check whether the
+ // template is valid
+ return if (template.isValid) {
+ template
+ } else {
+ throw InvalidTemplateException(message + "Specifically, some " +
+ "mandatory fields are either absent or empty in the " +
+ "template file. It is quite important to strictly " +
+ "follow the template specification, as detailed in the " +
+ "user manual, or the tool will not work at all.")
+ }
+ }
+ }
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/BooleanHandler.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/BooleanHandler.kt
new file mode 100644
index 0000000000..cbb74a532f
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/BooleanHandler.kt
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.model.handlers
+
+/**
+ * Implements a boolean handler.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+class BooleanHandler : Handler {
+ /**
+ * Applies the conversion to the string.
+ *
+ * @param string The string.
+ * @return A list.
+ */
+ override fun apply(string: String?): Any? {
+ return listOf("true", "1", "yes").contains(string!!.toLowerCase())
+ }
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/CSVListHandler.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/CSVListHandler.kt
new file mode 100644
index 0000000000..77424ed463
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/CSVListHandler.kt
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.model.handlers
+
+/**
+ * Implements a CSV list handler.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+class CSVListHandler : Handler {
+ /**
+ * Applies the conversion to the string.
+ *
+ * @param string The string.
+ * @return A list.
+ */
+ override fun apply(string: String?): Any? {
+ return string?.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)".toRegex())
+ ?.map { it.trim() }
+ ?.filter { it.isNotEmpty() }
+ }
+}
diff --git a/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/Handler.java b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/Handler.kt
index 721224a978..47b59a09a5 100644
--- a/support/texplate/source/main/java/org/islandoftex/texplate/model/handlers/Handler.java
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/model/handlers/Handler.kt
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.texplate.model.handlers;
+package org.islandoftex.texplate.model.handlers
/**
* Interface for handlers.
@@ -7,14 +7,12 @@ package org.islandoftex.texplate.model.handlers;
* @version 1.0
* @since 1.0
*/
-public interface Handler {
-
+interface Handler {
/**
* Apply the handler in the string.
*
* @param string The string.
* @return The resulting object.
*/
- Object apply(String string);
-
+ fun apply(string: String?): Any?
}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/HandlerUtils.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/HandlerUtils.kt
new file mode 100644
index 0000000000..44eebb2b69
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/HandlerUtils.kt
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.util
+
+import org.islandoftex.texplate.model.handlers.BooleanHandler
+import org.islandoftex.texplate.model.handlers.CSVListHandler
+import org.islandoftex.texplate.model.handlers.Handler
+
+/**
+ * Provides the map of handlers.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+object HandlerUtils {
+ /**
+ * Gets the map of handlers.
+ *
+ * @return Map of handlers.
+ */
+ @JvmStatic
+ val handlers: Map<String, Handler> = mapOf(
+ "to-csv-list" to CSVListHandler(),
+ "to-boolean" to BooleanHandler()
+ )
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MergingUtils.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MergingUtils.kt
new file mode 100644
index 0000000000..ef02349d5d
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MergingUtils.kt
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.util
+
+import java.io.IOException
+import java.nio.file.Path
+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.RuntimeSingleton
+import org.apache.velocity.runtime.parser.ParseException
+import org.islandoftex.texplate.exceptions.TemplateMergingException
+import org.islandoftex.texplate.model.Template
+import org.islandoftex.texplate.util.HandlerUtils.handlers
+import org.slf4j.helpers.NOPLoggerFactory
+
+/**
+ * Merging utilities.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+object 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.
+ */
+ @JvmStatic
+ @Throws(TemplateMergingException::class)
+ @Suppress("TooGenericExceptionCaught")
+ fun mergeTemplate(
+ template: Template,
+ map: Map<String, String?>,
+ output: Path,
+ cmap: Map<String, Any>
+ ): Long {
+ // create the context map
+ val context = handle(template, map, cmap)
+ // create a file writer for the output reference
+ try {
+ output.toFile().writer().use { writer ->
+ // the document is actually read into a string reader
+ val reader = template.document!!.reader()
+ // load both runtime services and the template model from Velocity
+ val services = RuntimeSingleton.getRuntimeServices()
+ services.addProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE,
+ NOPLoggerFactory().getLogger(""))
+ val reference = org.apache.velocity.Template()
+ // set both runtime services and document data into the template document
+ reference.setRuntimeServices(services)
+ reference.data = services.parse(reader, reference)
+ reference.initDocument()
+ // create the context based on the data map previously set
+ val entries = VelocityContext(context)
+ // merge both template and data into the file writer
+ reference.merge(entries, writer)
+ }
+ } catch (exception: Exception) {
+ // TODO: simplify
+ when (exception) {
+ is IOException, is MethodInvocationException, is ParseErrorException,
+ is ParseException, is ResourceNotFoundException, is TemplateInitException ->
+ throw 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)
+ else -> throw TemplateMergingException("Fatal error occured. " +
+ "This error should never happen. Please make a detailed " +
+ "report to the developers.")
+ }
+ }
+ // simply return the length of the generated output file
+ return output.toFile().length()
+ }
+
+ /**
+ * Handles the context map.
+ *
+ * @param template The template model.
+ * @param map The context map.
+ * @param configmap The map from a configuration file.
+ * @return The new context map.
+ */
+ private fun handle(
+ template: Template,
+ map: Map<String, String?>,
+ configmap: Map<String, Any>
+ ): Map<String, Any?> {
+ // no handlers found
+ return if (template.handlers.isEmpty()) {
+ // create a new map from the command line map and put the values from
+ // the configuration file cmap, if absent
+ configmap.mapValues { it.value.toString() }.plus(map)
+ } else {
+ // get default handlers and set the resulting map
+ val result: MutableMap<String, Any?> = mutableMapOf()
+ // check each key from the map
+ map.forEach { (key: String, value: String?) ->
+ // there is a handler for the current key
+ if (template.handlers.containsKey(key) &&
+ handlers.containsKey(template.handlers[key])) {
+ // apply the handler and store the value in the map
+ result[key] = handlers[template.handlers[key]]!!.apply(value)
+ } else {
+ // simply store the value
+ result[key] = value
+ // TODO: should we warn about an invalid handler?
+ }
+ }
+
+ // put remaining values from the configuration file, if absent
+ configmap.plus(result)
+ }
+ }
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MessageUtils.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MessageUtils.kt
new file mode 100644
index 0000000000..fbb68a819e
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/MessageUtils.kt
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.util
+
+import java.time.LocalDate
+
+/**
+ * Message helper methods.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+object MessageUtils {
+ // the message width
+ private const val WIDTH = 60
+ // the application version
+ private val VERSION = MessageUtils::class.java.`package`.implementationVersion
+ ?: "DEVELOPMENT BUILD"
+
+ /**
+ * Prints a line in the terminal, without a line break.
+ *
+ * @param message The message to be printed.
+ */
+ @JvmStatic
+ fun line(message: String) {
+ print("$message ".padEnd(WIDTH - " [FAILED]".length, '.') + " ")
+ }
+
+ /**
+ * Prints the status in the terminal.
+ *
+ * @param result The boolean value.
+ */
+ @JvmStatic
+ fun status(result: Boolean) {
+ println(if (result) "[ DONE ]" else "[FAILED]")
+ }
+
+ /**
+ * Prints the error in the terminal.
+ *
+ * @param throwable The throwable reference.
+ */
+ @JvmStatic
+ fun error(throwable: Throwable) {
+ println("\n" + "HOUSTON, WE'VE GOT A PROBLEM ".padEnd(WIDTH, '-') +
+ "\n" + throwable.message + "\n" +
+ "".padStart(WIDTH, '-') + "\n")
+ }
+
+ /**
+ * Prints the application logo in the terminal.
+ */
+ fun drawLogo() {
+ println(
+ " ______ __ __ ___ __ \n" +
+ "/\\__ _\\ /\\ \\ /\\ \\ /\\_ \\ /\\ \\__ \n" +
+ "\\/_/\\ \\/ __ \\ `\\`\\/'/' _____\\//\\ \\ __ \\ \\ ,_\\ __ \n" +
+ " \\ \\ \\ /'__`\\`\\/ > < /\\ '__`\\\\ \\ \\ /'__`\\ \\ \\ \\/ /'__`\\ \n" +
+ " \\ \\ \\/\\ __/ \\/'/\\`\\\\ \\ \\L\\ \\\\_\\ \\_/\\ \\L\\.\\_\\ \\ \\_/\\ __/ \n" +
+ " \\ \\_\\ \\____\\ /\\_\\\\ \\_\\ \\ ,__//\\____\\ \\__/.\\_\\\\ \\__\\ \\____\\\n" +
+ " \\/_/\\/____/ \\/_/ \\/_/\\ \\ \\/ \\/____/\\/__/\\/_/ \\/__/\\/____/\n" +
+ " \\ \\_\\ \n" +
+ " \\/_/ \n"
+ )
+ println(
+ "TeXplate $VERSION, a document structure creation tool\n" +
+ "Copyright (c) ${LocalDate.now().year}, Island of TeX\n" +
+ "All rights reserved.\n"
+ )
+ }
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/PathUtils.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/PathUtils.kt
new file mode 100644
index 0000000000..3a74603557
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/PathUtils.kt
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.util
+
+import java.io.FileNotFoundException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+
+/**
+ * Helper methods for path handling.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+object PathUtils {
+ // the templates folder
+ private const val TEMPLATES_FOLDER = "templates"
+ // the user application folder
+ private const val USER_APPLICATION_FOLDER = ".texplate"
+
+ /**
+ * The user's template path.
+ */
+ private val userTemplatePath: Path
+ get() = try {
+ Paths.get(System.getProperty("user.home"),
+ USER_APPLICATION_FOLDER, TEMPLATES_FOLDER)
+ } catch (e: RuntimeException) {
+ Paths.get(".")
+ }
+
+ /**
+ * Searches all paths looking for the provided template.
+ *
+ * @param name The name to be associated to a template file.
+ * @return The corresponding template file.
+ * @throws FileNotFoundException The template file could not be found.
+ */
+ @JvmStatic
+ @Throws(FileNotFoundException::class)
+ fun getTemplatePath(name: String): Path {
+ // the file has to be a TOML format, so we add the extension
+ val fullName = "$name.toml"
+ // the first reference is based on the user template path resolved with the
+ // file name
+ val reference = userTemplatePath.resolve(fullName)
+ // if the file actually exists, the search is done!
+ return if (Files.exists(reference)) {
+ reference
+ } else {
+ // the reference was not found in the user location, so let us try the
+ // system counterpart
+ try {
+ val tempFile = Files.createTempFile(null, null)
+ tempFile.toFile().writeText(PathUtils::class.java
+ .getResource("/org/islandoftex/texplate/templates/texplate-$fullName")
+ .readText())
+ tempFile
+ } catch (e: RuntimeException) {
+ throw FileNotFoundException("I am sorry, but the template " +
+ "file '" + fullName + "' could not be found in the " +
+ "default template locations (system and user). Make " +
+ "sure the reference is correct and try again. For " +
+ "reference, these are the paths I searched: '" +
+ userTemplatePath + "'.")
+ }
+ }
+ }
+}
diff --git a/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/ValidatorUtils.kt b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/ValidatorUtils.kt
new file mode 100644
index 0000000000..ef91a012c1
--- /dev/null
+++ b/support/texplate/source/main/kotlin/org/islandoftex/texplate/util/ValidatorUtils.kt
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: BSD-3-Clause
+package org.islandoftex.texplate.util
+
+import org.islandoftex.texplate.exceptions.InvalidKeySetException
+import org.islandoftex.texplate.model.Template
+
+/**
+ * Helper methods for validation.
+ *
+ * @version 1.0
+ * @since 1.0
+ */
+object ValidatorUtils {
+ /**
+ * Validates the data map based on the template requirements.
+ *
+ * @param template The template.
+ * @param map The data map.
+ * @return A boolean value indicating whether the data map is valid.
+ */
+ private fun validateRequirements(
+ template: Template,
+ map: Map<String, String>
+ ): Boolean {
+ return template.requirements.isNullOrEmpty() ||
+ template.requirements.containsAll(map.keys)
+ }
+
+ /**
+ * Validates the template pattern and the data map and throws an exception
+ * in case of failure.
+ *
+ * @param template The template.
+ * @param map The data map.
+ * @return The data map.
+ * @throws InvalidKeySetException There are invalid keys in the map.
+ */
+ @JvmStatic
+ @Throws(InvalidKeySetException::class)
+ fun validate(
+ template: Template,
+ map: Map<String, String>
+ ): Map<String, String> {
+ // for starters, we try to validate the template requirements
+ return if (validateRequirements(template, map)) {
+ map
+ } else {
+ throw InvalidKeySetException("The provided map does not " +
+ "contain all the keys required by the chosen " +
+ "template. Make sure to define such keys and try " +
+ "again. Check the user manual for further details.")
+ }
+ }
+}
diff --git a/support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-article.toml b/support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-article.toml
new file mode 100644
index 0000000000..53f7796762
--- /dev/null
+++ b/support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-article.toml
@@ -0,0 +1,54 @@
+name = "article"
+description = """
+A simple template for the default article class, with support for new
+engines (with fontspec fallback), babel languages, geometry options,
+generic packages, and TikZ and corresponding libraries.
+"""
+authors = [ "Island of TeX" ]
+requirements = []
+document = '''
+\documentclass#if($options)[$options]#{end}{article}
+
+#if ($xetex || $luatex)
+\usepackage{fontspec}
+#else
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+#end
+#if ($geometry)
+
+\usepackage[$geometry]{geometry}
+#end
+#if ($babel)
+#if (!$geometry)
+
+#end
+\usepackage[$babel]{babel}
+#end
+#if ($packages)
+
+#foreach ($package in $packages)
+\usepackage{$package}
+#end
+#end
+#if ($tikz)
+
+\usepackage{tikz}
+#if ($libraries)
+#foreach ($library in $libraries)
+\usetikzlibrary{$library}
+#end
+#end
+#end
+
+\begin{document}
+
+\end{document}
+'''
+
+[handlers]
+xetex = "to-boolean"
+luatex = "to-boolean"
+tikz = "to-boolean"
+libraries = "to-csv-list"
+packages = "to-csv-list"
diff --git a/support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-standalone.toml b/support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-standalone.toml
new file mode 100644
index 0000000000..dd8daea65f
--- /dev/null
+++ b/support/texplate/source/main/resources/org/islandoftex/texplate/templates/texplate-standalone.toml
@@ -0,0 +1,45 @@
+name = "standalone"
+description = """
+A simple template for the standalone class, with support for
+class options, new engines (with fontspec fallback), list of
+packages, and TikZ and corresponding libraries.
+"""
+authors = [ "Island of TeX" ]
+requirements = []
+document = '''
+\documentclass#if($options)[$options]#{end}{standalone}
+
+#if ($xetex || $luatex)
+\usepackage{fontspec}
+#else
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+#end
+#if ($packages)
+
+#foreach ($package in $packages)
+\usepackage{$package}
+#end
+#end
+#if ($tikz)
+
+\usepackage{tikz}
+#if ($libraries)
+#foreach ($library in $libraries)
+\usetikzlibrary{$library}
+#end
+#end
+#end
+
+\begin{document}
+
+\end{document}
+'''
+
+[handlers]
+xetex = "to-boolean"
+luatex = "to-boolean"
+tikz = "to-boolean"
+libraries = "to-csv-list"
+packages = "to-csv-list"
+