diff options
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.java | 391 |
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; + } + +} |