diff options
author | Norbert Preining <norbert@preining.info> | 2020-02-03 03:02:01 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2020-02-03 03:02:01 +0000 |
commit | 34897eebd22c3de968b64f09bb4a371d96cb0b89 (patch) | |
tree | 19a50e54fca63f1f6482d6cb4c2fdee676fd97b1 /support/texplate/source/main/kotlin/org/islandoftex/texplate/util | |
parent | e05dc0bb38b16bfb9ba35591285e40af0adaadc4 (diff) |
CTAN sync 202002030302
Diffstat (limited to 'support/texplate/source/main/kotlin/org/islandoftex/texplate/util')
5 files changed, 346 insertions, 0 deletions
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.") + } + } +} |