diff options
Diffstat (limited to 'support/arara/source/src/main/kotlin')
37 files changed, 0 insertions, 4858 deletions
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/Arara.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/Arara.kt deleted file mode 100644 index 2b4efbf8db..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/Arara.kt +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara - -import com.github.ajalt.clikt.parameters.options.versionOption -import com.uchuhimo.konf.Config -import java.time.LocalDate -import kotlin.time.ExperimentalTime -import org.islandoftex.arara.configuration.AraraSpec -import org.islandoftex.arara.configuration.Configuration -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.model.AraraException -import org.islandoftex.arara.model.Extractor -import org.islandoftex.arara.model.Interpreter -import org.islandoftex.arara.ruleset.DirectiveUtils -import org.islandoftex.arara.utils.DisplayUtils - -/** - * arara's main entry point - * - * @author Island of TeX - * @version 5.0 - * @since 5.0 - */ -object Arara { - // TODO: watch config files - val baseconfig = Config { addSpec(AraraSpec) } - .from.env() - .from.systemProperties() - var config = baseconfig.withLayer("initial") - - /** - * Main method. This is the application entry point. - * @param args A string array containing all command line arguments. - */ - @ExperimentalTime - @JvmStatic - fun main(args: Array<String>) { - // print the arara logo in the terminal; I just - // hope people use this tool in a good terminal with - // fixed-width fonts, otherwise the logo will be messed - DisplayUtils.printLogo() - - val version = config[AraraSpec.Application.version] - CLI().versionOption(version, names = setOf("-V", "--version"), - message = { - "arara $version\n" + - "Copyright (c) ${LocalDate.now().year}, Island of TeX\n" + - LanguageController.getMessage(Messages - .INFO_PARSER_NOTES) + "\n\n" + - "New features in version $version:\n" + - Arara::class.java - .getResource("/org/islandoftex/arara/configuration/release-notes") - .readText() - }) - .main(args) - } - - @ExperimentalTime - fun run() { - try { - // first of all, let's try to load a potential - // configuration file located at the current - // user's home directory; if there's a bad - // configuration file, arara will panic and - // end the execution - Configuration.load() - - // let's print the current file information; it is a - // basic display, just the file name, the size properly - // formatted as a human readable format, and the last - // modification date; also, in this point, the logging - // feature starts to collect data (of course, if enabled - // either through the configuration file or manually - // in the command line) - DisplayUtils.printFileInformation() - - // time to read the file and try to extract the directives; - // extract() brings us a list of directives properly parsed - // and almost ready to be handled; note that no directives - // in the provided file will raise an exception; this is - // by design and I opted to not include a default fallback - // (although it wouldn't be so difficult to write one, - // I decided not to take the risk) - val extracted = Extractor.extract(config[AraraSpec.Execution - .reference]) - - // it is time to validate the directives (for example, we have - // a couple of keywords that cannot be used as directive - // parameters); another interesting feature of the validate() - // method is to replicate a directive that has the 'files' - // keyword on it, since it's the whole point of having 'files' - // in the first place; if you check the log file, you will see - // that the list of extracted directives might differ from - // the final list of directives to be effectively processed - // by arara - val directives = DirectiveUtils.process(extracted) - - // time to shine, now the interpreter class will interpret - // one directive at a time, get the corresponding rule, - // set the parameters, evaluate it, get the tasks, run them, - // evaluate the result and print the status; note that - // arara, from this version on, will try to evaluate things - // progressively, so in case of an error, the previous tasks - // were already processed and potentially executed - Interpreter(directives).execute() - } catch (exception: AraraException) { - // something bad just happened, so arara will print the proper - // exception and provide details on it, if available; the idea - // here is to propagate an exception throughout the whole - // application and catch it here instead of a local treatment - DisplayUtils.printException(exception) - } - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/CLI.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/CLI.kt deleted file mode 100644 index 9de0f1bd3f..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/CLI.kt +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara - -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.arguments.argument -import com.github.ajalt.clikt.parameters.arguments.multiple -import com.github.ajalt.clikt.parameters.options.default -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.types.int -import com.github.ajalt.clikt.parameters.types.path -import com.github.ajalt.clikt.parameters.types.restrictTo -import kotlin.system.exitProcess -import kotlin.time.ClockMark -import kotlin.time.ExperimentalTime -import kotlin.time.MonoClock -import kotlin.time.milliseconds -import org.islandoftex.arara.configuration.AraraSpec -import org.islandoftex.arara.filehandling.FileSearchingUtils -import org.islandoftex.arara.localization.Language -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.model.AraraException -import org.islandoftex.arara.model.Session -import org.islandoftex.arara.utils.CommonUtils -import org.islandoftex.arara.utils.DisplayUtils -import org.islandoftex.arara.utils.LoggingUtils - -/** - * arara's command line interface - * - * @author Island of TeX - * @version 5.0 - * @since 5.0 - */ -class CLI : CliktCommand(name = "arara", printHelpOnEmptyArgs = true) { - private val log by option("-l", "--log", - help = "Generate a log output") - .flag(default = AraraSpec.Execution.logging.default) - private val verbose by option("-v", "--verbose", - help = "Print the command output") - .flag("-s", "--silent", - default = AraraSpec.Execution.verbose.default) - private val dryrun by option("-n", "--dry-run", - help = "Go through all the motions of running a command, but " + - "with no actual calls") - .flag(default = AraraSpec.Execution.dryrun.default) - private val onlyheader by option("-H", "--header", - help = "Extract directives only in the file header") - .flag(default = AraraSpec.Execution.onlyHeader.default) - private val timeout by option("-t", "--timeout", - help = "Set the execution timeout (in milliseconds)") - .int().restrictTo(min = 1) - private val language by option("-L", "--language", - help = "Set the application language") - .default(AraraSpec.Application.defaultLanguageCode.default) - private val maxLoops by option("-m", "--max-loops", - help = "Set the maximum number of loops (> 0)") - .int().restrictTo(min = 1) - .default(AraraSpec.Execution.maxLoops.default) - private val preamble by option("-p", "--preamble", - help = "Set the file preamble based on the configuration file") - private val workingDirectory by option("-d", "--working-directory", - help = "Set the working directory for all tools") - .path(exists = true, fileOkay = false, readable = true) - .default(AraraSpec.Execution.workingDirectory.default) - - private val reference by argument("file", - help = "The file(s) to evaluate and process") - .multiple(required = true) - - /** - * Update the default configuration with the values parsed from the - * command line. - */ - @ExperimentalTime - private fun updateConfigurationFromCommandLine() { - Arara.config[AraraSpec.Execution.language] = Language(language) - LanguageController.setLocale(Arara.config[AraraSpec.Execution.language] - .locale) - - Arara.config[AraraSpec.Execution.logging] = log - Arara.config[AraraSpec.Execution.verbose] = verbose - Arara.config[AraraSpec.Execution.dryrun] = dryrun - Arara.config[AraraSpec.Execution.onlyHeader] = onlyheader - Arara.config[AraraSpec.Execution.maxLoops] = maxLoops - Arara.config[AraraSpec.Execution.workingDirectory] = workingDirectory - preamble?.let { - val preambles = Arara.config[AraraSpec.Execution.preambles] - if (preambles.containsKey(it)) { - Arara.config[AraraSpec.Execution.preamblesActive] = true - Arara.config[AraraSpec.Execution.preamblesContent] = - // will never throw (see check above) - preambles.getValue(it) - } else { - throw AraraException( - LanguageController.getMessage( - Messages.ERROR_PARSER_INVALID_PREAMBLE, it) - ) - } - } - timeout?.let { - Arara.config[AraraSpec.Execution.timeout] = true - Arara.config[AraraSpec.Execution.timeoutValue] = it.milliseconds - } - - Arara.config[AraraSpec.UserInteraction.displayTime] = true - } - - /** - * The actual main method of arara (when run in command-line mode) - */ - @ExperimentalTime - override fun run() { - // the first component to be initialized is the - // logging controller; note init() actually disables - // the logging, so early exceptions won't generate - // a lot of noise in the terminal - LoggingUtils.init() - - // arara features a stopwatch, so we can see how much time has passed - // since everything started; internally, this class makes use of - // nano time, so we might get an interesting precision here - // (although timing is not a serious business in here, it's - // just a cool addition) - val executionStart: ClockMark = MonoClock.markNow() - - // arara stores the environment variables accessible at the start - // of the execution in the session object for the user - Session.updateEnvironmentVariables() - - // logging has to be initialized only once and for all because - // context resets lead to missing output - LoggingUtils.enableLogging(log) - - try { - // TODO: this will have to change for parallelization - reference.forEach { - // TODO: do we have to reset some more file-specific config? - // especially the working directory will have to be set and - // changed - Arara.config = Arara.baseconfig.withLayer(it) - // next, update the configuration - updateConfigurationFromCommandLine() - FileSearchingUtils.discoverFile(it) - Arara.run() - // add an empty line between file executions - println() - } - - // this is the last command from arara; once the execution time is - // available, print it; note that this notification is suppressed - // when the command line parsing returns false as result (it makes - // no sense to print the execution time for a help message, I guess) - DisplayUtils.printTime(executionStart.elapsedNow().inSeconds) - } catch (ex: AraraException) { - DisplayUtils.printException(ex) - Arara.config[AraraSpec.Execution.status] = 2 - } - - // gets the application exit status; the rule here is: - // 0 : everything went just fine (note that the dry-run mode always - // makes arara exit with 0, unless it is an error in the directive - // builder itself). - // 1 : one of the tasks failed, so the execution ended abruptly. This - // means the error relies on the command line call, not with arara. - // 2 : arara just handled an exception, meaning that something bad - // just happened and might require user intervention. - exitProcess(CommonUtils.exitStatus) - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/AraraSpec.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/AraraSpec.kt deleted file mode 100644 index 14100ec98b..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/AraraSpec.kt +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.configuration - -import com.uchuhimo.konf.ConfigSpec -import java.io.File -import java.nio.file.Paths -import kotlin.time.ExperimentalTime -import kotlin.time.milliseconds -import org.islandoftex.arara.localization.Language - -/** - * Configuration hierarchy for arara - * - * @author Island of TeX - * @version 5.0 - * @since 5.0 - */ -@Suppress("MagicNumber") -object AraraSpec : ConfigSpec() { - object Application : ConfigSpec() { - val defaultLanguageCode by optional("en") - val version by optional(AraraSpec::class.java.`package`.implementationVersion - ?: "DEVELOPMENT BUILD") - val namePattern by optional("arara:\\s") - val width by optional(65) - } - - object Execution : ConfigSpec() { - val maxLoops by optional(10) - val timeout by optional(false) - @ExperimentalTime - val timeoutValue by optional(0.milliseconds) - val haltOnErrors by optional(true) - - val databaseName by optional("arara") - val logName by optional("arara") - - val verbose by optional(false) - val language by optional(Language(Application.defaultLanguageCode.default)) - val logging by optional(false) - val dryrun by optional(false) - val status by optional(0) - val fileTypes by optional(ConfigurationUtils.defaultFileTypes) - val rulePaths by optional(setOf( - ConfigurationUtils.applicationPath.resolve("rules") - .toString() - )) - val preambles by optional(mapOf<String, String>()) - val preamblesActive by optional(false) - val preamblesContent by optional("") - - val workingDirectory by optional(Paths.get("")) - val configurationName by optional("[none]") - val onlyHeader by optional(false) - - // TODO: these are runtime values, they should be properly - // initialized and tested (maybe move them into their own - // Spec or session) - val reference by optional(File("/tmp/")) - val file by optional(File("/tmp/")) - - object InfoSpec : ConfigSpec() { - val ruleId by optional<String?>(null) - val rulePath by optional<String?>(null) - } - - object DirectiveSpec : ConfigSpec() { - val lines by optional(listOf<Int>()) - } - - val filePattern by optional("") - } - - object Directive : ConfigSpec() { - val linebreakPattern by optional("^\\s*-->\\s(.*)$") - - private const val directivestart = """^\s*(\w+)\s*(:\s*(\{.*\})\s*)?""" - private const val pattern = """(\s+(if|while|until|unless)\s+(\S.*))?$""" - val directivePattern by optional(directivestart + pattern) - } - - object UserInteraction : ConfigSpec() { - val lookAndFeel by optional("none") - val displayTime by optional(false) - val displayLine by optional(true) - val displayResult by optional(false) - val displayRolling by optional(false) - val displayException by optional(false) - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/Configuration.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/Configuration.kt deleted file mode 100644 index 06abeeb6db..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/Configuration.kt +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.configuration - -import kotlin.time.ExperimentalTime -import org.islandoftex.arara.Arara -import org.islandoftex.arara.filehandling.FileHandlingUtils -import org.islandoftex.arara.localization.Language -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.model.AraraException - -/** - * Implements the configuration model, which holds the default settings and can - * load the configuration file. The idea here is to provide a map that holds - * all configuration settings used by model and utilitary classes throughout - * the execution. This controller is implemented as a singleton. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object Configuration { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - /** - * Loads the application configuration. - * - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @ExperimentalTime - @Throws(AraraException::class) - fun load() { - // initialize both file type and language models, - // since we can track errors from there instead - // of relying on a check on this level - - // get the configuration file, if any - val file = ConfigurationUtils.configFile - if (file != null) { - // set the configuration file name for - // logging purposes - Arara.config[AraraSpec.Execution.configurationName] = - FileHandlingUtils.getCanonicalPath(file) - - // then validate it and update the - // configuration accordingly - val resource = ConfigurationUtils.loadLocalConfiguration(file) - update(resource) - } - - // just to be sure, update the - // current locale in order to - // display localized messages - val locale = Arara.config[AraraSpec.Execution.language].locale - LanguageController.setLocale(locale) - } - - /** - * Update the configuration based on the provided map. - * - * @param resource Map containing the new configuration settings. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - private fun update(resource: LocalConfiguration) { - if (resource.paths.isNotEmpty()) - Arara.config[AraraSpec.Execution.rulePaths] = - ConfigurationUtils.normalizePaths(resource.paths) - - if (resource.filetypes.isNotEmpty()) { - Arara.config[AraraSpec.Execution.fileTypes] = ConfigurationUtils.normalizeFileTypes(resource.filetypes) - } - - Arara.config[AraraSpec.Execution.verbose] = resource.isVerbose - Arara.config[AraraSpec.Execution.logging] = resource.isLogging - Arara.config[AraraSpec.Execution.onlyHeader] = resource.isHeader - Arara.config[AraraSpec.Execution.language] = - Language(resource.language) - Arara.config[AraraSpec.UserInteraction.lookAndFeel] = resource.laf - - Arara.config[AraraSpec.Execution.databaseName] = - ConfigurationUtils.cleanFileName(resource.dbname) - Arara.config[AraraSpec.Execution.logName] = - ConfigurationUtils.cleanFileName(resource.logname) - - val loops = resource.loops - if (loops <= 0) { - throw AraraException(messages.getMessage(Messages - .ERROR_CONFIGURATION_LOOPS_INVALID_RANGE)) - } else { - Arara.config[AraraSpec.Execution.maxLoops] = loops - } - - if (resource.preambles.isNotEmpty()) - Arara.config[AraraSpec.Execution.preambles] = resource.preambles - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/ConfigurationUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/ConfigurationUtils.kt deleted file mode 100644 index a48a6d0d3f..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/ConfigurationUtils.kt +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.configuration - -import com.charleskorn.kaml.Yaml -import java.io.File -import java.io.UnsupportedEncodingException -import java.net.URLDecoder -import java.nio.file.Path -import java.nio.file.Paths -import org.islandoftex.arara.Arara -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.model.AraraException -import org.islandoftex.arara.model.FileType -import org.islandoftex.arara.utils.CommonUtils - -/** - * Implements configuration utilitary methods. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object ConfigurationUtils { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - /** - * This map contains all file types that arara accepts - * and their corresponding search patterns (for comments). - */ - val defaultFileTypePatterns = mapOf( - "tex" to "^\\s*%\\s+", - "dtx" to "^\\s*%\\s+", - "ltx" to "^\\s*%\\s+", - "drv" to "^\\s*%\\s+", - "ins" to "^\\s*%\\s+" - ) - - /** - * Set of default file types provided by arara. - * Initialization may throw AraraException if file types are wrong - */ - val defaultFileTypes: Set<FileType> by lazy { - defaultFileTypePatterns - .map { (extension, pattern) -> FileType(extension, pattern) } - .toSet() - } - - /** - * The configuration file in use. - * - * Look for configuration files in the user's working directory first - * if no configuration files are found in the user's working directory, - * try to look up in a global directory, that is, the user home. - */ - val configFile: File? - get() { - val names = listOf(".araraconfig.yaml", - "araraconfig.yaml", ".arararc.yaml", "arararc.yaml") - Arara.config[AraraSpec.Execution.workingDirectory] - .let { workingDir -> - val first = names - .map { workingDir.resolve(it).toFile() } - .firstOrNull { it.exists() } - if (first != null) - return first - } - CommonUtils.getSystemPropertyOrNull("user.home")?.let { userHome -> - return names.map { File(userHome).resolve(it) } - .firstOrNull { it.exists() } - } - return null - } - - /** - * The canonical absolute application path. - * - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - val applicationPath: Path - @Throws(AraraException::class) - get() { - try { - var path = Arara::class.java.protectionDomain.codeSource - .location.path - path = URLDecoder.decode(path, "UTF-8") - return Paths.get(File(path).toURI()).parent.toAbsolutePath() - } catch (exception: UnsupportedEncodingException) { - throw AraraException( - messages.getMessage( - Messages.ERROR_GETAPPLICATIONPATH_ENCODING_EXCEPTION - ), - exception - ) - } - } - - /** - * Validates the configuration file. - * - * @param file The configuration file. - * @return The configuration file as a resource. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun loadLocalConfiguration(file: File): LocalConfiguration { - return file.runCatching { - val text = readText() - if (!text.startsWith("!config")) - throw Exception("Configuration should start with !config") - Yaml.default.parse(LocalConfiguration.serializer(), - text) - }.getOrElse { - throw AraraException(messages.getMessage( - Messages.ERROR_CONFIGURATION_GENERIC_ERROR), it) - } - } - - /** - * Normalize a list of rule paths, removing all duplicates. - * - * @param paths The list of rule paths. - * @return A list of normalized paths, without duplicates. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun normalizePaths(paths: Iterable<String>): Set<String> = - paths.union(AraraSpec.Execution.rulePaths.default) - - /** - * Normalize a list of file types, removing all duplicates. - * - * @param types The list of file types. - * @return A list of normalized file types, without duplicates. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun normalizeFileTypes(types: Iterable<FileType>): Set<FileType> = - types.union(defaultFileTypes) - - /** - * Cleans the file name to avoid invalid entries. - * - * @param name The file name. - * @return A cleaned file name. - */ - fun cleanFileName(name: String): String { - val result = File(name).name.trim() - return if (result.isEmpty()) "arara" else result.trim() - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/LocalConfiguration.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/LocalConfiguration.kt deleted file mode 100644 index 959f2a9ab8..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/configuration/LocalConfiguration.kt +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.configuration - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import org.islandoftex.arara.Arara -import org.islandoftex.arara.model.FileType -import org.islandoftex.arara.utils.CommonUtils -import org.mvel2.templates.TemplateRuntime - -/** - * A local configuration which resembles configuration files in the working - * directory. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@Serializable -class LocalConfiguration { - // rule paths - var paths: List<String> = listOf() - get() { - val user = mapOf( - "home" to (CommonUtils.getSystemPropertyOrNull("user.home") - ?: ""), - "name" to (CommonUtils.getSystemPropertyOrNull("user.name") - ?: "")) - val application = mapOf( - "workingDirectory" to Arara.config[AraraSpec.Execution.workingDirectory].toAbsolutePath().toString() - ) - - return field.map { input -> - var path = CommonUtils.removeKeywordNotNull(input) - try { - path = TemplateRuntime.eval(path, mapOf( - "user" to user, "application" to application - )) as String - } catch (_: RuntimeException) { - // do nothing, gracefully fallback to - // the default, unparsed path - } - path - } - } - - // file types - var filetypes: List<FileType> = listOf() - - // the application language - // default to English - var language: String = Arara.config[AraraSpec.Application.defaultLanguageCode] - get() = CommonUtils.removeKeywordNotNull(field) - - // maximum number of loops - var loops: Int = Arara.config[AraraSpec.Execution.maxLoops] - - // verbose flag - @SerialName("verbose") - var isVerbose: Boolean = Arara.config[AraraSpec.Execution.verbose] - - // logging flag - @SerialName("logging") - var isLogging: Boolean = Arara.config[AraraSpec.Execution.logging] - - // header flag - @SerialName("header") - var isHeader: Boolean = Arara.config[AraraSpec.Execution.onlyHeader] - - // database name - var dbname: String = Arara.config[AraraSpec.Execution.databaseName] - get() = CommonUtils.removeKeywordNotNull(field) - - // log name - var logname: String = Arara.config[AraraSpec.Execution.logName] - get() = CommonUtils.removeKeywordNotNull(field) - - // map of preambles - var preambles: Map<String, String> = Arara.config[AraraSpec.Execution.preambles] - - // look and feel - // default to none - var laf: String = Arara.config[AraraSpec.UserInteraction.lookAndFeel] - get() = CommonUtils.removeKeywordNotNull(field) -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/Database.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/Database.kt deleted file mode 100644 index e4b98cd194..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/Database.kt +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.filehandling - -import kotlinx.serialization.Serializable - -/** - * The database model, which keeps track on file changes. - * - * This database is a map because it maps files to hashes. So the key will - * always be a file representation and the value always a string. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@Serializable -data class Database( - /** - * The whole database is implemented as a map, where - * the key is the absolute canonical file and the value - * is its corresponding CRC32 hash. - */ - val map: MutableMap<String, String> = mutableMapOf() -) : MutableMap<String, String> by map diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/DatabaseUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/DatabaseUtils.kt deleted file mode 100644 index f4728c9366..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/DatabaseUtils.kt +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.filehandling - -import com.charleskorn.kaml.Yaml -import java.io.File -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 - -/** - * Implements database utilitary methods. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object DatabaseUtils { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - /** - * Gets the file representing the YAML file (database). - * - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - private val file: File - @Throws(AraraException::class) - get() { - val reference = Arara.config[AraraSpec.Execution.reference] - val name = "${Arara.config[AraraSpec.Execution.databaseName]}.yaml" - val path = FileHandlingUtils.getParentCanonicalFile(reference) - return path.resolve(name) - } - - /** - * Loads the YAML file representing the database. - * - * @return The database object. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun load(): Database { - return if (!exists()) { - Database() - } else { - file.runCatching { - val text = readText() - if (!text.startsWith("!database")) - throw Exception("Database should start with !database") - Yaml.default.parse(Database.serializer(), text) - }.getOrElse { - it.printStackTrace() - throw AraraException(messages.getMessage(Messages - .ERROR_LOAD_COULD_NOT_LOAD_XML, file.name), it) - } - } - } - - /** - * Saves the database on a YAML file. - * - * @param database The database object. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun save(database: Database) { - file.runCatching { - val content = "!database\n" + - Yaml.default.stringify(Database.serializer(), database) - writeText(content) - }.getOrElse { - throw AraraException( - messages.getMessage( - Messages.ERROR_SAVE_COULD_NOT_SAVE_XML, - file.name - ), it) - } - } - - /** - * Checks if the YAML file representing the database exists. - * - * @return A boolean value indicating if the YAML file exists. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - private fun exists(): Boolean { - return file.exists() - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/FileHandlingUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/FileHandlingUtils.kt deleted file mode 100644 index 07f1ecfab9..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/FileHandlingUtils.kt +++ /dev/null @@ -1,319 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.filehandling - -import java.io.File -import java.io.IOException -import java.text.SimpleDateFormat -import java.util.zip.CRC32 -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 - -/** - * Implements file handling utilitary methods. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object FileHandlingUtils { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - /** - * Gets the reference of the current file in execution. Note that this - * method might return a value different than the main file provided in - * the command line. - * - * @return A reference of the current file in execution. Might be different - * than the main file provided in the command line. - */ - private val currentFile: File - get() = Arara.config[AraraSpec.Execution.file] - - /** - * Writes the string to a file, using UTF-8 as default encoding. - * @param file The file. - * @param text The string to be written. - * @param append A flag whether to append the content. - * @return A logical value indicating whether it was successful. - */ - fun writeToFile(file: File, text: String, append: Boolean): Boolean { - return try { - // try to write the provided - // string to the file, with - // UTF-8 as encoding - if (append) - file.appendText(text, Charsets.UTF_8) - else - file.writeText(text, Charsets.UTF_8) - true - } catch (_: IOException) { - // if something bad happens, - // gracefully fallback to - // reporting the failure - false - } - } - - /** - * Writes the string list to a file, using UTF-8 as default encoding. - * @param file The file. - * @param lines The string list to be written. - * @param append A flag whether to append the content. - * @return A logical value indicating whether it was successful. - */ - fun writeToFile( - file: File, - lines: List<String>, - append: Boolean - ): Boolean = - try { - writeToFile(file, lines.joinToString(System.lineSeparator()), - append) - } catch (_: IOException) { - false - } - - /** - * Reads the provided file (UTF-8) into a list of strings. - * @param file The file. - * @return A list of strings. - */ - fun readFromFile(file: File): List<String> { - return try { - // returns the contents of - // the provided file as - // a list of strings - file.readLines(Charsets.UTF_8) - } catch (_: IOException) { - // if something bad happens, - // gracefully fallback to - // an empty file list - listOf() - } - } - - /** - * Checks if a file exists based on its extension. - * - * @param extension The extension. - * @return A boolean value indicating if the file exists. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun exists(extension: String): Boolean { - val file = File(getPath(extension)) - return file.exists() - } - - /** - * Gets the parent canonical path of a file. - * - * @param file The file. - * @return The parent canonical path of a file. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun getParentCanonicalPath(file: File): String { - return getParentCanonicalFile(file).toString() - } - - /** - * Gets the parent canonical file of a file. - * - * @param file The file. - * @return The parent canonical file of a file. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun getParentCanonicalFile(file: File): File { - return file.runCatching { - this.canonicalFile.parentFile - }.getOrElse { - // it is IOException || is is SecurityException - throw AraraException(messages.getMessage( - Messages.ERROR_GETPARENTCANONICALPATH_IO_EXCEPTION), it) - } - } - - /** - * Gets the full file path based on the provided extension. - * - * @param extension The extension. - * @return A string containing the full file path. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun getPath(extension: String): String { - val name = currentFile.nameWithoutExtension + ".$extension" - val path = getParentCanonicalFile(currentFile) - return path.resolve(name).toString() - } - - /** - * Gets the canonical path from the provided file. - * - * @param file The file. - * @return The canonical path from the provided file. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun getCanonicalPath(file: File): String { - return getCanonicalFile(file).toString() - } - - /** - * Gets the canonical file from the provided file. - * - * @param file The file. - * @return The canonical file from the provided file. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun getCanonicalFile(file: File): File { - try { - return file.canonicalFile - } catch (exception: IOException) { - throw AraraException( - messages.getMessage( - Messages.ERROR_GETCANONICALFILE_IO_EXCEPTION - ), - exception - ) - } - } - - /** - * Gets the date the provided file was last modified. - * - * @param file The file. - * @return A string representation of the date the provided file was last - * modified. - */ - fun getLastModifiedInformation(file: File): String { - return SimpleDateFormat("MM/dd/yyyy HH:mm:ss") - .format(file.lastModified()) - } - - /** - * Calculates the CRC32 checksum of the provided file. - * - * @param file The file. - * @return A string containing the CRC32 checksum of the provided file. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun calculateHash(file: File): String { - try { - return String.format("%08x", CRC32().run { - update(file.readBytes()) - value - }) - } catch (exception: IOException) { - throw AraraException(messages.getMessage(Messages - .ERROR_CALCULATEHASH_IO_EXCEPTION), exception) - } - } - - /** - * Gets the extension of a file. - * - * @param file The file. - * @return The corresponding file type. - */ - fun getFileExtension(file: File): String = file.extension - - /** - * Gets the base name of a file. - * - * @param file The file. - * @return The corresponding base name. - */ - fun getBasename(file: File): String = file.nameWithoutExtension - - /** - * Checks if a file has changed since the last verification. - * - * @param file The file. - * @return A boolean value indicating if the file has changed since the last - * verification. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun hasChanged(file: File): Boolean { - val database = DatabaseUtils.load() - val path = getCanonicalPath(file) - return if (!file.exists()) { - if (database.containsKey(path)) { - database.remove(path) - DatabaseUtils.save(database) - true - } else { - false - } - } else { - val hash = calculateHash(file) - if (database.containsKey(path)) { - val value = database[path] - if (hash == value) { - false - } else { - database[path] = hash - DatabaseUtils.save(database) - true - } - } else { - database[path] = hash - DatabaseUtils.save(database) - true - } - } - } - - /** - * Checks if the file has changed since the last verification based on the - * provided extension. - * - * @param extension The provided extension. - * @return A boolean value indicating if the file has changed since the last - * verification. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun hasChanged(extension: String): Boolean = - hasChanged(File(getPath(extension))) - - /** - * Checks whether a directory is under a root directory. - * - * @param child Directory to be inspected. - * @param parent Root directory. - * @return Logical value indicating whether the directoy is under root. - * @throws AraraException There was a problem with path retrieval. - */ - @Throws(AraraException::class) - fun isSubDirectory(child: File, parent: File): Boolean { - return if (child.isDirectory && parent.isDirectory) { - getCanonicalPath(child).startsWith( - getParentCanonicalPath(parent) + File.separator - ) - } else { - throw AraraException(messages.getMessage( - Messages.ERROR_ISSUBDIRECTORY_NOT_A_DIRECTORY, - child.name)) - } - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/FileSearchingUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/FileSearchingUtils.kt deleted file mode 100644 index 5c61bdb2db..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/filehandling/FileSearchingUtils.kt +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.filehandling - -import java.io.File -import java.io.FileFilter -import java.nio.file.FileSystems -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.utils.CommonUtils - -/** - * Implements file searching utilitary methods. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object FileSearchingUtils { - /** - * List all files from the provided directory according to the list of - * extensions. The leading dot must be omitted, unless it is part of the - * extension. - * @param directory The provided directory. - * @param extensions The list of extensions. - * @param recursive A flag indicating whether the search is recursive. - * @return A list of files. - */ - fun listFilesByExtensions( - directory: File, - extensions: List<String>, - recursive: Boolean - ): - List<File> = try { - // return the result of the - // provided search - if (recursive) - directory.walkTopDown().asSequence() - .filter { !it.isDirectory } - .filter { extensions.contains(it.extension) } - .toList() - else - directory.listFiles( - FileFilter { extensions.contains(it.extension) })!! - .toList() - } catch (_: Exception) { - // if something bad happens, - // gracefully fallback to - // an empty file list - listOf() - } - - /** - * List all files from the provided directory matching the list of file - * name patterns. Such list can contain wildcards. - * @param directory The provided directory. - * @param patterns The list of file name patterns. - * @param recursive A flag indicating whether the search is recursive. - * @return A list of files. - */ - fun listFilesByPatterns( - directory: File, - patterns: List<String>, - recursive: Boolean - ): - List<File> = try { - // return the result of the provided - // search, with the wildcard filter - // and a potential recursive search - val pathMatcher = patterns.map { - FileSystems.getDefault().getPathMatcher("glob:$it") - } - if (recursive) - directory.walkTopDown().asSequence() - .filter { !it.isDirectory } - .filter { file -> - pathMatcher.any { it.matches(file.toPath().fileName) } - }.toList() - else - directory.listFiles { file: File -> - pathMatcher.any { it.matches(file.toPath().fileName) } - }!!.toList() - } catch (_: Exception) { - // if something bad happens, - // gracefully fallback to - // an empty file list - listOf() - } - - /** - * Discovers the file through string reference lookup and sets the - * configuration accordingly. - * - * @param reference The string reference. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun discoverFile(reference: String) { - lookupFile(reference) - ?: throw AraraException( - LanguageController.getMessage( - Messages.ERROR_DISCOVERFILE_FILE_NOT_FOUND, - reference, - CommonUtils.fileTypesList - ) - ) - } - - /** - * Performs a file lookup based on a string reference. - * - * @param reference The file reference as a string. - * @return The file as result of the lookup operation. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - private fun lookupFile(reference: String): File? { - val types = Arara.config[AraraSpec.Execution.fileTypes] - val file = Arara.config[AraraSpec.Execution.workingDirectory] - .resolve(reference).toFile() - val name = file.name - val parent = FileHandlingUtils.getParentCanonicalFile(file) - - // direct search, so we are considering - // the reference as a complete name - val testFile = parent.resolve(name) - if (testFile.exists() && testFile.isFile) { - types.firstOrNull { - testFile.toString().endsWith("." + it.extension) - }?.let { - Arara.config[AraraSpec.Execution.filePattern] = - it.pattern - Arara.config[AraraSpec.Execution.reference] = testFile - return testFile - } - } - - // indirect search; in this case, we are considering - // that the file reference has an implicit extension, - // so we need to add it and look again - // TODO: disable this step in safe mode - return types.map { parent.resolve("$name.${it.extension}") to it } - .union(types.map { - parent.resolve("${name.removeSuffix(".").trim()}.${it.extension}") to it - }) - .firstOrNull { it.first.exists() && it.first.isFile } - ?.let { - Arara.config[AraraSpec.Execution.filePattern] = - it.second.pattern - Arara.config[AraraSpec.Execution.reference] = it.first - file - } - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/Language.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/Language.kt deleted file mode 100644 index 2afa77c41e..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/Language.kt +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.localization - -import java.util.Locale -import org.islandoftex.arara.model.AraraException - -/** - * Implements the language model. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -class Language(code: String) { - // the language code, based on - // ISO 639-1 and language variants - private val code: String - - /** - * Gets the language name. - * - * @return A string representing the language name. - */ - val name: String - get() = languages.getValue(code).first - - /** - * Gets the language locale. - * - * @return The language locale. - */ - val locale: Locale - get() = languages.getValue(code).second - - // throws an exception on invalid language - init { - if (languages.containsKey(code)) { - this.code = code - } else { - throw AraraException( - LanguageController.getMessage( - Messages.ERROR_LANGUAGE_INVALID_CODE, - languagesList - ) - ) - } - } - - companion object { - // map containing all languages - // supported by arara - private val languages = mapOf( - "en" to Pair("English", Locale("en")), - "de" to Pair("German", Locale("de")), - "nl" to Pair("Dutch", Locale("nl")), - "qn" to Pair("Broad Norfolk", Locale("en", "QN")), - "ptbr" to Pair("Brazilian Portuguese", Locale("pt", "BR")), - "it" to Pair("Italian", Locale("it")) - ) - - /** - * String representing the list of available languages - * because they don't change initialized with the string - */ - val languagesList: String = "(" + languages.map { (key, value) -> - value.first + ": " + key - }.joinToString(", ") + ")" - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/LanguageController.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/LanguageController.kt deleted file mode 100644 index e000ab4d63..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/LanguageController.kt +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.localization - -import ch.qos.cal10n.IMessageConveyor -import ch.qos.cal10n.MessageConveyor -import java.util.Locale -import org.islandoftex.arara.configuration.AraraSpec - -/** - * Implements the language controller. This controller provides a singleton - * object that holds the application messages, easily available to all model - * and utilitary classes. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object LanguageController { - // the message conveyor helps us to get localized messages - // according to the provided locale - // The fallback language is set to English for all - // messages in arara. - private var conveyor: IMessageConveyor = MessageConveyor(Locale( - AraraSpec.Application.defaultLanguageCode.default)) - - /** - * Sets the current locale. This method actually resets the language - * conveyor in order to use the new locale. It's quite simple. - * @param locale The new locale for localized messages through the language - * conveyor. - */ - fun setLocale(locale: Locale) { - conveyor = MessageConveyor(locale) - } - - /** - * Gets the localized message indexed by the provided enumeration key, - * applying an array of objects as parameters. This method is a wrapper to - * the conveyor's method of the same name. - * @param E Enumeration type that represents the conveyor messages. - * @param key Key set in the provided enumeration type. - * @param parameters Array of objects to be used as parameters. - * @return A string containing a localized message indexed by the provided - * enumeration key and applied the array of objects as parameters. - */ - @Suppress("SpreadOperator") - fun <E : Enum<*>> getMessage(key: E, vararg parameters: Any): String = - conveyor.getMessage(key, *parameters) - - /** - * Gets the localized message indexed by the provided enumeration key. This - * method is a wrapper to the conveyor's method of the same name. - * @param E Enumeration type that represents the conveyor messages. - * @param key Key set in the provided enumeration type. - * @return A string containing a localized message indexed by the provided - * enumeration key. - */ - fun <E : Enum<*>> getMessage(key: E): String = conveyor.getMessage(key) -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/Messages.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/Messages.kt deleted file mode 100644 index 75d957e1df..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/localization/Messages.kt +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.localization - -import ch.qos.cal10n.BaseName -import ch.qos.cal10n.Locale -import ch.qos.cal10n.LocaleData - -/** - * This enumeration contains all application messages. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@BaseName("org.islandoftex.arara.localization.messages") -@LocaleData(Locale(value = "de", charset = "UTF-8"), - Locale(value = "en", charset = "UTF-8"), - Locale(value = "en_QN", charset = "UTF-8"), - Locale(value = "it", charset = "UTF-8"), - Locale(value = "nl", charset = "UTF-8"), - Locale(value = "pt_BR", charset = "UTF-8")) -enum class Messages { - ERROR_BASENAME_NOT_A_FILE, - ERROR_CALCULATEHASH_IO_EXCEPTION, - ERROR_CHECKBOOLEAN_NOT_VALID_BOOLEAN, - ERROR_CHECKOS_INVALID_OPERATING_SYSTEM, - ERROR_CHECKREGEX_IO_EXCEPTION, - ERROR_CONFIGURATION_GENERIC_ERROR, - ERROR_CONFIGURATION_LOOPS_INVALID_RANGE, - ERROR_DISCOVERFILE_FILE_NOT_FOUND, - ERROR_EVALUATE_COMPILATION_FAILED, - ERROR_EVALUATE_NOT_BOOLEAN_VALUE, - ERROR_EXTRACTOR_IO_ERROR, - ERROR_FILETYPE_NOT_A_FILE, - ERROR_FILETYPE_UNKNOWN_EXTENSION, - ERROR_GETAPPLICATIONPATH_ENCODING_EXCEPTION, - ERROR_GETCANONICALFILE_IO_EXCEPTION, - ERROR_GETPARENTCANONICALPATH_IO_EXCEPTION, - ERROR_INTERPRETER_ARGUMENT_IS_REQUIRED, - ERROR_INTERPRETER_COMMAND_RUNTIME_ERROR, - ERROR_INTERPRETER_DEFAULT_VALUE_RUNTIME_ERROR, - ERROR_INTERPRETER_EXIT_RUNTIME_ERROR, - ERROR_INTERPRETER_FLAG_RUNTIME_EXCEPTION, - ERROR_INTERPRETER_RULE_NOT_FOUND, - ERROR_INTERPRETER_UNKNOWN_KEYS, - ERROR_INTERPRETER_WRONG_EXIT_CLOSURE_RETURN, - ERROR_ISSUBDIRECTORY_NOT_A_DIRECTORY, - ERROR_LANGUAGE_INVALID_CODE, - ERROR_LOAD_COULD_NOT_LOAD_XML, - ERROR_PARSER_INVALID_PREAMBLE, - ERROR_PARSERULE_GENERIC_ERROR, - ERROR_REPLICATELIST_MISSING_FORMAT_ARGUMENTS_EXCEPTION, - ERROR_RULE_IDENTIFIER_AND_PATH, - ERROR_RUN_GENERIC_EXCEPTION, - ERROR_RUN_INTERRUPTED_EXCEPTION, - ERROR_RUN_INVALID_EXIT_VALUE_EXCEPTION, - ERROR_RUN_IO_EXCEPTION, - ERROR_RUN_TIMEOUT_EXCEPTION, - ERROR_RUN_TIMEOUT_INVALID_RANGE, - ERROR_SAVE_COULD_NOT_SAVE_XML, - ERROR_SESSION_OBTAIN_UNKNOWN_KEY, - ERROR_SESSION_REMOVE_UNKNOWN_KEY, - ERROR_VALIDATE_EMPTY_FILES_LIST, - ERROR_VALIDATE_FILES_IS_NOT_A_LIST, - ERROR_VALIDATE_INVALID_DIRECTIVE_FORMAT, - ERROR_VALIDATE_NO_DIRECTIVES_FOUND, - ERROR_VALIDATE_ORPHAN_LINEBREAK, - ERROR_VALIDATE_REFERENCE_IS_RESERVED, - ERROR_VALIDATE_YAML_EXCEPTION, - ERROR_VALIDATEBODY_ARGUMENT_ID_IS_RESERVED, - ERROR_VALIDATEBODY_DUPLICATE_ARGUMENT_IDENTIFIERS, - ERROR_VALIDATEBODY_MISSING_KEYS, - ERROR_VALIDATEBODY_NULL_ARGUMENT_ID, - ERROR_VALIDATEBODY_NULL_COMMAND, - ERROR_VALIDATEHEADER_NULL_ID, - ERROR_VALIDATEHEADER_NULL_NAME, - ERROR_VALIDATEHEADER_WRONG_IDENTIFIER, - INFO_DISPLAY_EXCEPTION_MORE_DETAILS, - INFO_DISPLAY_EXECUTION_TIME, - INFO_DISPLAY_FILE_INFORMATION, - INFO_INTERPRETER_DRYRUN_MODE_BOOLEAN_MODE, - INFO_INTERPRETER_DRYRUN_MODE_SYSTEM_COMMAND, - INFO_LABEL_AUTHOR, - INFO_LABEL_AUTHORS, - INFO_LABEL_CONDITIONAL, - INFO_LABEL_NO_AUTHORS, - INFO_LABEL_ON_DETAILS, - INFO_LABEL_ON_ERROR, - INFO_LABEL_ON_FAILURE, - INFO_LABEL_ON_SUCCESS, - INFO_LABEL_UNNAMED_TASK, - INFO_PARSER_NOTES, - LOG_INFO_BEGIN_BUFFER, - LOG_INFO_BOOLEAN_MODE, - LOG_INFO_DIRECTIVES_BLOCK, - LOG_INFO_END_BUFFER, - LOG_INFO_INTERPRET_RULE, - LOG_INFO_INTERPRET_TASK, - LOG_INFO_POTENTIAL_DIRECTIVE_FOUND, - LOG_INFO_POTENTIAL_PATTERN_FOUND, - LOG_INFO_RULE_LOCATION, - LOG_INFO_SYSTEM_COMMAND, - LOG_INFO_TASK_RESULT, - LOG_INFO_VALIDATED_DIRECTIVES, - LOG_INFO_WELCOME_MESSAGE -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/AraraException.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/AraraException.kt deleted file mode 100644 index 6bf2d1e617..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/AraraException.kt +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.model - -/** - * Implements the specific exception model for arara. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -class AraraException : Exception { - /** - * The underlying exception, used to hold more details - * on what really happened - */ - val exception: Exception? - - /** - * Constructor. Takes the exception message. - * @param message The exception message. - */ - constructor(message: String) : super(message) { - this.exception = null - } - - /** - * Constructor. Takes the exception message and the underlying exception. - * @param message The exception message. - * @param exception The underlying exception object. - */ - constructor(message: String, exception: Exception) : super(message) { - this.exception = exception - } - - /** - * Constructor. Takes the exception message and the underlying exception. - * @param message The exception message. - * @param throwable The underlying exception as generic throwable. - */ - constructor(message: String, throwable: Throwable) : super(message) { - this.exception = RuntimeException(throwable) - } - - /** - * Checks if there is an underlying exception defined in the current object. - * @return A boolean value indicating if the current object has an - * underlying exception. - */ - fun hasException(): Boolean = exception?.message != null -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt deleted file mode 100644 index 78b98e709a..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Evaluator.kt +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.model - -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.ruleset.Conditional -import org.islandoftex.arara.utils.Methods -import org.mvel2.templates.TemplateRuntime - -/** - * Implements the evaluator model, on which a conditional can be analyzed and - * processed. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -class Evaluator { - // this attribute holds the maximum number of - // loops arara will accept; it's like - // reaching infinity - private val loops: Int = Arara.config[AraraSpec.Execution.maxLoops] - - // the counter for the current execution, it - // helps us keep track of the number of times - // this evaluation has happened, and also to - // prevent potential infinite loops - private var counter: Int = 0 - - // a flag that indicates the - // evaluation to halt regardless - // of the the result - private var halt: Boolean = false - - /** - * Check if a condition is of type if or unless and whether halt - * is set. - * @param type The type to check. - * @param haltCheck The value [halt] should have. - * @return `(type == if || type == unless) && haltCheck` - */ - private fun isIfUnlessAndHalt( - type: Conditional.ConditionalType, - haltCheck: Boolean = true - ): Boolean = - (type == Conditional.ConditionalType.IF || - type == Conditional.ConditionalType.UNLESS) && - halt == haltCheck - - /** - * Only run the evaluation of the conditional including a check whether - * the result needs to be inverted. - * @param conditional The conditional. - * @return The result of the evaluation. - */ - @Throws(AraraException::class, RuntimeException::class) - private fun evaluateCondition(conditional: Conditional): Boolean { - val result = TemplateRuntime.eval("@{ " + conditional.condition + " }", - Methods.getConditionalMethods()) - return if (result is Boolean) { - if (conditional.type == Conditional.ConditionalType.UNLESS || - conditional.type == Conditional.ConditionalType.UNTIL) - !result - else - result - } else { - throw AraraException(messages.getMessage( - Messages.ERROR_EVALUATE_NOT_BOOLEAN_VALUE)) - } - } - - /** - * Evaluate the provided conditional. - * - * @param conditional The conditional object. - * @return A boolean value indicating if the conditional holds. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - @Suppress("TooGenericExceptionCaught") - fun evaluate(conditional: Conditional): Boolean { - // when in dry-run mode or not evaluating a - // conditional, arara always ignores conditional - // evaluations - if (conditional.type == Conditional.ConditionalType.NONE || - Arara.config[AraraSpec.Execution.dryrun] || - isIfUnlessAndHalt(conditional.type, true)) - return false - else if (isIfUnlessAndHalt(conditional.type, false)) { - halt = true - } - - // check counters and see if the execution - // has reached our concept of infinity, - // thus breaking the cycles - counter++ - return when { - conditional.type === Conditional.ConditionalType.WHILE - && counter > loops -> false - conditional.type === Conditional.ConditionalType.UNTIL - && counter >= loops -> false - else -> { - try { - evaluateCondition(conditional) - } catch (exception: RuntimeException) { - throw AraraException(messages.getMessage(Messages - .ERROR_EVALUATE_COMPILATION_FAILED), - exception) - } - } - } - } - - companion object { - // the application messages obtained from the - // language controller - private val messages = LanguageController - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Extractor.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Extractor.kt deleted file mode 100644 index ed4d811d22..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Extractor.kt +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.model - -import java.io.File -import java.io.IOException -import java.nio.charset.Charset -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.ruleset.Directive -import org.islandoftex.arara.ruleset.DirectiveUtils -import org.islandoftex.arara.utils.CommonUtils - -/** - * Extractor for directives from the provided main file. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object Extractor { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - /** - * Extracts a list of directives from the provided main file, obtained from - * the configuration controller. - * @param file The file to extract the directives from. - * @param charset The charset of the file. - * @return A list of directives. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun extract(file: File, charset: Charset = Charsets.UTF_8): - List<Directive> { - try { - val content = CommonUtils.preambleContent.toMutableList() - content.addAll(file.readLines(charset)) - return DirectiveUtils.extractDirectives(content) - } catch (ioexception: IOException) { - throw AraraException( - messages.getMessage( - Messages.ERROR_EXTRACTOR_IO_ERROR - ), - ioexception - ) - } - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/FileType.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/FileType.kt deleted file mode 100644 index e6ed27d51a..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/FileType.kt +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.model - -import java.util.regex.PatternSyntaxException -import kotlinx.serialization.Serializable -import org.islandoftex.arara.configuration.ConfigurationUtils -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.utils.CommonUtils - -/** - * Implements the file type model. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@Serializable -class FileType { - // string representing the - // file extension - var extension: String = INVALID_EXTENSION - get() = CommonUtils.removeKeywordNotNull(field) - private set - // string representing the - // file pattern to be used - // as directive lookup - var pattern: String = INVALID_PATTERN - @Throws(AraraException::class) - get() { - CommonUtils.removeKeywordNotNull(field) - if (field == INVALID_PATTERN) { - field = ConfigurationUtils.defaultFileTypePatterns[extension] - ?: throw AraraException( - LanguageController.getMessage( - Messages.ERROR_FILETYPE_UNKNOWN_EXTENSION, - extension, - CommonUtils.fileTypesList - ) - ) - } - return field - } - private set - - constructor(extension: String, pattern: String) { - this.extension = extension - this.pattern = pattern - - try { - pattern.toPattern() - } catch (e: PatternSyntaxException) { - if (!ConfigurationUtils.defaultFileTypePatterns.containsKey(extension)) - throw AraraException( - LanguageController.getMessage( - Messages.ERROR_FILETYPE_UNKNOWN_EXTENSION, - extension, - CommonUtils.fileTypesList - ) - ) - } - } - - companion object { - /** - * This constant identifies an invalid extension. As unices do not - * allow a forward and Windows does not allow a backward slash, this - * should suffice. - */ - const val INVALID_EXTENSION = "/\\" - /** - * This constant identifies an invalid pattern. This is a opening - * character class which is invalid. - */ - const val INVALID_PATTERN = "[" - } - - /** - * Provides a textual representation of the current file type object. - * @return A string containing a textual representation of the current file - * type object. - */ - override fun toString(): String { - return ".$extension" - } - - /** - * Implements the file type equals method, checking if one file type is - * equal to another. Note that only the file extension is considered. - * @param other The object to be analyzed. - * @return A boolean value indicating if those two objects are equal. - */ - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as FileType - if (extension != other.extension) return false - return true - } - - /** - * Implements the file type hash code. Note that only the file extension is - * considered. - * @return An integer representing the file type hash code. - */ - override fun hashCode(): Int { - return extension.hashCode() - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Interpreter.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Interpreter.kt deleted file mode 100644 index 49cf40f1a2..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Interpreter.kt +++ /dev/null @@ -1,375 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.model - -import java.io.File -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.ruleset.Argument -import org.islandoftex.arara.ruleset.Command -import org.islandoftex.arara.ruleset.Conditional -import org.islandoftex.arara.ruleset.Directive -import org.islandoftex.arara.ruleset.Rule -import org.islandoftex.arara.ruleset.RuleCommand -import org.islandoftex.arara.ruleset.RuleUtils -import org.islandoftex.arara.utils.CommonUtils -import org.islandoftex.arara.utils.DisplayUtils -import org.islandoftex.arara.utils.InterpreterUtils -import org.islandoftex.arara.utils.Methods -import org.mvel2.templates.TemplateRuntime -import org.slf4j.LoggerFactory - -/** - * Interprets the list of directives. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -class Interpreter( - /** - * The list of directives to be interpreted and evaluated. - */ - val directives: List<Directive> -) { - /** - * Exception class to represent that the interpreter should stop for some - * reason - */ - private class HaltExpectedException(msg: String) : Exception(msg) - - /** - * Gets the rule according to the provided directive. - * - * @param directive The provided directive. - * @return The absolute canonical path of the rule, given the provided - * directive. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - private fun getRule(directive: Directive): File { - return InterpreterUtils.buildRulePath(directive.identifier) - ?: throw AraraException( - messages.getMessage( - Messages.ERROR_INTERPRETER_RULE_NOT_FOUND, - directive.identifier, - "(" + CommonUtils.allRulePaths - .joinToString("; ") + ")" - ) - ) - } - - // TODO: in the following, extract the printing into the higher level - // function - /** - * "Run" a boolean return value - * @param value The boolean. - * @param conditional The conditional to print in dry-run mode. - * @param authors The authors of the rule. - * @return Returns [value] - */ - private fun runBoolean( - value: Boolean, - conditional: Conditional, - authors: List<String> - ): Boolean { - logger.info(messages.getMessage(Messages.LOG_INFO_BOOLEAN_MODE, - value.toString())) - - if (Arara.config[AraraSpec.Execution.dryrun]) { - DisplayUtils.printAuthors(authors) - DisplayUtils.wrapText(messages.getMessage(Messages - .INFO_INTERPRETER_DRYRUN_MODE_BOOLEAN_MODE, - value)) - DisplayUtils.printConditional(conditional) - } - - return value - } - - /** - * Run a command - * @param command The command to run. - * @param conditional The conditional applied to the run (only for printing). - * @param authors The rule authors (only for printing). - * @param ruleCommandExitValue The exit value of the rule command. - * @return Success of the execution. - * @throws AraraException Execution failed. - */ - @ExperimentalTime - @Throws(AraraException::class) - @Suppress("TooGenericExceptionCaught") - private fun runCommand( - command: Command, - conditional: Conditional, - authors: List<String>, - ruleCommandExitValue: String? - ): Boolean { - logger.info(messages.getMessage(Messages.LOG_INFO_SYSTEM_COMMAND, - command)) - var success = true - - if (!Arara.config[AraraSpec.Execution.dryrun]) { - val code = InterpreterUtils.run(command) - val check: Any = try { - val context = mapOf<String, Any>("value" to code) - TemplateRuntime.eval( - "@{ " + (ruleCommandExitValue ?: "value == 0") + " }", - context) - } catch (exception: RuntimeException) { - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage(Messages - .ERROR_INTERPRETER_EXIT_RUNTIME_ERROR), - exception) - } - - success = if (check is Boolean) { - check - } else { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_INTERPRETER_WRONG_EXIT_CLOSURE_RETURN - ) - ) - } - } else { - DisplayUtils.printAuthors(authors) - DisplayUtils.wrapText(messages.getMessage( - Messages.INFO_INTERPRETER_DRYRUN_MODE_SYSTEM_COMMAND, - command)) - DisplayUtils.printConditional(conditional) - } - - return success - } - - /** - * Converts the command evaluation result to a flat list. - * @param result The result - * @return A flat list. - */ - private fun resultToList(result: Any) = if (result is List<*>) { - CommonUtils.flatten(result) - } else { - listOf(result) - } - - /** - * Execute a command. - * @param command The command to evaluate. - * @param conditional Under which condition to execute. - * @param rule The rule (only passed for output purposes). - * @param parameters The parameters for evaluation - * @throws AraraException Running the command failed. - */ - @ExperimentalTime - @Throws(AraraException::class) - @Suppress("TooGenericExceptionCaught", "ThrowsCount") - private fun executeCommand( - command: RuleCommand, - conditional: Conditional, - rule: Rule, - parameters: Map<String, Any> - ) { - val result: Any = try { - TemplateRuntime.eval(command.command!!, parameters) - } catch (exception: RuntimeException) { - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage(Messages - .ERROR_INTERPRETER_COMMAND_RUNTIME_ERROR), - exception) - } - - // TODO: check nullability - resultToList(result).filter { it.toString().isNotBlank() } - .forEach { current -> - DisplayUtils.printEntry(rule.name, command.name - ?: messages.getMessage(Messages - .INFO_LABEL_UNNAMED_TASK)) - - val success = when (current) { - is Boolean -> runBoolean(current, conditional, - rule.authors) - is Command -> runCommand(current, conditional, - rule.authors, command.exit) - else -> TODO("error: this should not happen" + - "we are only supporting boolean + command") - } - - DisplayUtils.printEntryResult(success) - - if (Arara.config[AraraSpec.Execution.haltOnErrors] && !success) - // TODO: localize - throw HaltExpectedException("Command failed") - - // TODO: document this key - val haltKey = "arara:${Arara.config[AraraSpec - .Execution.reference].name}:halt" - if (Session.contains(haltKey)) { - Arara.config[AraraSpec.Execution.status] = - Session[haltKey].toString().toInt() - // TODO: localize - throw HaltExpectedException("User requested halt") - } - } - } - - /** - * Executes each directive, throwing an exception if something bad has - * happened. - * - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @ExperimentalTime - @Throws(AraraException::class) - @Suppress("NestedBlockDepth") - fun execute() { - for (directive in directives) { - logger.info(messages.getMessage(Messages.LOG_INFO_INTERPRET_RULE, - directive.identifier)) - - Arara.config[AraraSpec.Execution.file] = - directive.parameters.getValue("reference") as File - val file = getRule(directive) - - logger.info(messages.getMessage(Messages.LOG_INFO_RULE_LOCATION, - file.parent)) - - Arara.config[AraraSpec.Execution.InfoSpec.ruleId] = - directive.identifier - Arara.config[AraraSpec.Execution.InfoSpec.rulePath] = - file.parent - Arara.config[AraraSpec.Execution.DirectiveSpec.lines] = - directive.lineNumbers - - // parse the rule identified by the directive - // (may throw an exception) - val rule = RuleUtils.parseRule(file, directive.identifier) - val parameters = parseArguments(rule, directive) - .plus(Methods.getRuleMethods()) - - val evaluator = Evaluator() - - var available = true - if (InterpreterUtils.runPriorEvaluation(directive.conditional)) { - available = evaluator.evaluate(directive.conditional) - } - - // if this directive is conditionally disabled, skip - if (!available) continue - // if not execute the commands associated with the directive - do { - rule.commands.forEach { command -> - try { - executeCommand(command, directive.conditional, rule, parameters) - } catch (_: HaltExpectedException) { - // if the user uses the halt rule to trigger - // a halt, this will be raised - return - } - } - } while (evaluator.evaluate(directive.conditional)) - } - } - - /** - * Parses the rule arguments against the provided directive. - * - * @param rule The rule object. - * @param directive The directive. - * @return A map containing all arguments resolved according to the - * directive parameters. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - private fun parseArguments(rule: Rule, directive: Directive): - Map<String, Any> { - val arguments = rule.arguments - val unknown = CommonUtils.getUnknownKeys(directive.parameters, - arguments).minus("reference") - if (unknown.isNotEmpty()) - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage( - Messages.ERROR_INTERPRETER_UNKNOWN_KEYS, - "(" + unknown.joinToString(", ") + ")")) - - val resolvedArguments = mutableMapOf<String, Any>() - resolvedArguments["reference"] = directive.parameters - .getValue("reference") - - val context = mapOf( - "parameters" to directive.parameters, - "reference" to directive.parameters.getValue("reference") - ).plus(Methods.getRuleMethods()) - - arguments.forEach { argument -> - resolvedArguments[argument.identifier!!] = processArgument(argument, - directive.parameters.containsKey(argument.identifier!!), - context) - } - - return resolvedArguments - } - - /** - * Process a single argument and return the evaluated result. - * @param argument The argument to process. - * @param idInDirectiveParams Whether the argument's identifier is - * contained in the directive's parameters field. - * @param context The context for the evaluation. - * @return The result of the evaluation. - * @throws AraraException The argument could not be processed. - */ - @Throws(AraraException::class) - @Suppress("TooGenericExceptionCaught", "ThrowsCount") - private fun processArgument( - argument: Argument, - idInDirectiveParams: Boolean, - context: Map<String, Any> - ): Any { - if (argument.isRequired && !idInDirectiveParams) - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage( - Messages.ERROR_INTERPRETER_ARGUMENT_IS_REQUIRED, - argument.identifier!!)) - - var ret = argument.default?.let { - try { - TemplateRuntime.eval(it, context) - } catch (exception: RuntimeException) { - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage(Messages - .ERROR_INTERPRETER_DEFAULT_VALUE_RUNTIME_ERROR), - exception) - } - } ?: "" - - if (argument.flag != null && idInDirectiveParams) { - ret = try { - TemplateRuntime.eval(argument.flag!!, context) - } catch (exception: RuntimeException) { - throw AraraException(CommonUtils.ruleErrorHeader + messages - .getMessage(Messages - .ERROR_INTERPRETER_FLAG_RUNTIME_EXCEPTION), - exception) - } - } - - return ret - } - - companion object { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - // the class logger obtained from - // the logger factory - private val logger = LoggerFactory.getLogger(Interpreter::class.java) - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Session.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Session.kt deleted file mode 100644 index c2093429e1..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/model/Session.kt +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.model - -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages - -/** - * Implements the session. - * - * This class wraps a map that holds the execution session, that is, a dirty - * maneuver to exchange pretty much any data between commands and even rules. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object Session { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - // the session map which holds the execution session; - // the idea here is to provide wrappers to the map - // methods, so it could be easily manipulated - private val map = mutableMapOf<String, Any>() - - /** - * Gets the object indexed by the provided key from the session. This method - * holds the map method of the very same name. - * - * @param key The provided key. - * @return The object indexed by the provided key. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - operator fun get(key: String): Any { - return if (contains(key)) { - map.getValue(key) - } else { - throw AraraException( - messages.getMessage( - Messages.ERROR_SESSION_OBTAIN_UNKNOWN_KEY, - key - ) - ) - } - } - - /** - * Inserts (or overwrites) the object indexed by the provided key into the - * session. This method holds the map method of the very same name. - * - * @param key The provided key. - * @param value The value to be inserted. - */ - fun put(key: String, value: Any) { - map[key] = value - } - - /** - * Removes the entry indexed by the provided key from the session. This method - * holds the map method of the same name. - * - * @param key The provided key. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun remove(key: String) { - if (contains(key)) { - map.remove(key) - } else { - throw AraraException( - messages.getMessage( - Messages.ERROR_SESSION_REMOVE_UNKNOWN_KEY, - key - ) - ) - } - } - - /** - * Checks if the provided key exists in the session. - * - * @param key The provided key. - * @return A boolean value indicating if the provided key exists in the - * session. - */ - operator fun contains(key: String): Boolean = map.containsKey(key) - - /** - * Clears the session (map). This method, as usual, holds the map method of - * the same name. - */ - fun clear() = map.clear() - - /** - * Update the environment variables stored in the session. - * - * @param additionFilter Which environment variables to include. You can - * filter their names (the string parameter) but not their values. By - * default all values will be added. - * @param removalFilter Which environment variables to remove beforehand. - * By default all values will be removed. - */ - fun updateEnvironmentVariables( - additionFilter: (String) -> Boolean = { true }, - removalFilter: (String) -> Boolean = { true } - ) { - // remove all current environment variables to clean up the session - map.filterKeys { it.startsWith("environment:") } - .filterKeys(removalFilter) - .forEach { remove(it.key) } - // add all relevant new environment variables - System.getenv().filterKeys(additionFilter) - .forEach { map["environment:${it.key}"] = it.value } - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Argument.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Argument.kt deleted file mode 100644 index dfd2b99403..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Argument.kt +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import org.islandoftex.arara.utils.CommonUtils - -/** - * The rule argument model. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@Serializable -class Argument { - /** - * The argument identifier - */ - var identifier: String? = null - get() = CommonUtils.removeKeyword(field) - - /** - * Boolean indicating if the current argument is required - */ - @SerialName("required") - var isRequired: Boolean = false - - /** - * Flag to hold the argument value manipulation - */ - var flag: String? = null - get() = CommonUtils.removeKeyword(field) - - /** - * The argument fallback if it is not defined in the directive - */ - var default: String? = null - get() = CommonUtils.removeKeyword(field) -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Command.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Command.kt deleted file mode 100644 index df1ce802f5..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Command.kt +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -import java.io.File -import org.islandoftex.arara.Arara -import org.islandoftex.arara.configuration.AraraSpec -import org.islandoftex.arara.utils.CommonUtils - -/** - * Implements a command model, containing a list of strings. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -class Command { - /** - * A list of elements which are components - * of a command and represented as strings - */ - val elements: List<String> - - /** - * An optional file acting as a reference for - * the default working directory - */ - var workingDirectory: File = Arara.config[AraraSpec.Execution - .workingDirectory].toFile() - - /** - * Constructor. - * @param values An array of objects. - */ - constructor(vararg values: Any) { - elements = mutableListOf() - val result = CommonUtils.flatten(values.toList()) - result.map { it.toString() }.filter { it.isNotEmpty() } - .forEach { elements.add(it) } - } - - /** - * Constructor. - * @param elements A list of strings. - */ - constructor(elements: List<String>) { - this.elements = elements - } - - /** - * Provides a textual representation of the current command. - * @return A string representing the current command. - */ - override fun toString(): String { - return "[ " + elements.joinToString(", ") + " ]" + - " @ $workingDirectory" - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Conditional.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Conditional.kt deleted file mode 100644 index 8a4ec6cb35..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Conditional.kt +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -/** - * The conditional class, it represents the type of conditional available - * for a directive and its corresponding expression to be evaluated. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -data class Conditional( - /** - * The type of the condition indicates the meaning when evaluated. - * Defaults to [ConditionalType.NONE]. - */ - val type: ConditionalType = ConditionalType.NONE, - /** - * The expression to be evaluated according to its type. Defaults - * to no evaluation (empty string). - */ - val condition: String = "" -) { - /** - * The types of conditionals arara is able to recognize. - */ - enum class ConditionalType { - /** - * Evaluated beforehand, directive is interpreted if and only if the - * result is true. - */ - IF, - /** - * There is no evaluation, directive is interpreted, no extra effort is - * needed. - */ - NONE, - /** - * Evaluated beforehand, directive is interpreted if and only if the - * result is false. - */ - UNLESS, - /** - * Directive is interpreted the first time, then the evaluation is - * done; while the result is false, the directive is interpreted again - * and again. - */ - UNTIL, - /** - * Evaluated beforehand, directive is interpreted if and only if the - * result is true, and the process is repeated while the result still - * holds true. - */ - WHILE - } - - /** - * Provides a textual representation of the conditional object. - * @return A string representation of this object. - */ - override fun toString(): String { - return "{ $type" + - if (type != ConditionalType.NONE) - ", expression: ${condition.trim()}" - else "" + " }" - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Directive.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Directive.kt deleted file mode 100644 index 33f8431744..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Directive.kt +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -/** - * Implements the directive model. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -data class Directive( - /** - * The directive identifier, it is resolved to the rule identifier - * later on. - */ - val identifier: String, - /** - * A map containing the parameters; they are validated later on in - * order to ensure they are valid. - */ - val parameters: Map<String, Any>, - /** - * A conditional containing the type and the expression to be evaluated - * later on. - */ - val conditional: Conditional, - /** - * A list contained all line numbers from the main file which built the - * current directive. - */ - val lineNumbers: List<Int> -) diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/DirectiveAssembler.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/DirectiveAssembler.kt deleted file mode 100644 index a431df6ae2..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/DirectiveAssembler.kt +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -/** - * Implements a directive assembler in order to help build a directive from a - * list of strings. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -class DirectiveAssembler { - // this variable holds a list of - // line numbers indicating which - // lines composed the resulting - // potential directive - private val lineNumbers = mutableListOf<Int>() - - // this variable holds the textual - // representation of the directive - private var text: String = "" - - /** - * Checks if an append operation is allowed. - * @return A boolean value indicating if an append operation is allowed. - */ - val isAppendAllowed: Boolean - get() = lineNumbers.isNotEmpty() - - /** - * Adds a line number to the assembler. - * @param line An integer representing the line number. - */ - fun addLineNumber(line: Int) = lineNumbers.add(line) - - /** - * Appends the provided line to the assembler text. - * @param line The provided line. - */ - fun appendLine(line: String) { - text = text + " " + line.trim() - } - - /** - * Gets the list of line numbers. - * @return The list of line numbers. - */ - fun getLineNumbers(): List<Int> = lineNumbers - - /** - * Gets the text. - * @return The assembler text, properly trimmed. - */ - fun getText(): String = text.trim() -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/DirectiveUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/DirectiveUtils.kt deleted file mode 100644 index e901e25fcf..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/DirectiveUtils.kt +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import com.fasterxml.jackson.module.kotlin.readValue -import com.fasterxml.jackson.module.kotlin.registerKotlinModule -import java.io.File -import java.util.regex.Pattern -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.utils.DisplayUtils -import org.slf4j.LoggerFactory - -/** - * Implements directive utilitary methods. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class) -object DirectiveUtils { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - // get the logger context from a factory - private val logger = LoggerFactory.getLogger(DirectiveUtils::class.java) - - /** - * This function filters the lines of a file to identify the potential - * directives. - * - * @param lines The lines of the file. - * @return A map containing the line number and the line's content. - */ - private fun getPotentialDirectiveLines(lines: List<String>): - Map<Int, String> { - val header = Arara.config[AraraSpec.Execution.onlyHeader] - val validLineRegex = Arara.config[AraraSpec.Execution.filePattern] - val validLinePattern = validLineRegex.toPattern() - val validLineStartPattern = (validLineRegex + Arara.config[AraraSpec - .Application.namePattern]).toPattern() - val map = mutableMapOf<Int, String>() - for ((i, text) in lines.withIndex()) { - val validLineMatcher = validLineStartPattern.matcher(text) - if (validLineMatcher.find()) { - val line = text.substring(validLineMatcher.end()) - map[i + 1] = line - - logger.info(messages.getMessage( - Messages.LOG_INFO_POTENTIAL_PATTERN_FOUND, - i + 1, line.trim())) - } else if (header && !checkLinePattern(validLinePattern, text)) { - // if we should only look within the file's header and reached - // a point where the line pattern does not match anymore, we - // assume we have left the header and break - break - } - } - return map - } - - /** - * Extracts a list of directives from a list of strings. - * - * @param lines List of strings. - * @return A list of directives. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - @Suppress("MagicNumber") - fun extractDirectives(lines: List<String>): List<Directive> { - val pairs = getPotentialDirectiveLines(lines) - .takeIf { it.isNotEmpty() } - ?: throw AraraException(messages.getMessage( - Messages.ERROR_VALIDATE_NO_DIRECTIVES_FOUND)) - - val assemblers = mutableListOf<DirectiveAssembler>() - var assembler = DirectiveAssembler() - val linebreakPattern = Arara.config[AraraSpec.Directive - .linebreakPattern].toPattern() - for ((lineno, content) in pairs) { - val linebreakMatcher = linebreakPattern.matcher(content) - if (linebreakMatcher.find()) { - if (!assembler.isAppendAllowed) { - throw AraraException( - messages.getMessage( - Messages.ERROR_VALIDATE_ORPHAN_LINEBREAK, - lineno - ) - ) - } else { - assembler.addLineNumber(lineno) - assembler.appendLine(linebreakMatcher.group(1)) - } - } else { - if (assembler.isAppendAllowed) { - assemblers.add(assembler) - } - assembler = DirectiveAssembler() - assembler.addLineNumber(lineno) - assembler.appendLine(content) - } - } - if (assembler.isAppendAllowed) { - assemblers.add(assembler) - } - - return assemblers.map { generateDirective(it) } - } - - /** - * Generates a directive from a directive assembler. - * - * @param assembler The directive assembler. - * @return The corresponding directive. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - @Suppress("MagicNumber") - fun generateDirective(assembler: DirectiveAssembler): Directive { - val matcher = Arara.config[AraraSpec.Directive.directivePattern] - .toPattern().matcher(assembler.getText()) - if (matcher.find()) { - val directive = Directive( - identifier = matcher.group(1)!!, - parameters = getParameters(matcher.group(3), - assembler.getLineNumbers()), - conditional = Conditional( - type = getType(matcher.group(5)), - condition = matcher.group(6) ?: "" - ), - lineNumbers = assembler.getLineNumbers() - ) - - logger.info(messages.getMessage( - Messages.LOG_INFO_POTENTIAL_DIRECTIVE_FOUND, directive)) - - return directive - } else { - throw AraraException( - messages.getMessage( - Messages.ERROR_VALIDATE_INVALID_DIRECTIVE_FORMAT, - "(" + assembler.getLineNumbers() - .joinToString(", ") + ")" - ) - ) - } - } - - /** - * Gets the conditional type based on the input string. - * - * @param text The input string. - * @return The conditional type. - */ - private fun getType(text: String?): Conditional.ConditionalType { - return when (text) { - null -> Conditional.ConditionalType.NONE - "if" -> Conditional.ConditionalType.IF - "while" -> Conditional.ConditionalType.WHILE - "until" -> Conditional.ConditionalType.UNTIL - else -> Conditional.ConditionalType.UNLESS - } - } - - /** - * Gets the parameters from the input string. - * - * @param text The input string. - * @param numbers The list of line numbers. - * @return A map containing the directive parameters. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - private fun getParameters( - text: String?, - numbers: List<Int> - ): Map<String, Any> { - if (text == null) - return mapOf() - - /* Before using kotlinx.serialization, there has been a dedicated - * directive resolver which instructed SnakeYAML to do the following: - * - * addImplicitResolver(Tag.MERGE, MERGE, "<") - * addImplicitResolver(Tag.NULL, NULL, "~nN\u0000") - * addImplicitResolver(Tag.NULL, EMPTY, null) - * - * This has been removed. - */ - return ObjectMapper(YAMLFactory()).registerKotlinModule().runCatching { - readValue<Map<String, Any>>(text) - }.getOrElse { - throw AraraException(messages.getMessage( - Messages.ERROR_VALIDATE_YAML_EXCEPTION, - "(" + numbers.joinToString(", ") + ")"), - it) - } - } - - /** - * Replicate a directive for given files. - * - * @param holder The list of files. - * @param parameters The parameters for the directive. - * @param directive The directive to clone. - * @return List of cloned directives. - * @throws AraraException If there is an error validating the [holder] - * object. - */ - @Throws(AraraException::class) - private fun replicateDirective( - holder: Any, - parameters: Map<String, Any>, - directive: Directive - ): List<Directive> { - return if (holder is List<*>) { - // we received a file list, so we map that list to files - holder.filterIsInstance<Any>() - .asSequence() - .map { File(it.toString()) } - .map(FileHandlingUtils::getCanonicalFile) - // and because we want directives, we replicate our - // directive to be applied to that file - .map { reference -> - directive.copy(parameters = parameters - .plus("reference" to reference)) - } - .toList() - // we take the result if and only if we have at least one - // file and we did not filter out any invalid argument - .takeIf { it.isNotEmpty() && holder.size == it.size } - // TODO: check exception according to condition - ?: throw AraraException( - messages.getMessage( - Messages.ERROR_VALIDATE_EMPTY_FILES_LIST, - "(" + directive.lineNumbers - .joinToString(", ") + ")" - ) - ) - } else { - throw AraraException( - messages.getMessage( - Messages.ERROR_VALIDATE_FILES_IS_NOT_A_LIST, - "(" + directive.lineNumbers.joinToString(", ") + ")" - ) - ) - } - } - - /** - * Validates the list of directives, returning a new list. - * - * @param directives The list of directives. - * @return A new list of directives. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun process(directives: List<Directive>): List<Directive> { - val result = mutableListOf<Directive>() - directives.forEach { directive -> - val parameters = directive.parameters - - if (parameters.containsKey("reference")) - throw AraraException(messages.getMessage( - Messages.ERROR_VALIDATE_REFERENCE_IS_RESERVED, - "(" + directive.lineNumbers.joinToString(", ") + ")")) - - if (parameters.containsKey("files")) { - result.addAll(replicateDirective(parameters.getValue("files"), - parameters.minus("files"), directive)) - } else { - result.add(directive.copy(parameters = parameters - .plus("reference" to - Arara.config[AraraSpec.Execution.reference]))) - } - } - - logger.info(messages.getMessage( - Messages.LOG_INFO_VALIDATED_DIRECTIVES)) - logger.info(DisplayUtils.displayOutputSeparator( - messages.getMessage(Messages.LOG_INFO_DIRECTIVES_BLOCK))) - result.forEach { logger.info(it.toString()) } - logger.info(DisplayUtils.displaySeparator()) - - return result - } - - /** - * Checks if the provided line contains the corresponding pattern, based on - * the file type, or an empty line. - * - * @param pattern Pattern to be matched, based on the file type. - * @param line Provided line. - * @return Logical value indicating if the provided line contains the - * corresponding pattern, based on the file type, or an empty line. - */ - private fun checkLinePattern(pattern: Pattern, line: String): Boolean { - return line.isBlank() || pattern.matcher(line).find() - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Rule.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Rule.kt deleted file mode 100644 index 53ac2aa8b1..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/Rule.kt +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -import kotlinx.serialization.Serializable -import org.islandoftex.arara.utils.CommonUtils - -/** - * Implements the rule model. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@Serializable -class Rule { - /** - * The rule identifier - */ - var identifier: String = INVALID_RULE_IDENTIFIER - get() = CommonUtils.removeKeywordNotNull(field) - - /** - * The rule name - */ - var name: String = INVALID_RULE_NAME - get() = CommonUtils.removeKeywordNotNull(field) - - /** - * The list of authors - */ - var authors: List<String> = listOf() - get() = field.mapNotNull { CommonUtils.removeKeyword(it) } - - /** - * The list of commands - */ - var commands: List<RuleCommand> = listOf() - - /** - * The list of arguments - */ - var arguments: List<Argument> = listOf() - - companion object { - const val INVALID_RULE_IDENTIFIER = "INVALID_RULE" - const val INVALID_RULE_NAME = "INVALID_RULE" - } -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/RuleCommand.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/RuleCommand.kt deleted file mode 100644 index 901718d412..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/RuleCommand.kt +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -import kotlinx.serialization.Serializable -import org.islandoftex.arara.utils.CommonUtils - -/** - * Implements the rule command model. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -@Serializable -class RuleCommand { - /** - * The command name - */ - var name: String? = null - get() = CommonUtils.removeKeyword(field) - - /** - * The command instruction - */ - var command: String? = null - get() = CommonUtils.removeKeyword(field) - - /** - * The exit status expression - */ - var exit: String? = null - get() = CommonUtils.removeKeyword(field) -} diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/RuleUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/RuleUtils.kt deleted file mode 100644 index e823e4e598..0000000000 --- a/support/arara/source/src/main/kotlin/org/islandoftex/arara/ruleset/RuleUtils.kt +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -package org.islandoftex.arara.ruleset - -import com.charleskorn.kaml.Yaml -import java.io.File -import org.islandoftex.arara.localization.LanguageController -import org.islandoftex.arara.localization.Messages -import org.islandoftex.arara.model.AraraException -import org.islandoftex.arara.utils.CommonUtils - -/** - * Implements rule utilitary methods. - * - * @author Island of TeX - * @version 5.0 - * @since 4.0 - */ -object RuleUtils { - // the application messages obtained from the - // language controller - private val messages = LanguageController - - /** - * Parses the provided file, checks the identifier and returns a rule - * representation. - * - * @param file The rule file. - * @param identifier The directive identifier. - * @return The rule object. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - fun parseRule(file: File, identifier: String): Rule { - val rule = file.runCatching { - val text = readText() - if (!text.startsWith("!config")) - throw Exception("Rule should start with !config") - Yaml.default.parse(Rule.serializer(), text) - }.getOrElse { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_PARSERULE_GENERIC_ERROR - ), it) - } - - validateHeader(rule, identifier) - validateBody(rule) - return rule - } - - /** - * Validates the rule header according to the directive identifier. - * - * @param rule The rule object. - * @param identifier The directive identifier. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - @Suppress("ThrowsCount") - private fun validateHeader(rule: Rule, identifier: String) { - if (rule.identifier != Rule.INVALID_RULE_IDENTIFIER) { - if (rule.identifier != identifier) { - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage( - Messages.ERROR_VALIDATEHEADER_WRONG_IDENTIFIER, - rule.identifier, - identifier)) - } - } else { - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage(Messages.ERROR_VALIDATEHEADER_NULL_ID)) - } - if (rule.name == Rule.INVALID_RULE_NAME) { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_VALIDATEHEADER_NULL_NAME - ) - ) - } - } - - /** - * Validates the rule body. - * - * @param rule The rule object. - * @throws AraraException Something wrong happened, to be caught in the - * higher levels. - */ - @Throws(AraraException::class) - @Suppress("ThrowsCount") - private fun validateBody(rule: Rule) { - if (rule.commands.any { it.command == null }) { - throw AraraException(CommonUtils.ruleErrorHeader + - messages.getMessage( - Messages.ERROR_VALIDATEBODY_NULL_COMMAND)) - } - - val arguments = mutableListOf<String>() - for (argument in rule.arguments) { - if (argument.identifier != null) { - if (argument.flag != null || argument.default != null) { - arguments.add(argument.identifier!!) - } else { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_VALIDATEBODY_MISSING_KEYS - ) - ) - } - } else { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_VALIDATEBODY_NULL_ARGUMENT_ID - ) - ) - } - } - - arguments.intersect(listOf("files", "reference")).forEach { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_VALIDATEBODY_ARGUMENT_ID_IS_RESERVED, - it - ) - ) - } - - val expected = arguments.size - val found = arguments.toSet().size - if (expected != found) { - throw AraraException( - CommonUtils.ruleErrorHeader + messages.getMessage( - Messages.ERROR_VALIDATEBODY_DUPLICATE_ARGUMENT_IDENTIFIERS - ) - ) - } - } -} 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 - } - } -} |