summaryrefslogtreecommitdiff
path: root/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils
diff options
context:
space:
mode:
Diffstat (limited to 'support/arara/source/src/main/kotlin/org/islandoftex/arara/utils')
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt121
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/CommonUtils.kt422
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/DisplayUtils.kt415
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/Extensions.kt68
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt164
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/LoggingUtils.kt70
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/MessageUtils.kt266
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/SystemCallUtils.kt105
-rw-r--r--support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/TeeOutputStream.kt66
9 files changed, 0 insertions, 1697 deletions
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt
deleted file mode 100644
index 05a1031a8a..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import java.io.File
-import java.lang.reflect.InvocationTargetException
-import java.net.MalformedURLException
-import java.net.URLClassLoader
-
-/**
- * Implements utilitary methods for classloading and object instantiation.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object ClassLoadingUtils {
- /**
- * Indicator of success or failure of class loading.
- */
- enum class ClassLoadingStatus {
- SUCCESS,
- FILE_NOT_FOUND,
- MALFORMED_URL,
- CLASS_NOT_FOUND,
- ILLEGAL_ACCESS,
- INSTANTIATION_EXCEPTION
- }
-
- /**
- * Loads a class from the provided file, potentially a Java archive.
- * @param file File containing the Java bytecode (namely, a JAR).
- * @param name The canonical name of the class.
- * @return A pair representing the status and the class.
- */
- fun loadClass(file: File, name: String):
- Pair<ClassLoadingStatus, Class<*>> {
- // status and class to be returned,
- // it defaults to an object class
- var value: Class<*> = Any::class.java
-
- // if file does not exist, nothing
- // can be done, status is changed
- val status = if (!file.exists()) {
- ClassLoadingStatus.FILE_NOT_FOUND
- } else {
- // classloading involves defining
- // a classloader and fetching the
- // desired class from it, based on
- // the provided file archive
- try {
- // creates a new classloader with
- // the provided file (potentially
- // a JAR file)
- val classloader = URLClassLoader(arrayOf(file.toURI().toURL()),
- ClassLoadingUtils::class.java.classLoader)
-
- // fetches the class from the
- // instantiated classloader
- value = Class.forName(name, true, classloader)
- ClassLoadingStatus.SUCCESS
- } catch (_: MalformedURLException) {
- ClassLoadingStatus.MALFORMED_URL
- } catch (_: ClassNotFoundException) {
- ClassLoadingStatus.CLASS_NOT_FOUND
- }
- }
-
- // return a new pair based on the
- // current status and class holder
- return status to value
- }
-
- /**
- * Loads a class from the provided file, instantiating it.
- * @param file File containing the Java bytecode (namely, a JAR).
- * @param name The canonical name of the class.
- * @return A pair representing the status and the class object.
- */
- fun loadObject(file: File, name: String): Pair<ClassLoadingStatus, Any> {
- // load the corresponding class
- // based on the qualified name
- val pair = loadClass(file, name)
-
- // status and object to be returned,
- // it defaults to an object
- var status = pair.first
- var value = Any()
-
- // checks if the class actually
- // exists, otherwise simply
- // ignore instantiation
- if (status == ClassLoadingStatus.SUCCESS) {
- // object instantiation relies
- // on the default constructor
- // (without arguments), class
- // must implement it
-
- // OBS: constructors with arguments
- // must be invoked through reflection
- try {
- // get the class reference from
- // the pair and instantiate it
- // by invoking the default
- // constructor (without arguments)
- value = pair.second.getDeclaredConstructor().newInstance()
- } catch (_: IllegalAccessException) {
- status = ClassLoadingStatus.ILLEGAL_ACCESS
- } catch (_: InstantiationException) {
- // the user wanted to instantiate an abstract class
- status = ClassLoadingStatus.INSTANTIATION_EXCEPTION
- } catch (_: InvocationTargetException) {
- // the underlying constructor caused an exception
- status = ClassLoadingStatus.INSTANTIATION_EXCEPTION
- }
- }
-
- // return a new pair based on the
- // current status and object holder
- return status to value
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/CommonUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/CommonUtils.kt
deleted file mode 100644
index 6887cd4e84..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/CommonUtils.kt
+++ /dev/null
@@ -1,422 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import java.io.File
-import java.io.IOException
-import java.util.MissingFormatArgumentException
-import java.util.regex.Pattern
-import kotlin.math.ln
-import kotlin.math.pow
-import org.islandoftex.arara.Arara
-import org.islandoftex.arara.configuration.AraraSpec
-import org.islandoftex.arara.filehandling.FileHandlingUtils
-import org.islandoftex.arara.localization.LanguageController
-import org.islandoftex.arara.localization.Messages
-import org.islandoftex.arara.model.AraraException
-import org.islandoftex.arara.ruleset.Argument
-
-/**
- * Implements common utilitary methods.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object CommonUtils {
- // the application messages obtained from the
- // language controller
- private val messages = LanguageController
-
- /**
- * Gets the list of file types as string, in order.
- *
- * @return A string representation of the list of file types, in order.
- */
- val fileTypesList: String
- get() = "[ " + Arara.config[AraraSpec.Execution.fileTypes]
- .joinToString(" | ") + " ]"
-
- /**
- * Gets the rule error header, containing the identifier and the path, if
- * any.
- *
- * @return A string representation of the rule error header, containing the
- * identifier and the path, if any.
- */
- val ruleErrorHeader: String
- get() {
- return if (Arara.config[AraraSpec.Execution.InfoSpec.ruleId] != null &&
- Arara.config[AraraSpec.Execution.InfoSpec.rulePath] != null) {
- val id = Arara.config[AraraSpec.Execution.InfoSpec.ruleId]!!
- val path = Arara.config[AraraSpec.Execution.InfoSpec.rulePath]!!
- messages.getMessage(
- Messages.ERROR_RULE_IDENTIFIER_AND_PATH,
- id,
- path
- ) + " "
- } else {
- ""
- }
- }
-
- /**
- * Gets a list of all rule paths.
- *
- * @return A list of all rule paths.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- val allRulePaths: List<String>
- @Throws(AraraException::class)
- get() = Arara.config[AraraSpec.Execution.rulePaths].map {
- val location = File(InterpreterUtils.construct(it, "quack"))
- FileHandlingUtils.getParentCanonicalPath(location)
- }
-
- /**
- * Returns the exit status of the application.
- *
- * @return An integer representing the exit status of the application.
- */
- val exitStatus: Int
- get() = Arara.config[AraraSpec.Execution.status]
-
- /**
- * Gets the preamble content, converting a single string into a list of
- * strings, based on new lines.
- *
- * @return A list of strings representing the preamble content.
- */
- val preambleContent: List<String>
- get() = if (Arara.config[AraraSpec.Execution.preamblesActive]) {
- Arara.config[AraraSpec.Execution.preamblesContent]
- .split("\n")
- .dropLastWhile { it.isEmpty() }
- .toList()
- } else {
- listOf()
- }
-
- /**
- * Checks if the input string is equal to a valid boolean value.
- *
- * @param value The input string.
- * @return A boolean value represented by the provided string.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun checkBoolean(value: String): Boolean {
- val yes = listOf("yes", "true", "1", "on")
- val no = listOf("no", "false", "0", "off")
- return if (!yes.union(no).contains(value.toLowerCase())) {
- throw AraraException(
- messages.getMessage(
- Messages.ERROR_CHECKBOOLEAN_NOT_VALID_BOOLEAN,
- value
- )
- )
- } else {
- yes.contains(value.toLowerCase())
- }
- }
-
- /**
- * Removes the keyword from the beginning of the provided string.
- *
- * @param line A string to be analyzed.
- * @return The provided string without the keyword.
- */
- fun removeKeyword(line: String?): String? {
- return if (line == null) null
- else removeKeywordNotNull(line)
- }
-
- /**
- * Removes the keyword from the beginning of the provided string.
- *
- * @param line A string to be analyzed.
- * @return The provided string without the keyword.
- */
- fun removeKeywordNotNull(line: String): String {
- var tempLine = line
- val pattern = "^(\\s)*<arara>\\s".toPattern()
- val matcher = pattern.matcher(tempLine)
- if (matcher.find()) {
- tempLine = tempLine.substring(matcher.end())
- }
- return tempLine.trim()
- }
-
- /**
- * Flattens a potential list of lists into a list of objects.
- *
- * @param list The list to be flattened.
- * @return The flattened list.
- */
- // TODO: check nullity
- fun flatten(list: List<*>): List<Any> {
- val result = mutableListOf<Any>()
- list.forEach { item ->
- if (item is List<*>)
- result.addAll(flatten(item))
- else
- result.add(item as Any)
- }
- return result
- }
-
- /**
- * Gets a set of strings containing unknown keys from a map and a list. It
- * is a set difference from the keys in the map and the entries in the list.
- *
- * @param parameters The map of parameters.
- * @param arguments The list of arguments.
- * @return A set of strings representing unknown keys from a map and a list.
- */
- fun getUnknownKeys(
- parameters: Map<String, Any>,
- arguments: List<Argument>
- ): Set<String> {
- val found = parameters.keys
- val expected = arguments.mapNotNull { it.identifier }
- return found.subtract(expected)
- }
-
- /**
- * Gets a human readable representation of a size.
- *
- * @param size The byte size to be converted.
- * @return A string representation of the size.
- */
- @Suppress("MagicNumber")
- fun byteSizeToString(size: Long): String {
- val language = Arara.config[AraraSpec.Execution.language]
- val conversionFactor = 1000.0
- return if (size < conversionFactor) "$size B"
- else
- (ln(size.toDouble()) / ln(conversionFactor)).toInt().let { exp ->
- "%.1f %sB".format(language.locale,
- size / conversionFactor.pow(exp.toDouble()),
- "kMGTPE"[exp - 1])
- }
- }
-
- /**
- * Generates a string based on a list of objects, separating each one of
- * them by one space.
- *
- * @param objects A list of objects.
- * @return A string based on the list of objects, separating each one of
- * them by one space. Empty values are not considered.
- */
- fun generateString(vararg objects: Any): String = objects
- .map { it.toString() }.filter { it.isNotEmpty() }
- .joinToString(" ")
-
- /**
- * Checks if the file based on the provided extension contains the provided
- * regex.
- *
- * @param extension The file extension.
- * @param regex The regex.
- * @return A boolean value indicating if the file contains the provided
- * regex.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun checkRegex(extension: String, regex: String): Boolean {
- val file = File(FileHandlingUtils.getPath(extension))
- return checkRegex(file, regex)
- }
-
- /**
- * Checks if the file contains the provided regex.
- *
- * As we use [File.readText] this should not be called on files > 2GB.
- *
- * @param file The file.
- * @param regex The regex.
- * @return A boolean value indicating if the file contains the provided
- * regex.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun checkRegex(file: File, regex: String): Boolean {
- try {
- val text = file.readText()
- val pattern = Pattern.compile(regex)
- val matcher = pattern.matcher(text)
- return matcher.find()
- } catch (exception: IOException) {
- throw AraraException(
- messages.getMessage(
- Messages.ERROR_CHECKREGEX_IO_EXCEPTION,
- file.name
- ),
- exception
- )
- }
- }
-
- /**
- * Replicates a string pattern based on a list of objects, generating a list
- * as result.
- *
- * @param pattern The string pattern.
- * @param values The list of objects to be merged with the pattern.
- * @return A list containing the string pattern replicated to each object
- * from the list.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun replicateList(
- pattern: String,
- values: List<Any>
- ): List<Any> {
- return try {
- values.map { String.format(pattern, it) }
- } catch (exception: MissingFormatArgumentException) {
- throw AraraException(
- messages.getMessage(
- Messages.ERROR_REPLICATELIST_MISSING_FORMAT_ARGUMENTS_EXCEPTION
- ),
- exception
- )
- }
- }
-
- /**
- * Checks if the provided operating system string holds according to the
- * underlying operating system.
- *
- * Supported operating systems:
- *
- * * Windows
- * * Linux
- * * Mac OS X
- * * Unix (Linux || Mac OS)
- * * Cygwin
- *
- * @param value A string representing an operating system.
- * @return A boolean value indicating if the provided string refers to the
- * underlying operating system.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun checkOS(value: String): Boolean {
- fun checkOSProperty(key: String): Boolean =
- getSystemPropertyOrNull("os.name")
- ?.toLowerCase()?.startsWith(key.toLowerCase()) ?: false
-
- val values = mutableMapOf<String, Boolean>()
- values["windows"] = checkOSProperty("Windows")
- values["linux"] = checkOSProperty("Linux")
- values["mac"] = checkOSProperty("Mac OS X")
- values["unix"] = checkOSProperty("Mac OS X") ||
- checkOSProperty("Linux")
- values["cygwin"] = SystemCallUtils["cygwin"] as Boolean
- if (!values.containsKey(value.toLowerCase())) {
- throw AraraException(
- messages.getMessage(
- Messages.ERROR_CHECKOS_INVALID_OPERATING_SYSTEM,
- value
- )
- )
- }
- // will never throw, see check above
- return values.getValue(value.toLowerCase())
- }
-
- /**
- * Gets the system property according to the provided key, or resort to the
- * fallback value if an exception is thrown or if the key is invalid.
- *
- * @param key The system property key.
- * @param fallback The fallback value.
- * @return A string containing the system property value or the fallback.
- */
- fun getSystemProperty(key: String, fallback: String): String =
- System.getProperties().runCatching {
- getOrDefault(key, fallback).toString().takeIf { it != "" }
- }.getOrNull() ?: fallback
-
- /**
- * Access a system property.
- *
- * @param key The key of the property.
- * @return The value of the system property or null if there is an
- * exception.
- */
- fun getSystemPropertyOrNull(key: String): String? =
- System.getProperties().runCatching { getValue(key).toString() }
- .getOrNull()
-
- /**
- * Generates a list of filenames from the provided command based on a list
- * of extensions for each underlying operating system.
- *
- * @param command A string representing the command.
- * @return A list of filenames.
- */
- private fun appendExtensions(command: String): List<String> {
- // list of extensions, specific for
- // each operating system (in fact, it
- // is more Windows specific)
- val extensions = if (checkOS("windows")) {
- // the application is running on
- // Windows, so let's look for the
- // following extensions in order
-
- // this list is actually a sublist from
- // the original Windows PATHEXT environment
- // variable which holds the list of executable
- // extensions that Windows supports
- listOf(".com", ".exe", ".bat", ".cmd")
- } else {
- // no Windows, so the default
- // extension will be just an
- // empty string
- listOf("")
- }
-
- // return the resulting list holding the
- // filenames generated from the
- // provided command
- return extensions.map { "$command$it" }
- }
-
- /**
- * Checks if the provided command name is reachable from the system path.
- *
- * @param command A string representing the command.
- * @return A logic value.
- */
- fun isOnPath(command: String): Boolean {
- // first and foremost, let's build the list
- // of filenames based on the underlying
- // operating system
- val filenames = appendExtensions(command)
- return kotlin.runCatching {
- // break the path into several parts
- // based on the path separator symbol
- System.getenv("PATH").split(File.pathSeparator)
- .asSequence()
- .mapNotNull { File(it).listFiles() }
- // if the search does not return an empty
- // list, one of the filenames got a match,
- // and the command is available somewhere
- // in the system path
- .firstOrNull {
- it.any { file ->
- filenames.contains(file.name) && !file.isDirectory
- }
- }?.let { true }
- }.getOrNull() ?: false
- // otherwise (and in case of an exception) it is not in the path
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/DisplayUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/DisplayUtils.kt
deleted file mode 100644
index 213a9a21c3..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/DisplayUtils.kt
+++ /dev/null
@@ -1,415 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import org.islandoftex.arara.Arara
-import org.islandoftex.arara.configuration.AraraSpec
-import org.islandoftex.arara.configuration.ConfigurationUtils
-import org.islandoftex.arara.filehandling.FileHandlingUtils
-import org.islandoftex.arara.localization.LanguageController
-import org.islandoftex.arara.localization.Messages
-import org.islandoftex.arara.model.AraraException
-import org.islandoftex.arara.ruleset.Conditional
-import org.slf4j.LoggerFactory
-
-/**
- * Implements display utilitary methods.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object DisplayUtils {
- // the application messages obtained from the
- // language controller
- private val messages = LanguageController
-
- // get the logger context from a factory
- private val logger = LoggerFactory.getLogger(DisplayUtils::class.java)
-
- /**
- * The length of the longest result match as integer.
- */
- private val longestMatch: Int = listOf(
- messages.getMessage(Messages.INFO_LABEL_ON_SUCCESS),
- messages.getMessage(Messages.INFO_LABEL_ON_FAILURE),
- messages.getMessage(Messages.INFO_LABEL_ON_ERROR))
- .map { it.length }.max()!!
- /**
- * If the longest match is longer than the width, then it will be truncated
- * to this length.
- */
- private const val shortenedLongestMatch = 10
-
- /**
- * The default terminal width defined in the settings.
- */
- private val width: Int
- get() = Arara.config[AraraSpec.Application.width]
-
- /**
- * Checks if the execution is in dry-run mode.
- */
- private val isDryRunMode: Boolean
- get() = Arara.config[AraraSpec.Execution.dryrun]
-
- /**
- * Checks if the execution is in verbose mode.
- */
- private val isVerboseMode: Boolean
- get() = Arara.config[AraraSpec.Execution.verbose]
-
- /**
- * The application path.
- */
- private val applicationPath: String
- get() = try {
- ConfigurationUtils.applicationPath.toString()
- } catch (ae: AraraException) {
- "[unknown application path]"
- }
-
- /**
- * Displays the short version of the current entry in the terminal.
- *
- * @param name Rule name.
- * @param task Task name.
- */
- private fun buildShortEntry(name: String, task: String) {
- val result = if (longestMatch >= width)
- shortenedLongestMatch
- else
- longestMatch
- val space = width - result - 1
- val line = "($name) $task ".abbreviate(space - "... ".length)
- print(line.padEnd(space, '.') + " ")
- }
-
- /**
- * Displays the short version of the current entry result in the terminal.
- *
- * @param value The boolean value to be displayed.
- */
- private fun buildShortResult(value: Boolean) {
- val result = longestMatch
- println(getResult(value).padStart(result))
- }
-
- /**
- * Displays the current entry result in the terminal.
- *
- * @param value The boolean value to be displayed.
- */
- fun printEntryResult(value: Boolean) {
- Arara.config[AraraSpec.UserInteraction.displayLine] = false
- Arara.config[AraraSpec.UserInteraction.displayResult] = true
- Arara.config[AraraSpec.Execution.status] = if (value) 0 else 1
- logger.info(
- messages.getMessage(
- Messages.LOG_INFO_TASK_RESULT
- ) + " " + getResult(value)
- )
- if (!isDryRunMode) {
- if (!isVerboseMode) {
- buildShortResult(value)
- } else {
- buildLongResult(value)
- }
- }
- }
-
- /**
- * Displays a long version of the current entry result in the terminal.
- *
- * @param value The boolean value to be displayed
- */
- private fun buildLongResult(value: Boolean) {
- val width = width
- println("\n" + (" " + getResult(value)).padStart(width, '-'))
- }
-
- /**
- * Displays the current entry in the terminal.
- *
- * @param name The rule name.
- * @param task The task name.
- */
- fun printEntry(name: String, task: String) {
- logger.info(
- messages.getMessage(
- Messages.LOG_INFO_INTERPRET_TASK,
- task,
- name
- )
- )
- Arara.config[AraraSpec.UserInteraction.displayLine] = true
- Arara.config[AraraSpec.UserInteraction.displayResult] = false
- if (!isDryRunMode) {
- if (!isVerboseMode) {
- buildShortEntry(name, task)
- } else {
- buildLongEntry(name, task)
- }
- } else {
- buildDryRunEntry(name, task)
- }
- }
-
- /**
- * Displays a long version of the current entry in the terminal.
- *
- * @param name Rule name.
- * @param task Task name.
- */
- private fun buildLongEntry(name: String, task: String) {
- if (Arara.config[AraraSpec.UserInteraction.displayRolling]) {
- addNewLine()
- } else {
- Arara.config[AraraSpec.UserInteraction.displayRolling] = true
- }
- println(displaySeparator())
- println("($name) $task".abbreviate(width))
- println(displaySeparator())
- }
-
- /**
- * Displays a dry-run version of the current entry in the terminal.
- *
- * @param name The rule name.
- * @param task The task name.
- */
- private fun buildDryRunEntry(name: String, task: String) {
- if (Arara.config[AraraSpec.UserInteraction.displayRolling]) {
- addNewLine()
- } else {
- Arara.config[AraraSpec.UserInteraction.displayRolling] = true
- }
- println("[DR] ($name) $task".abbreviate(width))
- println(displaySeparator())
- }
-
- /**
- * Displays the exception in the terminal.
- *
- * @param exception The exception object.
- */
- fun printException(exception: AraraException) {
- Arara.config[AraraSpec.UserInteraction.displayException] = true
- Arara.config[AraraSpec.Execution.status] = 2
-
- val display = Arara.config[AraraSpec.UserInteraction.displayLine]
- if (Arara.config[AraraSpec.UserInteraction.displayResult])
- addNewLine()
- if (display) {
- if (!isDryRunMode) {
- if (!isVerboseMode) {
- buildShortError()
- } else {
- buildLongError()
- }
- addNewLine()
- }
- }
- val text = (if (exception.hasException())
- exception.message + " " + messages.getMessage(
- Messages.INFO_DISPLAY_EXCEPTION_MORE_DETAILS)
- else
- exception.message) ?: "EXCEPTION PROVIDES NO MESSAGE"
- // TODO: check null handling
- logger.error(text)
- wrapText(text)
- if (exception.hasException()) {
- addNewLine()
- displayDetailsLine()
- val details = exception.exception!!.message!!
- logger.error(details)
- wrapText(details)
- }
- }
-
- /**
- * Gets the string representation of the provided boolean value.
- *
- * @param value The boolean value.
- * @return The string representation.
- */
- private fun getResult(value: Boolean): String {
- return if (value)
- messages.getMessage(
- Messages.INFO_LABEL_ON_SUCCESS
- )
- else
- messages.getMessage(Messages.INFO_LABEL_ON_FAILURE)
- }
-
- /**
- * Displays the short version of an error in the terminal.
- */
- private fun buildShortError() {
- val result = longestMatch
- println(messages.getMessage(Messages.INFO_LABEL_ON_ERROR)
- .padStart(result))
- }
-
- /**
- * Displays the long version of an error in the terminal.
- */
- private fun buildLongError() {
- println((" " + messages.getMessage(Messages.INFO_LABEL_ON_ERROR))
- .padStart(width, '-'))
- }
-
- /**
- * Displays the provided text wrapped nicely according to the default
- * terminal width.
- *
- * @param text The text to be displayed.
- */
- fun wrapText(text: String) = println(text.wrap(width))
-
- /**
- * Displays the rule authors in the terminal.
- *
- * @param authors The list of authors.
- */
- fun printAuthors(authors: List<String>) {
- val line = if (authors.size == 1)
- messages.getMessage(Messages.INFO_LABEL_AUTHOR)
- else
- messages.getMessage(Messages.INFO_LABEL_AUTHORS)
- val text = if (authors.isEmpty())
- messages.getMessage(Messages.INFO_LABEL_NO_AUTHORS)
- else
- authors.joinToString(", ") { it.trim() }
- wrapText("$line $text")
- }
-
- /**
- * Displays the current conditional in the terminal.
- *
- * @param conditional The conditional object.
- */
- fun printConditional(conditional: Conditional) {
- if (conditional.type !== Conditional.ConditionalType.NONE) {
- wrapText(messages.getMessage(Messages.INFO_LABEL_CONDITIONAL) +
- " (" + conditional.type + ") " +
- conditional.condition)
- }
- }
-
- /**
- * Displays the file information in the terminal.
- */
- fun printFileInformation() {
- val file = Arara.config[AraraSpec.Execution.reference]
- val version = Arara.config[AraraSpec.Application.version]
- val line = messages.getMessage(
- Messages.INFO_DISPLAY_FILE_INFORMATION,
- file.name,
- CommonUtils.byteSizeToString(file.length()),
- FileHandlingUtils.getLastModifiedInformation(file)
- )
- logger.info(messages.getMessage(
- Messages.LOG_INFO_WELCOME_MESSAGE,
- version
- ))
- logger.info(displaySeparator())
- logger.debug("::: arara @ $applicationPath")
- logger.debug("::: Java %s, %s".format(
- CommonUtils.getSystemProperty("java.version",
- "[unknown version]"),
- CommonUtils.getSystemProperty("java.vendor",
- "[unknown vendor]")
- ))
- logger.debug("::: %s".format(
- CommonUtils.getSystemProperty("java.home",
- "[unknown location]")
- ))
- logger.debug("::: %s, %s, %s".format(
- CommonUtils.getSystemProperty("os.name",
- "[unknown OS name]"),
- CommonUtils.getSystemProperty("os.arch",
- "[unknown OS arch]"),
- CommonUtils.getSystemProperty("os.version",
- "[unknown OS version]")
- ))
- logger.debug("::: user.home @ %s".format(
- CommonUtils.getSystemProperty("user.home",
- "[unknown user's home directory]")
- ))
- logger.debug("::: CF @ %s".format(Arara.config[AraraSpec.Execution
- .configurationName]))
- logger.debug(displaySeparator())
- logger.info(line)
- wrapText(line)
- addNewLine()
- }
-
- /**
- * Displays the elapsed time in the terminal.
- *
- * @param seconds The elapsed seconds.
- */
- fun printTime(seconds: Double) {
- val language = Arara.config[AraraSpec.Execution.language]
-
- if (Arara.config[AraraSpec.UserInteraction.displayTime]) {
- if (Arara.config[AraraSpec.UserInteraction.displayLine] ||
- Arara.config[AraraSpec.UserInteraction.displayException])
- addNewLine()
-
- val text = messages.getMessage(
- Messages.INFO_DISPLAY_EXECUTION_TIME,
- "%1.2f".format(language.locale, seconds))
- logger.info(text)
- wrapText(text)
- }
- }
-
- /**
- * Displays the application logo in the terminal.
- */
- fun printLogo() {
- println("""
- __ _ _ __ __ _ _ __ __ _
- / _` | '__/ _` | '__/ _` |
- | (_| | | | (_| | | | (_| |
- \__,_|_| \__,_|_| \__,_|
- """.trimIndent())
- addNewLine()
- }
-
- /**
- * Adds a new line in the terminal.
- */
- private fun addNewLine() {
- println()
- }
-
- /**
- * Displays a line containing details.
- */
- private fun displayDetailsLine() {
- val line = messages.getMessage(
- Messages.INFO_LABEL_ON_DETAILS) + " "
- println(line.abbreviate(width).padEnd(width, '-'))
- }
-
- /**
- * Gets the output separator with the provided text.
- *
- * @param message The provided text.
- * @return A string containing the output separator with the provided text.
- */
- fun displayOutputSeparator(message: String): String {
- return " $message ".center(width, '-')
- }
-
- /**
- * Gets the line separator.
- *
- * @return A string containing the line separator.
- */
- fun displaySeparator(): String {
- return "-".repeat(width)
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/Extensions.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/Extensions.kt
deleted file mode 100644
index 10ace0b7f1..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/Extensions.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import kotlin.math.ceil
-
-/**
- * Abbreviate a String to a maximal width.
- *
- * @param maxWidth The maximal width to truncate to.
- * @param ellipsis The string to use to indicate an ellipsis.
- * @throws IllegalArgumentException If the string would consist only of the
- * ellipsis after shortening.
- * @return The abbreviated string.
- */
-@Throws(IllegalArgumentException::class)
-fun String.abbreviate(maxWidth: Int, ellipsis: String = "…"): String {
- return when {
- maxWidth < ellipsis.length + 1 ->
- throw IllegalArgumentException("Can't abbreviate text further")
- this.length < maxWidth -> this
- else -> this.substring(0, maxWidth - ellipsis.length) + ellipsis
- }
-}
-
-/**
- * Center a string within a specified number of columns.
- *
- * This does not center anything if the string is longer than the specified
- * width.
- *
- * @param width The number of columns.
- * @param padChar The char to pad with.
- * @return The padded string.
- */
-fun String.center(width: Int, padChar: Char): String {
- return if (this.length > width) this
- else {
- val charsLeft = width - this.length
- padChar.toString().repeat(charsLeft / 2) + this +
- padChar.toString().repeat(ceil(charsLeft.toDouble() / 2.0).toInt())
- }
-}
-
-/**
- * Wrap text at a specified width.
- *
- * Algorithm from Wikipedia:
- * https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap#Minimum_number_of_lines
- *
- * @param width The width to wrap at.
- * @return Wrapped text.
- */
-fun String.wrap(width: Int): String {
- val words = this.split(" ")
- var wrapped = words[0]
- var spaceLeft = width - wrapped.length
- words.drop(1).forEach {
- val len = it.length
- wrapped += if (len + 1 > spaceLeft) {
- spaceLeft = width - len
- "\n$it"
- } else {
- spaceLeft -= len + 1
- " $it"
- }
- }
- return wrapped
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt
deleted file mode 100644
index 00313a7714..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/InterpreterUtils.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import java.io.ByteArrayOutputStream
-import java.io.File
-import java.io.IOException
-import java.io.OutputStream
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.TimeoutException
-import kotlin.time.Duration
-import kotlin.time.ExperimentalTime
-import org.islandoftex.arara.Arara
-import org.islandoftex.arara.configuration.AraraSpec
-import org.islandoftex.arara.localization.LanguageController
-import org.islandoftex.arara.localization.Messages
-import org.islandoftex.arara.model.AraraException
-import org.islandoftex.arara.ruleset.Command
-import org.islandoftex.arara.ruleset.Conditional
-import org.slf4j.LoggerFactory
-import org.zeroturnaround.exec.InvalidExitValueException
-import org.zeroturnaround.exec.ProcessExecutor
-import org.zeroturnaround.exec.listener.ShutdownHookProcessDestroyer
-
-/**
- * Implements interpreter utilitary methods.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object InterpreterUtils {
- // the application messages obtained from the
- // language controller
- private val messages = LanguageController
-
- // get the logger context from a factory
- private val logger = LoggerFactory.getLogger(InterpreterUtils::class.java)
-
- /**
- * Checks if the current conditional has a prior evaluation.
- *
- * @param conditional The current conditional object.
- * @return A boolean value indicating if the current conditional has a prior
- * evaluation.
- */
- fun runPriorEvaluation(conditional: Conditional): Boolean {
- return if (Arara.config[AraraSpec.Execution.dryrun]) {
- false
- } else {
- when (conditional.type) {
- Conditional.ConditionalType.IF,
- Conditional.ConditionalType.WHILE,
- Conditional.ConditionalType.UNLESS -> true
- else -> false
- }
- }
- }
-
- @ExperimentalTime
- private fun getProcessExecutorForCommand(
- command: Command,
- buffer: OutputStream
- ):
- ProcessExecutor {
- val timeOutValue = Arara.config[AraraSpec.Execution.timeoutValue]
- var executor = ProcessExecutor().command((command).elements)
- .directory(command.workingDirectory.absoluteFile)
- .addDestroyer(ShutdownHookProcessDestroyer())
- if (Arara.config[AraraSpec.Execution.timeout]) {
- if (timeOutValue == Duration.ZERO) {
- throw AraraException(messages.getMessage(Messages
- .ERROR_RUN_TIMEOUT_INVALID_RANGE))
- }
- executor = executor.timeout(timeOutValue.toLongNanoseconds(),
- TimeUnit.NANOSECONDS)
- }
- val tee = if (Arara.config[AraraSpec.Execution.verbose]) {
- executor = executor.redirectInput(System.`in`)
- TeeOutputStream(System.out, buffer)
- } else {
- TeeOutputStream(buffer)
- }
- executor = executor.redirectOutput(tee).redirectError(tee)
- return executor
- }
-
- /**
- * Runs the command in the underlying operating system.
- *
- * @param command An object representing the command.
- * @return An integer value representing the exit code.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @ExperimentalTime
- @Throws(AraraException::class)
- fun run(command: Command): Int {
- val buffer = ByteArrayOutputStream()
- val executor = getProcessExecutorForCommand(command, buffer)
- return executor.runCatching {
- val exit = execute().exitValue
- logger.info(DisplayUtils.displayOutputSeparator(
- messages.getMessage(Messages.LOG_INFO_BEGIN_BUFFER)))
- logger.info(buffer.toString())
- logger.info(DisplayUtils.displayOutputSeparator(
- messages.getMessage(Messages.LOG_INFO_END_BUFFER)))
- exit
- }.getOrElse {
- throw AraraException(messages.getMessage(
- when (it) {
- is IOException -> Messages.ERROR_RUN_IO_EXCEPTION
- is InterruptedException ->
- Messages.ERROR_RUN_INTERRUPTED_EXCEPTION
- is InvalidExitValueException ->
- Messages.ERROR_RUN_INVALID_EXIT_VALUE_EXCEPTION
- is TimeoutException ->
- Messages.ERROR_RUN_TIMEOUT_EXCEPTION
- else -> Messages.ERROR_RUN_GENERIC_EXCEPTION
- }), it)
- }
- }
-
- /**
- * Builds the rule path based on the rule name and returns the corresponding
- * file location.
- *
- * @param name The rule name.
- * @return The rule file.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun buildRulePath(name: String): File? {
- Arara.config[AraraSpec.Execution.rulePaths].forEach { path ->
- val location = File(construct(path, name))
- if (location.exists())
- return location
- }
- return null
- }
-
- /**
- * Constructs the path given the current path and the rule name.
- *
- * @param path The current path.
- * @param name The rule name.
- * @return The constructed path.
- * @throws AraraException Something wrong happened, to be caught in the
- * higher levels.
- */
- @Throws(AraraException::class)
- fun construct(path: String, name: String): String {
- val fileName = "$name.yaml"
- val location = File(path)
- return if (location.isAbsolute) {
- location.resolve(fileName).toString()
- } else {
- Arara.config[AraraSpec.Execution.workingDirectory]
- // first resolve the path (rule path) against the working
- // directory, then the rule name we want to resolve
- .resolve(path).resolve(fileName).toAbsolutePath().toString()
- }
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/LoggingUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/LoggingUtils.kt
deleted file mode 100644
index f9719fa31f..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/LoggingUtils.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import ch.qos.logback.classic.LoggerContext
-import ch.qos.logback.classic.joran.JoranConfigurator
-import ch.qos.logback.core.joran.spi.JoranException
-import java.io.InputStream
-import org.islandoftex.arara.Arara
-import org.islandoftex.arara.configuration.AraraSpec
-import org.slf4j.LoggerFactory
-
-/**
- * Implements the logging controller. This class actually sets the logging
- * configuration in order to allow appending results to a file.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object LoggingUtils {
- // configuration resource as an input stream
- // the configuration is actually a XML file.
- private val resource: InputStream by lazy {
- LoggingUtils::class.java
- .getResourceAsStream("/org/islandoftex/arara/configuration/logback.xml")
- }
-
- /**
- * Sets the logging configuration according to the provided boolean value.
- * If the value is set to true, the log entries will be appended to a file,
- * otherwise the logging feature will keep silent.
- * @param enable A boolean value that indicates the logging behaviour
- * throughout the application.
- */
- fun enableLogging(enable: Boolean) {
- // get the logger context from a factory, set a
- // new context and reset it
- val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
-
- try {
- // get a new configuration and set
- // the context
- val configurator = JoranConfigurator()
- configurator.context = loggerContext
- loggerContext.reset()
-
- // if enabled, the log entries will be
- // appended to a file, otherwise it will
- // remain silent
- if (enable) {
- // set the file name and configure
- // the logging controller to append
- // entries to the file
- val name = Arara.config[AraraSpec.Execution.logName]
- loggerContext.putProperty("name", name)
- configurator.doConfigure(resource)
- }
- } catch (_: JoranException) {
- // quack, quack, quack!
- }
- }
-
- /**
- * Initializes the logging controller by disabling it. I don't want an odd
- * behaviour out of the box.
- */
- fun init() {
- enableLogging(false)
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/MessageUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/MessageUtils.kt
deleted file mode 100644
index ae695c44fb..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/MessageUtils.kt
+++ /dev/null
@@ -1,266 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import javax.swing.JOptionPane
-import javax.swing.UIManager
-import org.islandoftex.arara.Arara
-import org.islandoftex.arara.configuration.AraraSpec
-
-/**
- * Implements utilitary methods for displaying messages.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object MessageUtils {
- // holds the default width for the
- // message body, in pixels
- private const val WIDTH = 250
-
- // let's start the UI manager and set
- // the default look and feel to be as
- // close as possible to the system
- init {
- // get the current look and feel
- var laf = Arara.config[AraraSpec.UserInteraction.lookAndFeel]
-
- // check if one is actually set
- if (laf != "none") {
- // use a special keyword to indicate
- // the use of a system look and feel
- if (laf == "system") {
- laf = UIManager.getSystemLookAndFeelClassName()
- }
-
- // let's try it, in case it fails,
- // rely to the default look and feel
- try {
- // get the system look and feel name
- // and try to set it as default
- UIManager.setLookAndFeel(laf)
- } catch (_: Exception) {
- // quack, quack, quack
- }
- }
- }
-
- /**
- * Normalizes the icon type to one of the five available icons.
- * @param value An integer value.
- * @return The normalized integer value.
- */
- @Suppress("MagicNumber")
- private fun normalizeIconType(value: Int): Int {
- // do the normalization according to the available
- // icons in the underlying message implementation
- return when (value) {
- 1 -> JOptionPane.ERROR_MESSAGE
- 2 -> JOptionPane.INFORMATION_MESSAGE
- 3 -> JOptionPane.WARNING_MESSAGE
- 4 -> JOptionPane.QUESTION_MESSAGE
- else -> JOptionPane.PLAIN_MESSAGE
- }
- }
-
- /**
- * Normalizes the message width, so only valid nonzero values are accepted.
- * @param value An integer value corresponding to the message width.
- * @return The normalized width.
- */
- private fun normalizeMessageWidth(value: Int): Int {
- return if (value > 0) value else WIDTH
- }
-
- /**
- * Shows the message.
- * @param width Integer value, in pixels.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- */
- fun showMessage(
- width: Int,
- type: Int,
- title: String,
- text: String
- ) {
- // effectively shows the message based
- // on the provided parameters
- JOptionPane.showMessageDialog(null,
- String.format(
- "<html><body style=\"width:%dpx\">%s</body></html>",
- normalizeMessageWidth(width),
- text),
- title,
- normalizeIconType(type)
- )
- }
-
- /**
- * Shows the message. It relies on the default width.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- */
- fun showMessage(type: Int, title: String, text: String) {
- showMessage(WIDTH, type, title, text)
- }
-
- /**
- * Shows a message with options presented as an array of buttons.
- * @param width Integer value, in pixels.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- * @param buttons An array of objects to be presented as buttons.
- * @return The index of the selected button, starting from 1.
- */
- fun showOptions(
- width: Int,
- type: Int,
- title: String,
- text: String,
- vararg buttons: Any
- ): Int {
- // returns the index of the selected button,
- // zero if nothing is selected
- return JOptionPane.showOptionDialog(null,
- String.format(
- "<html><body style=\"width:%dpx\">%s</body></html>",
- normalizeMessageWidth(width),
- text),
- title,
- JOptionPane.DEFAULT_OPTION,
- normalizeIconType(type), null,
- buttons,
- buttons[0]
- ) + 1
- }
-
- /**
- * Shows a message with options presented as an array of buttons. It relies
- * on the default width.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- * @param buttons An array of objects to be presented as buttons.
- * @return The index of the selected button, starting from 1.
- */
- @Suppress("SpreadOperator")
- fun showOptions(
- type: Int,
- title: String,
- text: String,
- vararg buttons: Any
- ): Int {
- return showOptions(WIDTH, type, title, text, *buttons)
- }
-
- /**
- * Shows a message with a text input.
- * @param width Integer value, in pixels.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- * @return The string representing the input text.
- */
- fun showInput(
- width: Int,
- type: Int,
- title: String,
- text: String
- ): String {
- // get the string from the
- // input text, if any
- val input = JOptionPane.showInputDialog(null,
- String.format(
- "<html><body style=\"width:%dpx\">%s</body></html>",
- normalizeMessageWidth(width),
- text),
- title,
- normalizeIconType(type))
-
- // if the input is not null, that is,
- // the user actually typed something
- // return the trimmed string otherwise
- // an empty string
- return input?.trim() ?: ""
- }
-
- /**
- * Shows a message with a text input. It relies on the default width.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- * @return The string representing the input text.
- */
- fun showInput(type: Int, title: String, text: String): String {
- return showInput(WIDTH, type, title, text)
- }
-
- /**
- * Shows a message with options presented as a dropdown list of elements.
- * @param width Integer value, in pixels.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- * @param elements An array of objects representing the elements.
- * @return The index of the selected element, starting from 1.
- */
- fun showDropdown(
- width: Int,
- type: Int,
- title: String,
- text: String,
- vararg elements: Any
- ): Int {
- // show the dropdown list and get
- // the selected object, if any
- val index = JOptionPane.showInputDialog(null,
- String.format(
- "<html><body style=\"width:%dpx\">%s</body></html>",
- normalizeMessageWidth(width),
- text),
- title,
- normalizeIconType(type), null,
- elements,
- elements[0])
-
- // if it's not a null object, let's
- // find the corresponding index
- if (index != null) {
- elements.forEachIndexed { i, value ->
- // if the element is found, simply
- // return the index plus 1, as zero
- // corresponds to no selection at all
- if (value == index) {
- return i + 1
- }
- }
- }
-
- // nothing was selected,
- // simply return zero
- return 0
- }
-
- /**
- * Shows a message with options presented as a dropdown list of elements. It
- * relies on the default width.
- * @param type Type of message.
- * @param title Title of the message.
- * @param text Text of the message.
- * @param elements An array of objects representing the elements.
- * @return The index of the selected element, starting from 1.
- */
- @Suppress("SpreadOperator")
- fun showDropdown(
- type: Int,
- title: String,
- text: String,
- vararg elements: Any
- ): Int {
- return showDropdown(WIDTH, type, title, text, *elements)
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/SystemCallUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/SystemCallUtils.kt
deleted file mode 100644
index 9b443736a1..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/SystemCallUtils.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import org.islandoftex.arara.model.AraraException
-import org.islandoftex.arara.ruleset.Command
-import org.zeroturnaround.exec.ProcessExecutor
-
-/**
- * Implements a system call controller.
- *
- * This class wraps a map that holds the result of system specific variables
- * not directly available at runtime and makes unsafe calling of system
- * commands available to rules.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-object SystemCallUtils {
- /**
- * When executing a system call goes wrong, this status code is returned.
- */
- const val errorExitStatus = -99
- /**
- * When executing a system call goes wrong and the caller asked for output,
- * this output will be returned.
- */
- const val errorCommandOutput = ""
-
- // the system call map which holds the result of
- // system specific variables not directly available
- // at runtime; the idea here is to provide wrappers
- // to the map getter, so it could be easily manipulated
- // create the new map instance to be
- // populated on demand
- private val map: MutableMap<String, Any> = mutableMapOf()
-
- // the commands map will allow the system call map being
- // populated only on demand, that is, if the key is not
- // found, this map will provide the corresponding method
- // and update the value
- // create the new map of commands and
- // add the corresponding system calls
- private val commands: MutableMap<String, () -> Any> = mutableMapOf(
- "cygwin" to {
- // Implements the body of the command. In this particular
- // instance, it checks if we are inside a Cygwin environment.
- // Returns a boolean value indicating if we are inside a Cygwin
- // environment.
-
- // execute a new system call to 'uname -s', read the output
- // as an UTF-8 string, lowercase it and check if it starts
- // with the 'cygwin' string; if so, we are inside Cygwin
- executeSystemCommand(Command("uname", "-s"))
- .second.toLowerCase().startsWith("cygwin")
- })
-
- /**
- * Gets the object indexed by the provided key. This method actually holds
- * the map method of the very same name.
- *
- * @param key The provided map key.
- * @return The object indexed by the provided map key.
- */
- @Throws(NoSuchElementException::class, AraraException::class)
- operator fun get(key: String): Any {
- // if key is not found, meaning that
- // the value wasn't required before
- if (!map.containsKey(key)) {
- if (commands.containsKey(key))
- // perform the system call and
- // populate the corresponding value
- map[key] = commands[key]!!.invoke()
- else
- throw AraraException("The requested key could not be " +
- "translated into a command to get the call value.")
- }
-
- // simply return the corresponding
- // value based on the provided key
- return map.getValue(key)
- }
-
- /**
- * Executes a system command from the underlying operating system and
- * returns a pair containing the exit status and the command output as a
- * string.
- * @param command The system command to be executed.
- * @return A pair containing the exit status and the system command output
- * as a string.
- */
- fun executeSystemCommand(command: Command): Pair<Int, String> {
- return ProcessExecutor(command.elements).runCatching {
- directory(command.workingDirectory)
- readOutput(true)
- execute().run {
- exitValue to outputUTF8()
- }
- }.getOrElse {
- // quack, quack, do nothing, just
- // return a default error code
- errorExitStatus to errorCommandOutput
- }
- }
-}
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/TeeOutputStream.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/TeeOutputStream.kt
deleted file mode 100644
index dee15176f2..0000000000
--- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/TeeOutputStream.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-package org.islandoftex.arara.utils
-
-import java.io.IOException
-import java.io.OutputStream
-
-/**
- * Implements a stream splitter.
- *
- * @author Island of TeX
- * @version 5.0
- * @since 4.0
- */
-class TeeOutputStream(
- /**
- * The array of output streams holds every output stream that will be
- * written to.
- */
- vararg outputStreams: OutputStream
-) : OutputStream() {
- /**
- * An array of streams in which an object of this class will split data.
- */
- private val streams: List<OutputStream> = outputStreams.toList()
-
- /**
- * Writes the provided integer to each stream.
- *
- * @param b The provided integer
- * @throws IOException An IO exception.
- */
- @Throws(IOException::class)
- override fun write(b: Int) = streams.forEach { it.write(b) }
-
- /**
- * Writes the provided byte array to each stream, with the provided offset
- * and length.
- *
- * @param b The byte array.
- * @param offset The offset.
- * @param length The length.
- * @throws IOException An IO exception.
- */
- @Throws(IOException::class)
- override fun write(b: ByteArray, offset: Int, length: Int) =
- streams.forEach { it.write(b, offset, length) }
-
- /**
- * Flushes every stream.
- *
- * @throws IOException An IO exception.
- */
- @Throws(IOException::class)
- override fun flush() = streams.forEach { it.flush() }
-
- /**
- * Closes every stream silently.
- */
- override fun close() = streams.forEach {
- try {
- it.close()
- } catch (ignored: IOException) {
- // do nothing on purpose
- }
- }
-}