summaryrefslogtreecommitdiff
path: root/support/texplate/source/main/kotlin/org/islandoftex/texplate/util
diff options
context:
space:
mode:
Diffstat (limited to 'support/texplate/source/main/kotlin/org/islandoftex/texplate/util')
-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
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.")
+ }
+ }
+}