summaryrefslogtreecommitdiff
path: root/support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java
diff options
context:
space:
mode:
Diffstat (limited to 'support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java')
-rw-r--r--support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java391
1 files changed, 391 insertions, 0 deletions
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
new file mode 100644
index 0000000000..eccaf6c732
--- /dev/null
+++ b/support/texplate/source/main/java/org/islandoftex/texplate/model/TemplateProcessing.java
@@ -0,0 +1,391 @@
+// 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;
+ }
+
+}